diff --git a/.github/workflows/stylecheck.yml b/.github/workflows/stylecheck.yml new file mode 100644 index 000000000..2b6bc4292 --- /dev/null +++ b/.github/workflows/stylecheck.yml @@ -0,0 +1,13 @@ +on: [pull_request] +name: Python Style Check +jobs: + pycodestyle: + name: pycodestyle + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: pycodestyle + uses: ankitvgupta/pycodestyle-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PRECOMMAND_MESSAGE: You have style errors. See them below. diff --git a/docs/conf.py b/docs/conf.py index ea698fa28..4f5761320 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,6 +8,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. +import datetime import sys # If extensions (or modules to document with autodoc) are in another directory, @@ -61,7 +62,6 @@ master_doc = 'index' # General information about the project. -import datetime now = datetime.datetime.today() project = 'tsfresh' copyright = '2016-{}, Maximilian Christ et al./ Blue Yonder GmbH'.format(now.year) @@ -205,21 +205,21 @@ # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -# 'papersize': 'letterpaper', + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', -# The font size ('10pt', '11pt' or '12pt'). -# 'pointsize': '10pt', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', -# Additional stuff for the LaTeX preamble. -# 'preamble': '', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'user_guide.tex', 'tsfresh Documentation', - '', 'manual'), + ('index', 'user_guide.tex', 'tsfresh Documentation', + '', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/setup.cfg b/setup.cfg index cc608e373..2fe034a0b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -89,3 +89,6 @@ no-vcs = 1 formats = bdist_wheel # do not upload the docs as we host them on read-the-docs with-docs = 0 + +[pycodestyle] +max-line-length = 120 diff --git a/tests/integrations/dask-worker-space/global.lock b/tests/integrations/dask-worker-space/global.lock new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integrations/dask-worker-space/purge.lock b/tests/integrations/dask-worker-space/purge.lock new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integrations/examples/test_driftbif_simulation.py b/tests/integrations/examples/test_driftbif_simulation.py index ef62373a1..f8e0748ef 100644 --- a/tests/integrations/examples/test_driftbif_simulation.py +++ b/tests/integrations/examples/test_driftbif_simulation.py @@ -30,9 +30,10 @@ def test_relaxation_dynamics(self): k3t = ds.kappa_3 * ds.tau k3st = ds.kappa_3 ** 2 * ds.tau a0 = v0 / ds.kappa_3 - acceleration = lambda t: ds.kappa_3 * (a0 * np.sqrt(k3t - 1) * np.exp(k3st * t) / - np.sqrt(np.exp(2.0 * k3st * t) * ds.Q * a0 ** 2 + - np.exp(2.0 * ds.kappa_3 * t) * (k3t - 1 - ds.Q * a0 ** 2))) + + def acceleration(t): return ds.kappa_3 * (a0 * np.sqrt(k3t - 1) * np.exp(k3st * t) / + np.sqrt(np.exp(2.0 * k3st * t) * ds.Q * a0 ** 2 + + np.exp(2.0 * ds.kappa_3 * t) * (k3t - 1 - ds.Q * a0 ** 2))) t = ds.delta_t * np.arange(Nt) return np.testing.assert_array_almost_equal(v[:, 0], np.vectorize(acceleration)(t), decimal=8) @@ -55,7 +56,8 @@ def test_dimensionality(self): Nt = 10 v = ds.simulate(Nt) self.assertEqual(v.shape, (Nt, 2), - 'The default configuration should return velocities from a two-dimensional dissipative soliton.') + "The default configuration should return velocities " + "from a two-dimensional dissipative soliton.") v = ds.simulate(Nt, v0=np.zeros(3)) self.assertEqual(v.shape, (Nt, 3), diff --git a/tests/integrations/examples/test_har_dataset.py b/tests/integrations/examples/test_har_dataset.py index d0dd90968..8fe29b348 100644 --- a/tests/integrations/examples/test_har_dataset.py +++ b/tests/integrations/examples/test_har_dataset.py @@ -6,6 +6,7 @@ from tsfresh.examples.har_dataset import download_har_dataset, load_har_dataset, load_har_classes from pandas import DataFrame, Series + class HumanActivityTestCase(TestCase): def setUp(self): download_har_dataset() @@ -20,4 +21,4 @@ def test_characteristics_downloaded_robot_execution_failures(self): self.assertIsInstance(self.classes, Series) def test_index(self): - self.assertCountEqual(self.data.index, self.classes.index) \ No newline at end of file + self.assertCountEqual(self.data.index, self.classes.index) diff --git a/tests/integrations/test_full_pipeline.py b/tests/integrations/test_full_pipeline.py index d6d1e426f..96ac51d31 100644 --- a/tests/integrations/test_full_pipeline.py +++ b/tests/integrations/test_full_pipeline.py @@ -50,4 +50,4 @@ def test_relevant_extraction(self): 'F_x__variance_larger_than_standard_deviation'} self.assertGreaterEqual(set(extracted_features.columns), some_expected_features) - self.assertGreater(len(extracted_features), 0) \ No newline at end of file + self.assertGreater(len(extracted_features), 0) diff --git a/tests/integrations/test_notebooks.py b/tests/integrations/test_notebooks.py index 3459c6497..4ad1be9e2 100644 --- a/tests/integrations/test_notebooks.py +++ b/tests/integrations/test_notebooks.py @@ -28,15 +28,15 @@ def _notebook_run(path, timeout=default_timeout): try: if os.environ['TRAVIS']: return [], [] - except: + except BaseException: pass - # Ensure temporary files are not auto-deleted as processes have limited + # Ensure temporary files are not auto-deleted as processes have limited # permissions to re-use file handles under WinNT-based operating systems. fname = '' with tempfile.NamedTemporaryFile(mode='w+t', suffix=".ipynb", delete=False) as fout: fname = fout.name - + args = ["jupyter", "nbconvert", "--to", "notebook", "--execute", execproc_timeout] args += ["--ExecutePreprocessor.kernel_name=python3"] @@ -48,7 +48,7 @@ def _notebook_run(path, timeout=default_timeout): os.remove(fname) errors = [output for cell in nb.cells if "outputs" in cell - for output in cell["outputs"] \ + for output in cell["outputs"] if output.output_type == "error"] return nb, errors diff --git a/tests/integrations/test_relevant_feature_extraction.py b/tests/integrations/test_relevant_feature_extraction.py index 05a2f2c4e..0e1944490 100644 --- a/tests/integrations/test_relevant_feature_extraction.py +++ b/tests/integrations/test_relevant_feature_extraction.py @@ -11,6 +11,7 @@ import pandas.util.testing as pdt import pandas as pd + class RelevantFeatureExtractionDataTestCase(DataTestCase): """ Test case for the relevant_feature_extraction function @@ -109,5 +110,3 @@ def test_raises_y_not_more_than_one_label(self): df_dict = {"a": pd.DataFrame({"val": [1, 2, 3, 4, 10, 11], "id": [1, 1, 1, 1, 2, 2]}), "b": pd.DataFrame({"val": [5, 6, 7, 8, 12, 13], "id": [4, 4, 3, 3, 2, 2]})} self.assertRaises(AssertionError, extract_relevant_features, df_dict, y, None, None, None, "id", None, "val") - - diff --git a/tests/units/feature_extraction/test_extraction.py b/tests/units/feature_extraction/test_extraction.py index cba461592..9f63d91bb 100644 --- a/tests/units/feature_extraction/test_extraction.py +++ b/tests/units/feature_extraction/test_extraction.py @@ -280,13 +280,17 @@ def test_simple_data_sample_four_timeseries(self): df.sort_values(by=["id", "kind", "sort"], inplace=True) result = generate_data_chunk_format(df, "id", "kind", "val") - expected = [(10, 'a', pd.Series([36, 71, 27, 62, 56, 58, 67, 11, 2, 24, 45, 30, 0, 9, 41, 28, 33, 19, 29, 43], - index=[10]*20, name="val")), - (10, 'b', pd.Series([78, 37, 23, 44, 6, 3, 21, 61, 39, 31, 53, 16, 66, 50, 40, 47, 7, 42, 38, 55], - index=[10] *20, name="val")), - (500, 'a', pd.Series([76, 72, 74, 75, 32, 64, 46, 35, 15, 70, 57, 65, 51, 26, 5, 25, 10, 69, 73, 77], - index=[500]*20, name="val")), - (500, 'b', pd.Series([8, 60, 12, 68, 22, 17, 18, 63, 49, 34, 20, 52, 48, 14, 79, 4, 1, 59, 54, 13], - index=[500] *20, name="val"))] + expected = [(10, 'a', pd.Series([36, 71, 27, 62, 56, 58, 67, 11, 2, 24, 45, 30, 0, + 9, 41, 28, 33, 19, 29, 43], + index=[10] * 20, name="val")), + (10, 'b', pd.Series([78, 37, 23, 44, 6, 3, 21, 61, 39, 31, 53, 16, 66, + 50, 40, 47, 7, 42, 38, 55], + index=[10] * 20, name="val")), + (500, 'a', pd.Series([76, 72, 74, 75, 32, 64, 46, 35, 15, 70, 57, 65, + 51, 26, 5, 25, 10, 69, 73, 77], + index=[500] * 20, name="val")), + (500, 'b', pd.Series([8, 60, 12, 68, 22, 17, 18, 63, 49, 34, 20, 52, + 48, 14, 79, 4, 1, 59, 54, 13], + index=[500] * 20, name="val"))] self.assert_data_chunk_object_equal(result, expected) diff --git a/tests/units/feature_extraction/test_feature_calculations.py b/tests/units/feature_extraction/test_feature_calculations.py index b8060aece..09860cb2f 100644 --- a/tests/units/feature_extraction/test_feature_calculations.py +++ b/tests/units/feature_extraction/test_feature_calculations.py @@ -45,8 +45,10 @@ def assertFalseOnAllArrayTypes(self, f, input_to_f, *args, **kwargs): def assertAllFalseOnAllArrayTypes(self, f, input_to_f, *args, **kwargs): self.assertFalse(any(dict(f(input_to_f, *args, **kwargs)).values()), msg="Not false for lists") - self.assertFalse(any(dict(f(np.array(input_to_f), *args, **kwargs)).values()), msg="Not false for numpy.arrays") - self.assertFalse(any(dict(f(pd.Series(input_to_f), *args, **kwargs)).values()), msg="Not false for pandas.Series") + self.assertFalse(any(dict(f(np.array(input_to_f), *args, **kwargs)).values()), + msg="Not false for numpy.arrays") + self.assertFalse(any(dict(f(pd.Series(input_to_f), *args, **kwargs)).values()), + msg="Not false for pandas.Series") def assertAlmostEqualOnAllArrayTypes(self, f, input_t_f, result, *args, **kwargs): self.assertAlmostEqual(f(input_t_f, *args, **kwargs), result, @@ -71,7 +73,7 @@ def assertEqualPandasSeriesWrapper(self, f, input_to_f, result, *args, **kwargs) def test__roll(self): x = np.random.normal(size=30) for shift in [0, 1, 10, 11, 30, 31, 50, 51, 150, 151]: - np.testing.assert_array_equal(_roll(x, shift), np.roll(x, shift)) + np.testing.assert_array_equal(_roll(x, shift), np.roll(x, shift)) np.testing.assert_array_equal(_roll(x, -shift), np.roll(x, -shift)) def test___get_length_sequences_where(self): @@ -99,7 +101,7 @@ def test_large_standard_deviation(self): def test_symmetry_looking(self): self.assertAllTrueOnAllArrayTypes(symmetry_looking, [-1, -1, 1, 1], - [dict(r=0.05), dict(r=0.75)]) + [dict(r=0.05), dict(r=0.75)]) self.assertAllFalseOnAllArrayTypes(symmetry_looking, [-1, -1, 1, 1], [dict(r=0)]) self.assertAllFalseOnAllArrayTypes(symmetry_looking, [-1, -1, -1, -1, 1], [dict(r=0.05)]) self.assertAllTrueOnAllArrayTypes(symmetry_looking, [-2, -2, -2, -1, -1, -1], [dict(r=0.05)]) @@ -240,7 +242,6 @@ def test_partial_autocorrelation(self): else: self.assertIsNaN(lag_val) - def test_augmented_dickey_fuller(self): # todo: add unit test for the values of the test statistic @@ -266,7 +267,7 @@ def test_augmented_dickey_fuller(self): x = [0] * m x[0] = 100 for i in range(1, m): - x[i] = x[i-1] * 0.5 + e[i] + x[i] = x[i - 1] * 0.5 + e[i] param = [{"attr": "teststat"}, {"attr": "pvalue"}, {"attr": "usedlag"}] expected_index = ['attr_"teststat"', 'attr_"pvalue"', 'attr_"usedlag"'] @@ -302,10 +303,10 @@ def test_cid_ce(self): def test_ratio_beyond_r_sigma(self): - x = [0, 1]*10 + [10, 20, -30] # std of x is 7.21, mean 3.04 - self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 3./len(x), r=1) - self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 2./len(x), r=2) - self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 1./len(x), r=3) + x = [0, 1] * 10 + [10, 20, -30] # std of x is 7.21, mean 3.04 + self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 3. / len(x), r=1) + self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 2. / len(x), r=2) + self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 1. / len(x), r=3) self.assertEqualOnAllArrayTypes(ratio_beyond_r_sigma, x, 0, r=20) def test_mean_abs_change(self): @@ -472,7 +473,7 @@ def test_fft_coefficient(self): param = [{"coeff": 0, "attr": "real"}, {"coeff": 1, "attr": "real"}, {"coeff": 2, "attr": "real"}, {"coeff": 0, "attr": "imag"}, {"coeff": 1, "attr": "imag"}, {"coeff": 2, "attr": "imag"}, {"coeff": 0, "attr": "angle"}, {"coeff": 1, "attr": "angle"}, {"coeff": 2, "attr": "angle"}, - {"coeff": 0, "attr": "abs"}, {"coeff": 1, "attr": "abs"}, {"coeff": 2, "attr": "abs"} ] + {"coeff": 0, "attr": "abs"}, {"coeff": 1, "attr": "abs"}, {"coeff": 2, "attr": "abs"}] expected_index = ['coeff_0__attr_"real"', 'coeff_1__attr_"real"', 'coeff_2__attr_"real"', 'coeff_0__attr_"imag"', 'coeff_1__attr_"imag"', 'coeff_2__attr_"imag"', 'coeff_0__attr_"angle"', 'coeff_1__attr_"angle"', 'coeff_2__attr_"angle"', @@ -524,7 +525,7 @@ def test_fft_aggregated(self): self.assertAlmostEqual(res['aggtype_"kurtosis"'], 3.643, places=3) # Scalar multiplying the distribution should not change the results: - x = 10*x + x = 10 * x res = pd.Series(dict(fft_aggregated(x, param))) self.assertCountEqual(list(res.index), expected_index) self.assertAlmostEqual(res['aggtype_"centroid"'], 1.135, places=3) @@ -545,15 +546,17 @@ def test_fft_aggregated(self): # Gaussian test: def normal(y, mean_, sigma_): - return 1/(2 * np.pi * sigma_ ** 2) * np.exp(-(y - mean_) ** 2 / (2 * sigma_ ** 2)) - mean_ = 500.; sigma_ = 1.; range_ = int(2*mean_) + return 1 / (2 * np.pi * sigma_ ** 2) * np.exp(-(y - mean_) ** 2 / (2 * sigma_ ** 2)) + mean_ = 500. + sigma_ = 1. + range_ = int(2 * mean_) x = list(map(lambda x: normal(x, mean_, sigma_), range(range_))) # The fourier transform of a Normal dist in the positive halfspace is a half normal, # Hand calculated values of centroid and variance based for the half-normal dist: # (Ref: https://en.wikipedia.org/wiki/Half-normal_distribution) - expected_fft_centroid = (range_/(2*np.pi*sigma_))*np.sqrt(2/np.pi) - expected_fft_var = (range_/(2*np.pi*sigma_))**2*(1-2/np.pi) + expected_fft_centroid = (range_ / (2 * np.pi * sigma_)) * np.sqrt(2 / np.pi) + expected_fft_var = (range_ / (2 * np.pi * sigma_))**2 * (1 - 2 / np.pi) # Calculate values for unit test: res = pd.Series(dict(fft_aggregated(x, param))) @@ -563,11 +566,11 @@ def normal(y, mean_, sigma_): rel_diff_allowed = 0.02 self.assertAlmostEqual( res['aggtype_"centroid"'], expected_fft_centroid, - delta=rel_diff_allowed*expected_fft_centroid + delta=rel_diff_allowed * expected_fft_centroid ) self.assertAlmostEqual( res['aggtype_"variance"'], expected_fft_var, - delta=rel_diff_allowed*expected_fft_var + delta=rel_diff_allowed * expected_fft_var ) def test_number_peaks(self): @@ -886,7 +889,6 @@ def test_linear_trend(self): param = [{"attr": "pvalue"}, {"attr": "rvalue"}, {"attr": "intercept"}, {"attr": "slope"}, {"attr": "stderr"}] res = linear_trend(x, param) - res = pd.Series(dict(res)) expected_index = ["attr_\"pvalue\"", "attr_\"intercept\"", @@ -922,14 +924,17 @@ def test_linear_trend(self): def test__aggregate_on_chunks(self): self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 2, 3]), f_agg="max", chunk_len=2), [1, 3]) - self.assertListEqual(_aggregate_on_chunks(x=pd.Series([1, 1, 3, 3]), f_agg="max", chunk_len=2), [1, 3]) + self.assertListEqual(_aggregate_on_chunks(x=pd.Series([1, 1, 3, 3]), f_agg="max", chunk_len=2), [1, 3]) self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 2, 3]), f_agg="min", chunk_len=2), [0, 2]) self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 2, 3, 5]), f_agg="min", chunk_len=2), [0, 2, 5]) - self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 2, 3]), f_agg="mean", chunk_len=2), [0.5, 2.5]) - self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 0, 4, 5]), f_agg="mean", chunk_len=2), [0.5, 2, 5]) - self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 0, 4, 5]), f_agg="mean", chunk_len=3), [1/3, 4.5]) + self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 2, 3]), f_agg="mean", chunk_len=2), + [0.5, 2.5]) + self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 0, 4, 5]), f_agg="mean", chunk_len=2), + [0.5, 2, 5]) + self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 0, 4, 5]), f_agg="mean", chunk_len=3), + [1 / 3, 4.5]) self.assertListEqual(_aggregate_on_chunks(x=pd.Series([0, 1, 2, 3, 5, -2]), f_agg="median", chunk_len=2), [0.5, 2.5, 1.5]) @@ -1090,7 +1095,7 @@ def test_linear_trend_timewise_seconds(self): """Test linear_trend_timewise function with second intervals.""" # Try with different days x = pd.Series( - [0, 1/float(3600), 2/float(3600), 3/float(3600)], + [0, 1 / float(3600), 2 / float(3600), 3 / float(3600)], index=pd.DatetimeIndex([ '2018-01-01 04:00:01', '2018-01-01 04:00:02', '2018-01-01 04:00:03', '2018-01-01 04:00:04' @@ -1111,7 +1116,7 @@ def test_linear_trend_timewise_years(self): """Test linear_trend_timewise function with year intervals.""" # Try with different days x = pd.Series( - [0, 365*24, 365*48, 365*72+24], # Add 24 to the last one since it's a leap year + [0, 365 * 24, 365 * 48, 365 * 72 + 24], # Add 24 to the last one since it's a leap year index=pd.DatetimeIndex([ '2018-01-01 04:00:00', '2019-01-01 04:00:00', '2020-01-01 04:00:00', '2021-01-01 04:00:00' @@ -1134,7 +1139,7 @@ def test_change_quantiles(self): res = change_quantiles(np.random.rand(10000) * 1000, 0.1, 0.2, False, 'mean') self.assertAlmostEqual(res, -0.9443846621365727) - + class FriedrichTestCase(TestCase): def test_estimate_friedrich_coefficients(self): @@ -1177,10 +1182,10 @@ def test_friedrich_number_of_returned_features_is_equal_to_number_of_parameters( def test_friedrich_equal_to_snapshot(self): param = [{"coeff": coeff, "m": 2, "r": 30} for coeff in range(4)] - x = np.array([-0.53, -0.61, -1.26, -0.88, -0.34, 0.58, 2.86, -0.47, 0.78, - -0.45, -0.27, 0.43, 1.72, 0.26, 1.02, -0.09, 0.65, 1.49, - -0.95, -1.02, -0.64, -1.63, -0.71, -0.43, -1.69, 0.05, 1.58, - 1.1, 0.55, -1.02]) + x = np.array([-0.53, -0.61, -1.26, -0.88, -0.34, 0.58, 2.86, -0.47, 0.78, + -0.45, -0.27, 0.43, 1.72, 0.26, 1.02, -0.09, 0.65, 1.49, + -0.95, -1.02, -0.64, -1.63, -0.71, -0.43, -1.69, 0.05, 1.58, + 1.1, 0.55, -1.02]) res = pd.Series(dict(friedrich_coefficients(x, param))) diff --git a/tests/units/feature_extraction/test_settings.py b/tests/units/feature_extraction/test_settings.py index e63ee95ba..df4ded835 100644 --- a/tests/units/feature_extraction/test_settings.py +++ b/tests/units/feature_extraction/test_settings.py @@ -41,8 +41,8 @@ def test_from_column_correct_for_selected_columns(self): kind_to_fc_parameters = from_columns(feature_names) self.assertCountEqual(list(kind_to_fc_parameters[tsn].keys()), - ["sum_values", "median", "length", "sample_entropy", "quantile", "number_peaks", - "ar_coefficient", "value_count"]) + ["sum_values", "median", "length", "sample_entropy", "quantile", "number_peaks", + "ar_coefficient", "value_count"]) self.assertIsNone(kind_to_fc_parameters[tsn]["sum_values"]) self.assertEqual(kind_to_fc_parameters[tsn]["ar_coefficient"], @@ -74,7 +74,7 @@ def test_from_columns_ignores_columns(self): "THIS_AS_WELL"]) self.assertCountEqual(list(kind_to_fc_parameters[tsn].keys()), - ["sum_values", "median", "length", "sample_entropy"]) + ["sum_values", "median", "length", "sample_entropy"]) def test_default_calculates_all_features(self): """ @@ -195,6 +195,6 @@ def test_extraction_runs_through(self): column_sort="time", column_id="id") self.assertCountEqual(extracted_features.columns, ["0__median", "0__standard_deviation", "0__sum_values", - "0__maximum", "0__variance", "0__minimum", "0__mean", - "0__length"]) + "0__maximum", "0__variance", "0__minimum", "0__mean", + "0__length"]) self.assertCountEqual(extracted_features.index, [0, 1]) diff --git a/tests/units/feature_selection/test_checks.py b/tests/units/feature_selection/test_checks.py index 23974e9c5..b9c8690ae 100644 --- a/tests/units/feature_selection/test_checks.py +++ b/tests/units/feature_selection/test_checks.py @@ -7,8 +7,8 @@ import numpy as np from tsfresh.defaults import TEST_FOR_BINARY_TARGET_REAL_FEATURE -from tsfresh.feature_selection.significance_tests import target_real_feature_binary_test, target_real_feature_real_test,\ - target_binary_feature_real_test, target_binary_feature_binary_test +from tsfresh.feature_selection.significance_tests import target_real_feature_binary_test,\ + target_real_feature_real_test, target_binary_feature_real_test, target_binary_feature_binary_test @pytest.fixture() @@ -35,6 +35,7 @@ class TestChecksBinaryReal: """ Test the checks for the `target_binary_feature_real_test`. """ + def test_check_target_is_binary(self, real_series): with pytest.raises(ValueError): target_binary_feature_real_test(x=real_series, y=real_series, @@ -67,6 +68,7 @@ class TestChecksBinaryBinary: """ Test the checks for the `target_binary_feature_binary_test`. """ + def test_checks_feature_is_binary(self, binary_series, real_series): with pytest.raises(ValueError): target_binary_feature_binary_test(x=real_series, y=binary_series) @@ -96,6 +98,7 @@ class TestChecksRealReal: """ Test the checks for the `target_real_feature_real_test`. """ + def test_checks_feature_is_series(self, real_series): with pytest.raises(TypeError): target_real_feature_real_test(x=real_series.values, y=real_series) @@ -117,6 +120,7 @@ class TestChecksRealBinary: """ Test the checks for the `target_real_feature_binary_test`. """ + def test_feature_is_binary(self, real_series): with pytest.raises(ValueError): target_real_feature_binary_test(x=real_series, y=real_series) diff --git a/tests/units/feature_selection/test_relevance.py b/tests/units/feature_selection/test_relevance.py index 522707673..bc47b9001 100644 --- a/tests/units/feature_selection/test_relevance.py +++ b/tests/units/feature_selection/test_relevance.py @@ -17,7 +17,6 @@ def test_infers_classification_for_integer_target(self): y = pd.Series([1, 2, 3]) assert 'classification' == infer_ml_task(y) - def test_infers_classification_for_boolean_target(self): y = pd.Series([True, False, False]) assert 'classification' == infer_ml_task(y) @@ -58,12 +57,12 @@ def test_constant_feature_irrelevant(self, y_binary): assert "feature_binary" == relevance_table.index[0] assert 'constant' == relevance_table.type[0] assert np.isnan(relevance_table.p_value[0]) - assert False == relevance_table.relevant[0] + assert not relevance_table.relevant[0] @mock.patch('tsfresh.feature_selection.relevance.target_binary_feature_real_test') @mock.patch('tsfresh.feature_selection.relevance.target_binary_feature_binary_test') def test_target_binary_calls_correct_tests(self, significance_test_feature_binary_mock, - significance_test_feature_real_mock, X, y_binary): + significance_test_feature_real_mock, X, y_binary): significance_test_feature_binary_mock.return_value = 0.5 significance_test_feature_real_mock.return_value = 0.7 relevance_table = calculate_relevance_table(X, y_binary, n_jobs=0) @@ -76,7 +75,7 @@ def test_target_binary_calls_correct_tests(self, significance_test_feature_binar @mock.patch('tsfresh.feature_selection.relevance.target_real_feature_real_test') @mock.patch('tsfresh.feature_selection.relevance.target_real_feature_binary_test') def test_target_real_calls_correct_tests(self, significance_test_feature_binary_mock, - significance_test_feature_real_mock, X, y_real): + significance_test_feature_real_mock, X, y_real): significance_test_feature_binary_mock.return_value = 0.5 significance_test_feature_real_mock.return_value = 0.7 @@ -96,10 +95,11 @@ def test_warning_for_no_relevant_feature(self, significance_test_feature_binary_ with mock.patch('logging.Logger.warning') as m: _ = calculate_relevance_table(X, y_real, n_jobs=0, ml_task="regression") - m.assert_called_with("No feature was found relevant for regression for fdr level = 0.05 (which corresponds " + m.assert_called_with("No feature was found relevant for regression for fdr level = 0.05 (which corresponds " "to the maximal percentage of irrelevant features, consider using an higher fdr level " "or add other features.") + class TestCombineRelevanceTables: @pytest.fixture() def relevance_table(self): @@ -143,4 +143,4 @@ def test_constant(self): def test_real(self): feature = pd.Series([0.0, 1.0, 2.0]) - assert 'real' == get_feature_type(feature) \ No newline at end of file + assert 'real' == get_feature_type(feature) diff --git a/tests/units/feature_selection/test_selection.py b/tests/units/feature_selection/test_selection.py index 94e30836e..f09782c69 100644 --- a/tests/units/feature_selection/test_selection.py +++ b/tests/units/feature_selection/test_selection.py @@ -12,7 +12,7 @@ class TestSelectFeatures: def test_assert_list(self): with pytest.raises(AssertionError): - select_features(pd.DataFrame(index=range(2)),[1,2,3]) + select_features(pd.DataFrame(index=range(2)), [1, 2, 3]) def test_assert_one_row_X(self): X = pd.DataFrame([1], index=[1]) diff --git a/tests/units/feature_selection/test_significance_tests.py b/tests/units/feature_selection/test_significance_tests.py index 66d696d6b..950c8d6ea 100644 --- a/tests/units/feature_selection/test_significance_tests.py +++ b/tests/units/feature_selection/test_significance_tests.py @@ -40,10 +40,9 @@ class TestUnsignificant: def minimal_p_value_for_unsignificant_features(self): return 0.05 - def test_feature_selection_target_binary_features_binary(self, minimal_p_value_for_unsignificant_features, - binary_feature, - binary_target_not_related): + binary_feature, + binary_target_not_related): """ Test if the p_value returned by target_binary_feature_binary_test is large enough for highly unsignificant features. @@ -51,10 +50,9 @@ def test_feature_selection_target_binary_features_binary(self, minimal_p_value_f p_value = target_binary_feature_binary_test(binary_feature, binary_target_not_related) assert minimal_p_value_for_unsignificant_features < p_value - def test_feature_selection_target_binary_features_realvalued(self, minimal_p_value_for_unsignificant_features, - real_feature, - binary_target_not_related): + real_feature, + binary_target_not_related): """ Test if the p_value returned by target_binary_feature_binary_test is large enough for highly unsignificant features. @@ -63,20 +61,18 @@ def test_feature_selection_target_binary_features_realvalued(self, minimal_p_val TEST_FOR_BINARY_TARGET_REAL_FEATURE) assert minimal_p_value_for_unsignificant_features < p_value - def test_feature_selection_target_realvalued_features_binary(self, minimal_p_value_for_unsignificant_features, - binary_feature, - real_target_not_related): + binary_feature, + real_target_not_related): """ Test if the p_value returned by target_real_feature_binary_test is large enough for highly unsignificant features.""" p_value = target_real_feature_binary_test(binary_feature, real_target_not_related) assert minimal_p_value_for_unsignificant_features < p_value - def test_feature_selection_target_realvalued_features_realvalued(self, minimal_p_value_for_unsignificant_features, - real_feature, - real_target_not_related): + real_feature, + real_target_not_related): """ Test if the p_value returned by target_real_feature_real_test is large enough for highly unsignificant features. @@ -91,7 +87,7 @@ def maximal_p_value_for_significant_features(self): return 0.15 def test_feature_selection_target_binary_features_binary(self, maximal_p_value_for_significant_features, - binary_feature): + binary_feature): """ Test if the p_value returned by target_binary_feature_binary_test is low enough for highly significant features. @@ -104,9 +100,8 @@ def test_feature_selection_target_binary_features_binary(self, maximal_p_value_f p_value = target_binary_feature_binary_test(binary_feature, y) assert maximal_p_value_for_significant_features > p_value - def test_feature_selection_target_binary_features_realvalued_mann(self, maximal_p_value_for_significant_features, - real_feature): + real_feature): """ Test if the p_value returned by target_binary_feature_real_test is low enough for highly significant features. @@ -121,9 +116,8 @@ def test_feature_selection_target_binary_features_realvalued_mann(self, maximal_ p_value = target_binary_feature_real_test(real_feature, y, TEST_FOR_BINARY_TARGET_REAL_FEATURE) assert maximal_p_value_for_significant_features > p_value - def test_feature_selection_target_binary_features_realvalued_smir(self, maximal_p_value_for_significant_features, - real_feature): + real_feature): """ Test if the p_value returned by target_binary_feature_real_test is low enough for highly significant features. @@ -138,9 +132,8 @@ def test_feature_selection_target_binary_features_realvalued_smir(self, maximal_ p_value = target_binary_feature_real_test(real_feature, y, test="smir") assert maximal_p_value_for_significant_features > p_value - def test_feature_selection_target_realvalued_features_binary(self, maximal_p_value_for_significant_features, - binary_feature): + binary_feature): """ Test if the p_value returned by target_real_feature_binary_test is low enough for highly significant features. @@ -150,9 +143,8 @@ def test_feature_selection_target_realvalued_features_binary(self, maximal_p_val p_value = target_real_feature_binary_test(binary_feature, y) assert maximal_p_value_for_significant_features > p_value - def test_feature_selection_target_realvalued_features_realvalued(self, maximal_p_value_for_significant_features, - real_feature): + real_feature): """ Test if the p_value returned by target_real_feature_real_test is low enough for highly significant features. diff --git a/tests/units/scripts/test_run_tsfresh.py b/tests/units/scripts/test_run_tsfresh.py index 736e530e5..b8d04bbb3 100644 --- a/tests/units/scripts/test_run_tsfresh.py +++ b/tests/units/scripts/test_run_tsfresh.py @@ -14,6 +14,7 @@ class RunTSFreshTestCase(TestCase): Test the command line interface to tsfresh. This does not test the tsfresh functionality (this is tested elsewhere), but mocks the extract_features functionality by just outputting the same df as going in into the function. """ + def setUp(self): # Create a temporary directory self.test_dir = tempfile.mkdtemp() @@ -92,8 +93,9 @@ def test_csv_with_header(self): input_csv = "ID SORT KIND VALUE\n0 0 a 0\n1 0 a 1\n0 0 b 1\n1 0 b 3" output_csv = ",ID,SORT,KIND,VALUE\n0,0,0,a,0\n1,1,0,a,1\n2,0,0,b,1\n3,1,0,b,3\n" - result_csv = self.call_main_function(input_csv_string=input_csv, - arguments="--column-id ID --column-sort SORT --column-value VALUE --column-kind KIND --csv-with-headers") + result_csv = self.call_main_function( + input_csv_string=input_csv, + arguments="--column-id ID --column-sort SORT --column-value VALUE --column-kind KIND --csv-with-headers") self.assertEqual(result_csv, output_csv) @@ -102,4 +104,4 @@ def test_csv_with_header(self): self.assertEqual(called_kwargs["column_id"], "ID") self.assertEqual(called_kwargs["column_value"], "VALUE") self.assertEqual(called_kwargs["column_kind"], "KIND") - self.assertEqual(called_kwargs["column_sort"], "SORT") \ No newline at end of file + self.assertEqual(called_kwargs["column_sort"], "SORT") diff --git a/tests/units/transformers/test_feature_augmenter.py b/tests/units/transformers/test_feature_augmenter.py index c3d067785..ef3845139 100644 --- a/tests/units/transformers/test_feature_augmenter.py +++ b/tests/units/transformers/test_feature_augmenter.py @@ -8,6 +8,7 @@ from tsfresh.transformers import FeatureAugmenter import numpy as np + class FeatureAugmenterTestCase(DataTestCase): def setUp(self): self.test_df = self.create_test_data_sample() @@ -21,7 +22,7 @@ def test_fit_and_transform(self): column_kind="kind", kind_to_fc_parameters=self.kind_to_fc_parameters, n_jobs=0, - disable_progressbar = True) + disable_progressbar=True) # Fit should do nothing returned_df = augmenter.fit() @@ -31,7 +32,7 @@ def test_fit_and_transform(self): augmenter.set_timeseries_container(self.test_df) # Add features to all time series - X_with_index = pd.DataFrame([{"feature_1": 1}]*2, index=[10, 500]) + X_with_index = pd.DataFrame([{"feature_1": 1}] * 2, index=[10, 500]) X_transformed = augmenter.transform(X_with_index) # Require same shape @@ -57,7 +58,7 @@ def test_add_features_to_only_a_part(self): column_kind="kind", kind_to_fc_parameters=self.kind_to_fc_parameters, n_jobs=0, - disable_progressbar = True) + disable_progressbar=True) augmenter.set_timeseries_container(self.test_df) diff --git a/tests/units/transformers/test_feature_selector.py b/tests/units/transformers/test_feature_selector.py index b85790af0..7bd344e93 100644 --- a/tests/units/transformers/test_feature_selector.py +++ b/tests/units/transformers/test_feature_selector.py @@ -10,11 +10,11 @@ from tsfresh.transformers.feature_selector import FeatureSelector + class FeatureSelectorTestCase(TestCase): def setUp(self): np.random.seed(0) - def test_not_fitted(self): selector = FeatureSelector() @@ -98,11 +98,11 @@ def test_with_numpy_array(self): self.assertTrue((selected_X_numpy == selected_X.values).all()) self.assertTrue(selected_X_numpy.shape, (1, 1000)) - + def test_feature_importance(self): selector = FeatureSelector() self.assertIsNone(selector.feature_importances_) - + y = pd.Series(np.random.binomial(1, 0.5, 1000)) X = pd.DataFrame(index=list(range(1000))) @@ -116,7 +116,7 @@ def test_feature_importance(self): def test_feature_importance(self): selector = FeatureSelector() self.assertIsNone(selector.p_values) - + y = pd.Series(np.random.binomial(1, 0.5, 1000)) X = pd.DataFrame(index=list(range(1000))) @@ -127,5 +127,4 @@ def test_feature_importance(self): self.assertEqual(selector.p_values.shape, (2,)) np.testing.assert_almost_equal(selector.p_values, - 1.0-selector.feature_importances_) - + 1.0 - selector.feature_importances_) diff --git a/tests/units/transformers/test_per_column_imputer.py b/tests/units/transformers/test_per_column_imputer.py index b6a326939..2f7ebabc5 100644 --- a/tests/units/transformers/test_per_column_imputer.py +++ b/tests/units/transformers/test_per_column_imputer.py @@ -53,7 +53,7 @@ def test_with_numpy_array(self): imputer.fit(X) selected_X = imputer.transform(X) - #re-initialize for new dicts + # re-initialize for new dicts imputer = PerColumnImputer() imputer.fit(X_numpy) selected_X_numpy = imputer.transform(X_numpy) @@ -166,8 +166,8 @@ def test_only_subset_of_columns_given(self): data = [np.NINF, np.PINF, np.nan, 100.0, -100.0, 1.0, 1.0] truth_a = [-100.0, 100.0, 0.0, 100.0, -100.0, 1.0, 1.0] truth_b = [-100.0, 100.0, 1.0, 100.0, -100.0, 1.0, 1.0] - X = pd.DataFrame({"a": data, "b":data}) - true_X = pd.DataFrame({"a":truth_a, "b":truth_b}) + X = pd.DataFrame({"a": data, "b": data}) + true_X = pd.DataFrame({"a": truth_a, "b": truth_b}) col_to_median = {"a": 0} imputer = PerColumnImputer(col_to_NAN_repl_preset=col_to_median) @@ -175,13 +175,13 @@ def test_only_subset_of_columns_given(self): imputer.fit(X) selected_X = imputer.transform(X) - pdt.assert_frame_equal(selected_X,true_X) + pdt.assert_frame_equal(selected_X, true_X) def test_NINF_preset_contains_more_columns_than_dataframe_to_fit(self): X = pd.DataFrame(index=list(range(10))) X["a"] = np.ones(10) - col_to_min = {"a": 0, "b":0} + col_to_min = {"a": 0, "b": 0} imputer = PerColumnImputer(col_to_NINF_repl_preset=col_to_min) @@ -191,7 +191,7 @@ def test_PINF_preset_contains_more_columns_than_dataframe_to_fit(self): X = pd.DataFrame(index=list(range(10))) X["a"] = np.ones(10) - col_to_max = {"a": 0, "b":0} + col_to_max = {"a": 0, "b": 0} imputer = PerColumnImputer(col_to_PINF_repl_preset=col_to_max) @@ -201,7 +201,7 @@ def test_NAN_preset_contains_more_columns_than_dataframe_to_fit(self): X = pd.DataFrame(index=list(range(10))) X["a"] = np.ones(10) - col_to_median = {"a": 0, "b":0} + col_to_median = {"a": 0, "b": 0} imputer = PerColumnImputer(col_to_NAN_repl_preset=col_to_median) diff --git a/tests/units/utilities/test_dataframe_functions.py b/tests/units/utilities/test_dataframe_functions.py index dda9661a5..72646d6db 100644 --- a/tests/units/utilities/test_dataframe_functions.py +++ b/tests/units/utilities/test_dataframe_functions.py @@ -41,8 +41,10 @@ def test_with_dictionaries_two_rows(self): self.assertEqual(column_id, "id") # Assert sorted and without sort column - self.assertEqual(result_df[result_df[column_kind] == "a"].iloc[0].to_dict(), {"_variables": "a", "value": 1, "id": "id_1"}) - self.assertEqual(result_df[result_df[column_kind] == "a"].iloc[1].to_dict(), {"_variables": "a", "value": 2, "id": "id_1"}) + self.assertEqual(result_df[result_df[column_kind] == "a"].iloc[0].to_dict(), + {"_variables": "a", "value": 1, "id": "id_1"}) + self.assertEqual(result_df[result_df[column_kind] == "a"].iloc[1].to_dict(), + {"_variables": "a", "value": 2, "id": "id_1"}) def test_with_dictionaries_two_rows_sorted(self): test_df = pd.DataFrame([{"value": 2, "id": "id_1"}, @@ -55,7 +57,8 @@ def test_with_dictionaries_two_rows_sorted(self): self.assertEqual(column_value, "value") self.assertEqual(column_id, "id") - self.assertEqual(result_df[result_df[column_kind] == "a"].iloc[0].to_dict(), {"_variables": "a", "value": 2, "id": "id_1"}) + self.assertEqual(result_df[result_df[column_kind] == "a"].iloc[0].to_dict(), + {"_variables": "a", "value": 2, "id": "id_1"}) def test_with_df_1(self): # give everyting @@ -146,11 +149,25 @@ def test_with_wrong_input(self): None, None, None, "value") test_df = pd.DataFrame([{"id": 0, "a_": 3, "b": 5, "sort": 1}]) - self.assertRaises(ValueError, dataframe_functions._normalize_input_to_internal_representation, test_df, "id", "sort", None, None) + self.assertRaises( + ValueError, + dataframe_functions._normalize_input_to_internal_representation, + test_df, + "id", + "sort", + None, + None) test_df = pd.DataFrame([{"id": 0, "a__c": 3, "b": 5, "sort": 1}]) - self.assertRaises(ValueError, dataframe_functions._normalize_input_to_internal_representation, test_df, "id", "sort", None, None) - + self.assertRaises( + ValueError, + dataframe_functions._normalize_input_to_internal_representation, + test_df, + "id", + "sort", + None, + None) + test_df = pd.DataFrame([{"id": 0}]) self.assertRaises(ValueError, dataframe_functions._normalize_input_to_internal_representation, test_df, "id", None, None, None) @@ -159,7 +176,6 @@ def test_with_wrong_input(self): self.assertRaises(ValueError, dataframe_functions._normalize_input_to_internal_representation, test_df, "id", "sort", None, None) - def test_wide_dataframe_order_preserved_with_sort_column(self): """ verifies that the order of the sort column from a wide time series container is preserved """ @@ -178,7 +194,6 @@ def test_wide_dataframe_order_preserved_with_sort_column(self): assert (test_df.sort_values("sort").query("id=='a'")["v2"].values == melt_df.query("id=='a'").query("_variables=='v2'")["_values"].values).all() - def test_wide_dataframe_order_preserved(self): """ verifies that the order of the time series inside a wide time series container are preserved (columns_sort=None) @@ -196,6 +211,7 @@ def test_wide_dataframe_order_preserved(self): assert (test_df.query("id=='a'")["v2"].values == melt_df.query("id=='a'").query("_variables=='v2'")["_values"].values).all() + class RollingTestCase(TestCase): def test_with_wrong_input(self): test_df = pd.DataFrame({"id": [0, 0], "kind": ["a", "b"], "value": [3, 3], "sort": [np.NaN, np.NaN]}) @@ -276,7 +292,6 @@ def test_positive_rolling(self): column_kind=None, rolling_direction=1, max_timeshift=4) - self.assertListEqual(list(df["id"]), correct_indices) self.assertListEqual(list(df["a"].values), correct_values_a) self.assertListEqual(list(df["b"].values), correct_values_b) @@ -285,7 +300,7 @@ def test_positive_rolling(self): column_kind=None, rolling_direction=1, max_timeshift=2) - correct_indices = [0, 1, 1, 2, 2, 2, 3, 3, 3, 20, 21, 21] + correct_indices = [0, 1, 1, 2, 2, 2, 3, 3, 3, 20, 21, 21] correct_values_a = [1.0, 1.0, 2.0, 1.0, 2.0, 3.0, 2.0, 3.0, 4.0, 10.0, 10.0, 11.0] correct_values_b = [5.0, 5.0, 6.0, 5.0, 6.0, 7.0, 6.0, 7.0, 8.0, 12.0, 12.0, 13.0] @@ -293,7 +308,6 @@ def test_positive_rolling(self): self.assertListEqual(list(df["a"].values), correct_values_a) self.assertListEqual(list(df["b"].values), correct_values_b) - def test_negative_rolling(self): first_class = pd.DataFrame({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "time": range(4)}) second_class = pd.DataFrame({"a": [10, 11], "b": [12, 13], "time": range(20, 22)}) @@ -302,7 +316,7 @@ def test_negative_rolling(self): second_class["id"] = 2 df_full = pd.concat([first_class, second_class], ignore_index=True) - """ df_full is + """ df_full is a b time id 0 1 5 0 1 1 2 6 1 1 @@ -380,7 +394,7 @@ def test_stacked_rolling(self): df_full[["time", "id", "b"]].rename(columns={"b": "_value"})], ignore_index=True) df_stacked["kind"] = ["a"] * 6 + ["b"] * 6 - """ df_stacked is + """ df_stacked is time id _value kind 0 0 1 1 a 1 1 1 2 a @@ -399,7 +413,7 @@ def test_stacked_rolling(self): df = dataframe_functions.roll_time_series(df_stacked, column_id="id", column_sort="time", column_kind="kind", rolling_direction=-1) - correct_indices = ([0]*2*4 + [1]*2*3 + [2]*2*2 + [3]*2*1 + [20]*4 + [21] *2) + correct_indices = ([0] * 2 * 4 + [1] * 2 * 3 + [2] * 2 * 2 + [3] * 2 * 1 + [20] * 4 + [21] * 2) self.assertListEqual(list(df["id"].values), correct_indices) print(df["_value"].values) @@ -415,7 +429,7 @@ def test_dict_rolling(self): } df = dataframe_functions.roll_time_series(df_dict, column_id="id", column_sort=None, column_kind=None, rolling_direction=-1) - """ df is + """ df is {a: _value sort id 7 1.0 0.0 0 3 2.0 1.0 0 @@ -429,8 +443,8 @@ def test_dict_rolling(self): 10 4.0 3.0 3 11 10.0 4.0 4 6 11.0 5.0 4 - 12 11.0 5.0 5, - + 12 11.0 5.0 5, + b: _value sort id 7 5.0 0.0 0 3 6.0 1.0 0 @@ -464,7 +478,7 @@ def test_dict_rolling_maxshift_1(self): } df = dataframe_functions.roll_time_series(df_dict, column_id="id", column_sort=None, column_kind=None, rolling_direction=-1, max_timeshift=1) - """ df is + """ df is {a: _value sort id 7 1.0 0.0 0 3 2.0 1.0 0 @@ -475,7 +489,7 @@ def test_dict_rolling_maxshift_1(self): 10 4.0 3.0 3 11 10.0 4.0 4 6 11.0 5.0 4 - 12 11.0 5.0 5, + 12 11.0 5.0 5, b: _value sort id 7 5.0 0.0 0 @@ -649,7 +663,6 @@ def get_df(): self.assertRaises(ValueError, dataframe_functions.impute_dataframe_range, df, col_to_max, col_to_min, col_to_median) - df = pd.DataFrame([0, 1, 2, 3, 4], columns=["test"]) col_dict = {"test": 0} dataframe_functions.impute_dataframe_range(df, col_dict, col_dict, col_dict) @@ -726,16 +739,18 @@ def test_no_finite_values_yields_0(self): class MakeForecastingFrameTestCase(TestCase): def test_make_forecasting_frame_list(self): - df, y = dataframe_functions.make_forecasting_frame(x=range(4), kind="test", max_timeshift=1, rolling_direction=1) - expected_df = pd.DataFrame({"id": [1, 2, 3], "kind": ["test"]*3, "value": [0., 1., 2.], "time": [0., 1., 2.]}) + df, y = dataframe_functions.make_forecasting_frame(x=range(4), kind="test", + max_timeshift=1, rolling_direction=1) + expected_df = pd.DataFrame({"id": [1, 2, 3], "kind": ["test"] * 3, "value": [0., 1., 2.], "time": [0., 1., 2.]}) expected_y = pd.Series(data=[1, 2, 3], index=[1, 2, 3], name="value") assert_frame_equal(df.sort_index(axis=1), expected_df.sort_index(axis=1)) assert_series_equal(y, expected_y) def test_make_forecasting_frame_range(self): - df, y = dataframe_functions.make_forecasting_frame(x=np.arange(4), kind="test", max_timeshift=1, rolling_direction=1) - expected_df = pd.DataFrame({"id": [1, 2, 3], "kind": ["test"]*3, "value": [0., 1., 2.], "time": [0., 1., 2.]}) + df, y = dataframe_functions.make_forecasting_frame(x=np.arange(4), kind="test", + max_timeshift=1, rolling_direction=1) + expected_df = pd.DataFrame({"id": [1, 2, 3], "kind": ["test"] * 3, "value": [0., 1., 2.], "time": [0., 1., 2.]}) assert_frame_equal(df.sort_index(axis=1), expected_df.sort_index(axis=1)) def test_make_forecasting_frame_pdSeries(self): @@ -748,7 +763,7 @@ def test_make_forecasting_frame_pdSeries(self): "2011-01-01 03:00:00"]), name="value") expected_df = pd.DataFrame({"id": pd.DatetimeIndex(["2011-01-01 01:00:00", "2011-01-01 02:00:00", "2011-01-01 03:00:00"]), - "kind": ["test"]*3, "value": [0., 1., 2.], + "kind": ["test"] * 3, "value": [0., 1., 2.], "time": pd.DatetimeIndex(["2011-01-01 00:00:00", "2011-01-01 01:00:00", "2011-01-01 02:00:00"]) }) diff --git a/tests/units/utilities/test_distribution.py b/tests/units/utilities/test_distribution.py index 0da8a2a50..53848ba5d 100644 --- a/tests/units/utilities/test_distribution.py +++ b/tests/units/utilities/test_distribution.py @@ -63,7 +63,6 @@ def test_local_dask_cluster_extraction_one_worker(self): self.assertTrue(np.all(extracted_features.b__mean == np.array([37.85, 34.75]))) self.assertTrue(np.all(extracted_features.b__median == np.array([39.5, 28.0]))) - def test_local_dask_cluster_extraction_two_worker(self): Distributor = LocalDaskDistributor(n_workers=2) @@ -83,6 +82,7 @@ def test_local_dask_cluster_extraction_two_worker(self): self.assertTrue(np.all(extracted_features.b__mean == np.array([37.85, 34.75]))) self.assertTrue(np.all(extracted_features.b__median == np.array([39.5, 28.0]))) + class ClusterDaskDistributorTestCase(DataTestCase): def test_dask_cluster_extraction_one_worker(self): @@ -128,4 +128,3 @@ def test_dask_cluster_extraction_two_workers(self): self.assertTrue(np.all(extracted_features.b__mean == np.array([37.85, 34.75]))) self.assertTrue(np.all(extracted_features.b__median == np.array([39.5, 28.0]))) cluster.close() - diff --git a/tsfresh/__init__.py b/tsfresh/__init__.py index 15a08b854..267266882 100644 --- a/tsfresh/__init__.py +++ b/tsfresh/__init__.py @@ -14,7 +14,7 @@ try: __version__ = pkg_resources.get_distribution(__name__).version -except: +except Exception: __version__ = 'unknown' @@ -25,6 +25,6 @@ logging.getLogger(__name__).addHandler(NullHandler()) -from tsfresh.convenience.relevant_extraction import extract_relevant_features -from tsfresh.feature_extraction import extract_features -from tsfresh.feature_selection import select_features +from tsfresh.convenience.relevant_extraction import extract_relevant_features # noqa: E402 +from tsfresh.feature_extraction import extract_features # noqa: E402 +from tsfresh.feature_selection import select_features # noqa: E402 diff --git a/tsfresh/convenience/relevant_extraction.py b/tsfresh/convenience/relevant_extraction.py index abc2a9a1a..36ed1ca11 100644 --- a/tsfresh/convenience/relevant_extraction.py +++ b/tsfresh/convenience/relevant_extraction.py @@ -103,7 +103,8 @@ def extract_relevant_features(timeseries_container, y, X=None, :param profiling_filename: Where to save the profiling results. :type profiling_filename: basestring - :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature (currently unused) + :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature + (currently unused) :type test_for_binary_target_binary_feature: str :param test_for_binary_target_real_feature: Which test to be used for binary target, real feature diff --git a/tsfresh/examples/driftbif_simulation.py b/tsfresh/examples/driftbif_simulation.py index db037046d..c6852f965 100644 --- a/tsfresh/examples/driftbif_simulation.py +++ b/tsfresh/examples/driftbif_simulation.py @@ -30,16 +30,21 @@ class velocity: >>> ds = velocity(tau=3.5) # Dissipative soliton with equilibrium velocity 1.5e-3 >>> print(ds.label) # Discriminating before or beyond Drift-Bifurcation 1 - >>> print(ds.deterministic) # Equilibrium velocity + + # Equilibrium velocity + >>> print(ds.deterministic) 0.0015191090506254991 - >>> v = ds.simulate(20000) #Simulated velocity as a time series with 20000 time steps being disturbed by Gaussian white noise + + # Simulated velocity as a time series with 20000 time steps being disturbed by Gaussian white noise + >>> v = ds.simulate(20000) """ def __init__(self, tau=3.8, kappa_3=0.3, Q=1950.0, R=3e-4, delta_t=0.05, seed=None): """ - :param tau: Bifurcation parameter determining the intrinsic velocity of the dissipative soliton, which is zero for tau<=1.0/kappa_3 and np.sqrt(kappa_3**3/Q * (tau - 1.0/kappa_3)) otherwise + :param tau: Bifurcation parameter determining the intrinsic velocity of the dissipative soliton, + which is zero for tau<=1.0/kappa_3 and np.sqrt(kappa_3**3/Q * (tau - 1.0/kappa_3)) otherwise :type tau: float - :param kappa_3: Inverse bifurcation point. + :param kappa_3: Inverse bifurcation point. :type kappa_3: :param Q: Shape parameter of dissipative soliton :type Q: float @@ -60,7 +65,7 @@ def __init__(self, tau=3.8, kappa_3=0.3, Q=1950.0, R=3e-4, delta_t=0.05, seed=No self.c = np.sqrt(self.delta_t) * R self.delta_t = self.delta_t - if not seed is None: + if seed is not None: np.random.seed(seed) if tau <= 1.0 / kappa_3: @@ -85,7 +90,7 @@ def simulate(self, N, v0=np.zeros(2)): :param N: number of time steps :type N: int - :param v0: initial velocity vector + :param v0: initial velocity vector :type v0: ndarray :return: time series of velocity vectors with shape (N, v0.shape[0]) :rtype: ndarray @@ -106,8 +111,8 @@ def sample_tau(n=10, kappa_3=0.3, ratio=0.5, rel_increase=0.15): """ Return list of control parameters - :param n: number of samples - :type n: int + :param n: number of samples + :type n: int :param kappa_3: inverse bifurcation point :type kappa_3: float :param ratio: ratio (default 0.5) of samples before and beyond drift-bifurcation @@ -138,7 +143,7 @@ def load_driftbif(n, l, m=2, classification=True, kappa_3=0.3, seed=False): classification=False: target is bifurcation parameter tau - :param n: number of samples + :param n: number of samples :type n: int :param l: length of the time series :type l: int diff --git a/tsfresh/examples/har_dataset.py b/tsfresh/examples/har_dataset.py index a4b3859e3..ca60849c4 100644 --- a/tsfresh/examples/har_dataset.py +++ b/tsfresh/examples/har_dataset.py @@ -37,7 +37,7 @@ def download_har_dataset(): """ Download human activity recognition dataset from UCI ML Repository and store it at /tsfresh/notebooks/data. - + Examples ======== @@ -45,7 +45,7 @@ def download_har_dataset(): >>> har_dataset.download_har_dataset() """ - zipurl = 'https://github.com/MaxBenChrist/human-activity-dataset/blob/master/UCI%20HAR%20Dataset.zip?raw=True' + zipurl = 'https://github.com/MaxBenChrist/human-activity-dataset/blob/master/UCI%20HAR%20Dataset.zip?raw=True' if os.path.exists(data_file_name_dataset) and os.path.exists(data_file_name_classes): _logger.warning("You have already downloaded the Human Activity Data Set.") diff --git a/tsfresh/feature_extraction/extraction.py b/tsfresh/feature_extraction/extraction.py index 8521d4b94..d66cf62ec 100644 --- a/tsfresh/feature_extraction/extraction.py +++ b/tsfresh/feature_extraction/extraction.py @@ -225,8 +225,10 @@ def generate_data_chunk_format(df, column_id, column_kind, column_value): if df[[column_id, column_kind]].nunique().prod() >= MAX_VALUES_GROUPBY: _logger.error( - "The time series container has {} different ids and {} different kind of time series, in total {} possible combinations. " - "Due to a limitation in pandas we can only process a maximum of {} id/kind combinations. Please reduce your time series container and restart " + "The time series container has {} different ids and {} different kind of time series, " + "in total {} possible combinations. " + "Due to a limitation in pandas we can only process a maximum of {} id/kind combinations. " + "Please reduce your time series container and restart " "the calculation".format( df[column_id].nunique(), df[column_kind].nunique(), df[[column_id, column_kind]].nunique().prod(), MAX_VALUES_GROUPBY) diff --git a/tsfresh/feature_extraction/feature_calculators.py b/tsfresh/feature_extraction/feature_calculators.py index 67c4881c0..8454ae272 100644 --- a/tsfresh/feature_extraction/feature_calculators.py +++ b/tsfresh/feature_extraction/feature_calculators.py @@ -205,7 +205,7 @@ def ratio_beyond_r_sigma(x, r): """ if not isinstance(x, (np.ndarray, pd.Series)): x = np.asarray(x) - return np.sum(np.abs(x - np.mean(x)) > r * np.std(x))/x.size + return np.sum(np.abs(x - np.mean(x)) > r * np.std(x)) / x.size @set_property("fctype", "simple") @@ -437,13 +437,13 @@ def augmented_dickey_fuller(x, param): res = adfuller(x) except LinAlgError: res = np.NaN, np.NaN, np.NaN - except ValueError: # occurs if sample size is too small + except ValueError: # occurs if sample size is too small res = np.NaN, np.NaN, np.NaN - except MissingDataError: # is thrown for e.g. inf or nan in the data + except MissingDataError: # is thrown for e.g. inf or nan in the data res = np.NaN, np.NaN, np.NaN return [('attr_"{}"'.format(config["attr"]), - res[0] if config["attr"] == "teststat" + res[0] if config["attr"] == "teststat" else res[1] if config["attr"] == "pvalue" else res[2] if config["attr"] == "usedlag" else np.NaN) for config in param] @@ -496,8 +496,8 @@ def cid_ce(x, normalize): x = np.asarray(x) if normalize: s = np.std(x) - if s!=0: - x = (x - np.mean(x))/s + if s != 0: + x = (x - np.mean(x)) / s else: return 0.0 @@ -975,7 +975,6 @@ def fft_aggregated(x, param): assert {config["aggtype"] for config in param} <= {"centroid", "variance", "skew", "kurtosis"}, \ 'Attribute must be "centroid", "variance", "skew", "kurtosis"' - def get_moment(y, moment): r""" Returns the (non centered) moment of the distribution y: @@ -1026,7 +1025,7 @@ def get_skew(y): return np.nan else: return ( - get_moment(y, 3) - 3*get_centroid(y)*variance - get_centroid(y)**3 + get_moment(y, 3) - 3 * get_centroid(y) * variance - get_centroid(y)**3 ) / get_variance(y)**(1.5) def get_kurtosis(y): @@ -1047,8 +1046,8 @@ def get_kurtosis(y): return np.nan else: return ( - get_moment(y, 4) - 4*get_centroid(y)*get_moment(y, 3) - + 6*get_moment(y, 2)*get_centroid(y)**2 - 3*get_centroid(y) + get_moment(y, 4) - 4 * get_centroid(y) * get_moment(y, 3) + + 6 * get_moment(y, 2) * get_centroid(y)**2 - 3 * get_centroid(y) ) / get_variance(y)**2 calculation = dict( @@ -1130,7 +1129,7 @@ def index_mass_quantile(x, param): # at least one value is not zero mass_centralized = np.cumsum(abs_x) / s return [("q_{}".format(config["q"]), - (np.argmax(mass_centralized >= config["q"])+1)/len(x)) for config in param] + (np.argmax(mass_centralized >= config["q"]) + 1) / len(x)) for config in param] @set_property("fctype", "simple") @@ -1294,7 +1293,7 @@ def ar_coefficient(x, param): try: calculated_ar_params[k] = calculated_AR.fit(maxlag=k, solver="mle").params except (LinAlgError, ValueError): - calculated_ar_params[k] = [np.NaN]*k + calculated_ar_params[k] = [np.NaN] * k mod = calculated_ar_params[k] @@ -1355,7 +1354,6 @@ def change_quantiles(x, ql, qh, isabs, f_agg): return aggregator(div[ind_inside_corridor]) - @set_property("fctype", "simple") def time_reversal_asymmetry_statistic(x, lag): """ @@ -1484,8 +1482,8 @@ def sample_entropy(x): """ x = np.array(x) - sample_length = 1 # number of sequential points of the time series - tolerance = 0.2 * np.std(x) # 0.2 is a common value for r - why? + sample_length = 1 # number of sequential points of the time series + tolerance = 0.2 * np.std(x) # 0.2 is a common value for r - why? n = len(x) prev = np.zeros(n) @@ -1545,12 +1543,12 @@ def autocorrelation(x, lag): """ # This is important: If a series is passed, the product below is calculated # based on the index, which corresponds to squaring the series. - if type(x) is pd.Series: + if isinstance(x, pd.Series): x = x.values if len(x) < lag: return np.nan # Slice the relevant subseries based on the lag - y1 = x[:(len(x)-lag)] + y1 = x[:(len(x) - lag)] y2 = x[lag:] # Subtract the mean of the whole series x x_mean = np.mean(x) @@ -1707,13 +1705,13 @@ def approximate_entropy(x, m, r): r *= np.std(x) if r < 0: raise ValueError("Parameter r must be positive.") - if N <= m+1: + if N <= m + 1: return 0 def _phi(m): - x_re = np.array([x[i:i+m] for i in range(N - m + 1)]) + x_re = np.array([x[i:i + m] for i in range(N - m + 1)]) C = np.sum(np.max(np.abs(x_re[:, np.newaxis] - x_re[np.newaxis, :]), - axis=2) <= r, axis=0) / (N-m+1) + axis=2) <= r, axis=0) / (N - m + 1) return np.sum(np.log(C)) / (N - m + 1.0) return np.abs(_phi(m) - _phi(m + 1)) @@ -1739,15 +1737,18 @@ def friedrich_coefficients(x, param): :param x: the time series to calculate the feature of :type x: numpy.ndarray - :param param: contains dictionaries {"m": x, "r": y, "coeff": z} with x being positive integer, the order of polynom to fit for estimating fixed points of - dynamics, y positive float, the number of quantils to use for averaging and finally z, a positive integer corresponding to the returned - coefficient + :param param: contains dictionaries {"m": x, "r": y, "coeff": z} with x being positive integer, + the order of polynom to fit for estimating fixed points of + dynamics, y positive float, the number of quantils to use for averaging and finally z, + a positive integer corresponding to the returned coefficient :type param: list :return: the different feature values :return type: pandas.Series """ - calculated = defaultdict(dict) # calculated is dictionary storing the calculated coefficients {m: {r: friedrich_coefficients}} - res = {} # res is a dictionary containg the results {"m_10__r_2__coeff_3": 15.43} + # calculated is dictionary storing the calculated coefficients {m: {r: friedrich_coefficients}} + calculated = defaultdict(dict) + # res is a dictionary containg the results {"m_10__r_2__coeff_3": 15.43} + res = {} for parameter_combination in param: m = parameter_combination['m'] @@ -1892,11 +1893,12 @@ def energy_ratio_by_chunks(x, param): if full_series_energy == 0: res_data.append(np.NaN) else: - res_data.append(np.sum(np.array_split(x, num_segments)[segment_focus] ** 2.0)/full_series_energy) + res_data.append(np.sum(np.array_split(x, num_segments)[segment_focus] ** 2.0) / full_series_energy) res_index.append("num_segments_{}__segment_focus_{}".format(num_segments, segment_focus)) - return list(zip(res_index, res_data)) # Materialize as list for Python 3 compatibility with name handling + # Materialize as list for Python 3 compatibility with name handling + return list(zip(res_index, res_data)) @set_property("fctype", "combiner") diff --git a/tsfresh/feature_extraction/settings.py b/tsfresh/feature_extraction/settings.py index 4fce56704..9b830e0c2 100644 --- a/tsfresh/feature_extraction/settings.py +++ b/tsfresh/feature_extraction/settings.py @@ -98,6 +98,7 @@ class ComprehensiveFCParameters(dict): to extract all features (which is the default nevertheless) or you change the ComprehensiveFCParameters object to other types (see below). """ + def __init__(self): name_to_param = {} @@ -124,9 +125,10 @@ def __init__(self): "spkt_welch_density": [{"coeff": coeff} for coeff in [2, 5, 8]], "ar_coefficient": [{"coeff": coeff, "k": k} for coeff in range(5) for k in [10]], "change_quantiles": [{"ql": ql, "qh": qh, "isabs": b, "f_agg": f} - for ql in [0., .2, .4, .6, .8] for qh in [.2, .4, .6, .8, 1.] - for b in [False, True] for f in ["mean", "var"] if ql < qh], - "fft_coefficient": [{"coeff": k, "attr": a} for a, k in product(["real", "imag", "abs", "angle"], range(100))], + for ql in [0., .2, .4, .6, .8] for qh in [.2, .4, .6, .8, 1.] + for b in [False, True] for f in ["mean", "var"] if ql < qh], + "fft_coefficient": [{"coeff": k, "attr": a} for a, k in + product(["real", "imag", "abs", "angle"], range(100))], "fft_aggregated": [{"aggtype": s} for s in ["centroid", "variance", "skew", "kurtosis"]], "value_count": [{"value": value} for value in [0, 1, -1]], "range_count": [{"min": -1, "max": 1}, {"min": 1e12, "max": 0}, {"min": 0, "max": 1e12}], @@ -141,10 +143,10 @@ def __init__(self): for f in ["max", "min", "mean", "var"]], "augmented_dickey_fuller": [{"attr": "teststat"}, {"attr": "pvalue"}, {"attr": "usedlag"}], "number_crossing_m": [{"m": 0}, {"m": -1}, {"m": 1}], - "energy_ratio_by_chunks": [{"num_segments" : 10, "segment_focus": i} for i in range(10)], + "energy_ratio_by_chunks": [{"num_segments": 10, "segment_focus": i} for i in range(10)], "ratio_beyond_r_sigma": [{"r": x} for x in [0.5, 1, 1.5, 2, 2.5, 3, 5, 6, 7, 10]], "linear_trend_timewise": [{"attr": "pvalue"}, {"attr": "rvalue"}, {"attr": "intercept"}, - {"attr": "slope"}, {"attr": "stderr"}] + {"attr": "slope"}, {"attr": "stderr"}] }) super().__init__(name_to_param) diff --git a/tsfresh/feature_selection/benjamini_hochberg_test.py b/tsfresh/feature_selection/benjamini_hochberg_test.py index 6b59a7be8..2e58d3448 100644 --- a/tsfresh/feature_selection/benjamini_hochberg_test.py +++ b/tsfresh/feature_selection/benjamini_hochberg_test.py @@ -67,4 +67,4 @@ def benjamini_hochberg_test(df_pvalues, hypotheses_independent, fdr_level): # Add the column denoting if null hypothesis has been rejected df_pvalues["relevant"] = [True] * k_max + [False] * (m - k_max) - return df_pvalues \ No newline at end of file + return df_pvalues diff --git a/tsfresh/feature_selection/relevance.py b/tsfresh/feature_selection/relevance.py index abcbd112b..add520c23 100644 --- a/tsfresh/feature_selection/relevance.py +++ b/tsfresh/feature_selection/relevance.py @@ -27,11 +27,11 @@ def calculate_relevance_table(X, y, ml_task='auto', n_jobs=defaults.N_PROCESSES, chunksize=defaults.CHUNKSIZE, - test_for_binary_target_binary_feature=defaults.TEST_FOR_BINARY_TARGET_BINARY_FEATURE, - test_for_binary_target_real_feature=defaults.TEST_FOR_BINARY_TARGET_REAL_FEATURE, - test_for_real_target_binary_feature=defaults.TEST_FOR_REAL_TARGET_BINARY_FEATURE, - test_for_real_target_real_feature=defaults.TEST_FOR_REAL_TARGET_REAL_FEATURE, - fdr_level=defaults.FDR_LEVEL, hypotheses_independent=defaults.HYPOTHESES_INDEPENDENT): + test_for_binary_target_binary_feature=defaults.TEST_FOR_BINARY_TARGET_BINARY_FEATURE, + test_for_binary_target_real_feature=defaults.TEST_FOR_BINARY_TARGET_REAL_FEATURE, + test_for_real_target_binary_feature=defaults.TEST_FOR_REAL_TARGET_BINARY_FEATURE, + test_for_real_target_real_feature=defaults.TEST_FOR_REAL_TARGET_REAL_FEATURE, + fdr_level=defaults.FDR_LEVEL, hypotheses_independent=defaults.HYPOTHESES_INDEPENDENT): """ Calculate the relevance table for the features contained in feature matrix `X` with respect to target vector `y`. The relevance table is calculated for the intended machine learning task `ml_task`. @@ -85,7 +85,8 @@ def calculate_relevance_table(X, y, ml_task='auto', n_jobs=defaults.N_PROCESSES, else regression. :type ml_task: str - :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature (currently unused) + :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature + (currently unused) :type test_for_binary_target_binary_feature: str :param test_for_binary_target_real_feature: Which test to be used for binary target, real feature diff --git a/tsfresh/feature_selection/selection.py b/tsfresh/feature_selection/selection.py index eeff8a923..3e6c88ff3 100644 --- a/tsfresh/feature_selection/selection.py +++ b/tsfresh/feature_selection/selection.py @@ -80,7 +80,8 @@ def select_features(X, y, test_for_binary_target_binary_feature=defaults.TEST_FO :param y: Target vector which is needed to test which features are relevant. Can be binary or real-valued. :type y: pandas.Series or numpy.ndarray - :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature (currently unused) + :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature + (currently unused) :type test_for_binary_target_binary_feature: str :param test_for_binary_target_real_feature: Which test to be used for binary target, real feature diff --git a/tsfresh/feature_selection/significance_tests.py b/tsfresh/feature_selection/significance_tests.py index f945ce8f3..ccf6ec2a9 100644 --- a/tsfresh/feature_selection/significance_tests.py +++ b/tsfresh/feature_selection/significance_tests.py @@ -12,10 +12,14 @@ The four functions are named -1. :func:`~tsfresh.feature_selection.significance_tests.target_binary_feature_binary_test`: Target and feature are both binary -2. :func:`~tsfresh.feature_selection.significance_tests.target_binary_feature_real_test`: Target is binary and feature real -3. :func:`~tsfresh.feature_selection.significance_tests.target_real_feature_binary_test`: Target is real and the feature is binary -4. :func:`~tsfresh.feature_selection.significance_tests.target_real_feature_real_test`: Target and feature are both real +1. :func:`~tsfresh.feature_selection.significance_tests.target_binary_feature_binary_test`: + Target and feature are both binary +2. :func:`~tsfresh.feature_selection.significance_tests.target_binary_feature_real_test`: + Target is binary and feature real +3. :func:`~tsfresh.feature_selection.significance_tests.target_real_feature_binary_test`: + Target is real and the feature is binary +4. :func:`~tsfresh.feature_selection.significance_tests.target_real_feature_real_test`: + Target and feature are both real References ---------- @@ -61,7 +65,6 @@ def target_binary_feature_binary_test(x, y): __check_for_binary_feature(x) __check_for_binary_target(y) - # Extract the unique values x0, x1 = np.unique(x.values) y0, y1 = np.unique(y.values) @@ -199,9 +202,9 @@ def __check_if_pandas_series(x, y): :raise: ``TypeError`` if one of the objects is not a pandas.Series. """ - if not type(x) == pd.Series: + if not isinstance(x, pd.Series): raise TypeError("x should be a pandas Series") - if not type(y) == pd.Series: + if not isinstance(y, pd.Series): raise TypeError("y should be a pandas Series") @@ -219,8 +222,9 @@ def __check_for_binary_target(y): :raises: ``ValueError`` if the values are not binary. """ if not set(y) == {0, 1}: - _logger.warning("[target_binary_feature_binary_test] The binary target should have values 1 and 0 (or True and False). " + - "Instead found" + str(set(y))) + _logger.warning("[target_binary_feature_binary_test] The binary target should have " + "values 1 and 0 (or True and False). " + "Instead found" + str(set(y))) if len(set(y)) > 2: raise ValueError("[target_binary_feature_binary_test] Target is not binary!") @@ -239,8 +243,9 @@ def __check_for_binary_feature(x): :raises: ``ValueError`` if the values are not binary. """ if not set(x) == {0, 1}: - _logger.warning("[target_binary_feature_binary_test] A binary feature should have only values 1 and 0 (incl. True and False). " + - "Instead found " + str(set(x)) + " in feature ''" + str(x.name) + "''.") + _logger.warning("[target_binary_feature_binary_test] A binary feature should have only " + "values 1 and 0 (incl. True and False). " + "Instead found " + str(set(x)) + " in feature ''" + str(x.name) + "''.") if len(set(x)) > 2: raise ValueError("[target_binary_feature_binary_test] Feature is not binary!") diff --git a/tsfresh/transformers/feature_augmenter.py b/tsfresh/transformers/feature_augmenter.py index 3cb172f53..f40313ab5 100644 --- a/tsfresh/transformers/feature_augmenter.py +++ b/tsfresh/transformers/feature_augmenter.py @@ -44,8 +44,9 @@ class FeatureAugmenter(BaseEstimator, TransformerMixin): >>> augmenter.set_timeseries_container(time_series) >>> df_with_time_series_features = augmenter.transform(df) - The settings for the feature calculation can be controlled with the settings object. If you pass ``None``, the default - settings are used. Please refer to :class:`~tsfresh.feature_extraction.settings.ComprehensiveFCParameters` for + The settings for the feature calculation can be controlled with the settings object. + If you pass ``None``, the default settings are used. + Please refer to :class:`~tsfresh.feature_extraction.settings.ComprehensiveFCParameters` for more information. This estimator does not select the relevant features, but calculates and adds all of them to the DataFrame. See the @@ -55,6 +56,7 @@ class FeatureAugmenter(BaseEstimator, TransformerMixin): For a description what the parameters column_id, column_sort, column_kind and column_value mean, please see :mod:`~tsfresh.feature_extraction.extraction`. """ + def __init__(self, default_fc_parameters=None, kind_to_fc_parameters=None, column_id=None, column_sort=None, column_kind=None, column_value=None, timeseries_container=None, @@ -75,8 +77,8 @@ def __init__(self, default_fc_parameters=None, :param kind_to_fc_parameters: mapping from kind names to objects of the same type as the ones for default_fc_parameters. If you put a kind as a key here, the fc_parameters - object (which is the value), will be used instead of the default_fc_parameters. This means that kinds, for - which kind_of_fc_parameters doe not have any entries, will be ignored by the feature selection. + object (which is the value), will be used instead of the default_fc_parameters. This means that kinds, + for which kind_of_fc_parameters doe not have any entries, will be ignored by the feature selection. :type kind_to_fc_parameters: dict :param column_id: The column with the id. See :mod:`~tsfresh.feature_extraction.extraction`. diff --git a/tsfresh/transformers/feature_selector.py b/tsfresh/transformers/feature_selector.py index 7e68473f5..ff9117911 100644 --- a/tsfresh/transformers/feature_selector.py +++ b/tsfresh/transformers/feature_selector.py @@ -56,6 +56,7 @@ class FeatureSelector(BaseEstimator, TransformerMixin): If you are interested in more information on the features, you can look into the member ``relevant_features`` after the fit. """ + def __init__(self, test_for_binary_target_binary_feature=defaults.TEST_FOR_BINARY_TARGET_BINARY_FEATURE, test_for_binary_target_real_feature=defaults.TEST_FOR_BINARY_TARGET_REAL_FEATURE, test_for_real_target_binary_feature=defaults.TEST_FOR_REAL_TARGET_BINARY_FEATURE, @@ -65,20 +66,22 @@ def __init__(self, test_for_binary_target_binary_feature=defaults.TEST_FOR_BINAR """ Create a new FeatureSelector instance. - :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature (currently unused) + :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature + (currently unused) :type test_for_binary_target_binary_feature: str :param test_for_binary_target_real_feature: Which test to be used for binary target, real feature :type test_for_binary_target_real_feature: str - :param test_for_real_target_binary_feature: Which test to be used for real target, binary feature (currently unused) + :param test_for_real_target_binary_feature: Which test to be used for real target, binary feature + (currently unused) :type test_for_real_target_binary_feature: str :param test_for_real_target_real_feature: Which test to be used for real target, real feature (currently unused) :type test_for_real_target_real_feature: str - :param fdr_level: The FDR level that should be respected, this is the theoretical expected percentage of irrelevant - features among all created features. + :param fdr_level: The FDR level that should be respected, this is the theoretical expected percentage + of irrelevant features among all created features. :type fdr_level: float :param hypotheses_independent: Can the significance of the features be assumed to be independent? @@ -139,10 +142,10 @@ def fit(self, X, y): y = pd.Series(y.copy()) relevance_table = calculate_relevance_table( - X, y, ml_task=self.ml_task, n_jobs=self.n_jobs, - chunksize=self.chunksize, fdr_level=self.fdr_level, - hypotheses_independent=self.hypotheses_independent, - test_for_binary_target_real_feature=self.test_for_binary_target_real_feature) + X, y, ml_task=self.ml_task, n_jobs=self.n_jobs, + chunksize=self.chunksize, fdr_level=self.fdr_level, + hypotheses_independent=self.hypotheses_independent, + test_for_binary_target_real_feature=self.test_for_binary_target_real_feature) self.relevant_features = relevance_table.loc[relevance_table.relevant].feature.tolist() self.feature_importances_ = 1.0 - relevance_table.p_value.values self.p_values = relevance_table.p_value.values diff --git a/tsfresh/transformers/per_column_imputer.py b/tsfresh/transformers/per_column_imputer.py index a984cb28a..7a201867c 100644 --- a/tsfresh/transformers/per_column_imputer.py +++ b/tsfresh/transformers/per_column_imputer.py @@ -7,103 +7,105 @@ from tsfresh.utilities.dataframe_functions import get_range_values_per_column, impute_dataframe_range import pandas as pd + class PerColumnImputer(BaseEstimator, TransformerMixin): + """ + Sklearn-compatible estimator, for column-wise imputing DataFrames by replacing all ``NaNs`` and ``infs`` + with with average/extreme values from the same columns. It is basically a wrapper around + :func:`~tsfresh.utilities.dataframe_functions.impute`. + + Each occurring ``inf`` or ``NaN`` in the DataFrame is replaced by + + * ``-inf`` -> ``min`` + * ``+inf`` -> ``max`` + * ``NaN`` -> ``median`` + + This estimator - as most of the sklearn estimators - works in a two step procedure. First, the ``.fit`` + function is called where for each column the min, max and median are computed. + Secondly, the ``.transform`` function is called which replaces the occurances of ``NaNs`` and ``infs`` using + the column-wise computed min, max and median values. + """ + + def __init__(self, col_to_NINF_repl_preset=None, col_to_PINF_repl_preset=None, col_to_NAN_repl_preset=None): + """ + Create a new PerColumnImputer instance, optionally with dictionaries containing replacements for + ``NaNs`` and ``infs``. + + :param col_to_NINF_repl: Dictionary mapping column names to ``-inf`` replacement values + :type col_to_NINF_repl: dict + :param col_to_PINF_repl: Dictionary mapping column names to ``+inf`` replacement values + :type col_to_PINF_repl: dict + :param col_to_NAN_repl: Dictionary mapping column names to ``NaN`` replacement values + :type col_to_NAN_repl: dict + """ + self._col_to_NINF_repl = None + self._col_to_PINF_repl = None + self._col_to_NAN_repl = None + self.col_to_NINF_repl_preset = col_to_NINF_repl_preset + self.col_to_PINF_repl_preset = col_to_PINF_repl_preset + self.col_to_NAN_repl_preset = col_to_NAN_repl_preset + + def fit(self, X, y=None): """ - Sklearn-compatible estimator, for column-wise imputing DataFrames by replacing all ``NaNs`` and ``infs`` - with with average/extreme values from the same columns. It is basically a wrapper around - :func:`~tsfresh.utilities.dataframe_functions.impute`. + Compute the min, max and median for all columns in the DataFrame. For more information, + please see the :func:`~tsfresh.utilities.dataframe_functions.get_range_values_per_column` function. - Each occurring ``inf`` or ``NaN`` in the DataFrame is replaced by + :param X: DataFrame to calculate min, max and median values on + :type X: pandas.DataFrame + :param y: Unneeded. + :type y: Any - * ``-inf`` -> ``min`` - * ``+inf`` -> ``max`` - * ``NaN`` -> ``median`` + :return: the estimator with the computed min, max and median values + :rtype: Imputer + """ + if not isinstance(X, pd.DataFrame): + X = pd.DataFrame(X) + + col_to_max, col_to_min, col_to_median = get_range_values_per_column(X) + + if self.col_to_NINF_repl_preset is not None: + if not set(X.columns) >= set(self.col_to_NINF_repl_preset.keys()): + raise ValueError("Preset dictionary 'col_to_NINF_repl_preset' contain more keys " + "than the column names in X") + col_to_min.update(self.col_to_NINF_repl_preset) + self._col_to_NINF_repl = col_to_min + + if self.col_to_PINF_repl_preset is not None: + if not set(X.columns) >= set(self.col_to_PINF_repl_preset.keys()): + raise ValueError("Preset dictionary 'col_to_PINF_repl_preset' contain more keys " + "than the column names in X") + col_to_max.update(self.col_to_PINF_repl_preset) + self._col_to_PINF_repl = col_to_max + + if self.col_to_NAN_repl_preset is not None: + if not set(X.columns) >= set(self.col_to_NAN_repl_preset.keys()): + raise ValueError("Preset dictionary 'col_to_NAN_repl_preset' contain more keys " + "than the column names in X") + col_to_median.update(self.col_to_NAN_repl_preset) + self._col_to_NAN_repl = col_to_median + + return self + + def transform(self, X): + """ + Column-wise replace all ``NaNs``, ``-inf`` and ``+inf`` in the DataFrame `X` with average/extreme + values from the provided dictionaries. - This estimator - as most of the sklearn estimators - works in a two step procedure. First, the ``.fit`` - function is called where for each column the min, max and median are computed. - Secondly, the ``.transform`` function is called which replaces the occurances of ``NaNs`` and ``infs`` using - the column-wise computed min, max and median values. + :param X: DataFrame to impute + :type X: pandas.DataFrame + + :return: imputed DataFrame + :rtype: pandas.DataFrame + :raise RuntimeError: if the replacement dictionaries are still of None type. + This can happen if the transformer was not fitted. """ - def __init__(self, col_to_NINF_repl_preset=None, col_to_PINF_repl_preset=None, col_to_NAN_repl_preset=None): - """ - Create a new PerColumnImputer instance, optionally with dictionaries containing replacements for - ``NaNs`` and ``infs``. - - :param col_to_NINF_repl: Dictionary mapping column names to ``-inf`` replacement values - :type col_to_NINF_repl: dict - :param col_to_PINF_repl: Dictionary mapping column names to ``+inf`` replacement values - :type col_to_PINF_repl: dict - :param col_to_NAN_repl: Dictionary mapping column names to ``NaN`` replacement values - :type col_to_NAN_repl: dict - """ - self._col_to_NINF_repl = None - self._col_to_PINF_repl = None - self._col_to_NAN_repl = None - self.col_to_NINF_repl_preset = col_to_NINF_repl_preset - self.col_to_PINF_repl_preset = col_to_PINF_repl_preset - self.col_to_NAN_repl_preset = col_to_NAN_repl_preset - - def fit(self, X, y=None): - """ - Compute the min, max and median for all columns in the DataFrame. For more information, - please see the :func:`~tsfresh.utilities.dataframe_functions.get_range_values_per_column` function. - - :param X: DataFrame to calculate min, max and median values on - :type X: pandas.DataFrame - :param y: Unneeded. - :type y: Any - - :return: the estimator with the computed min, max and median values - :rtype: Imputer - """ - if not isinstance(X, pd.DataFrame): - X = pd.DataFrame(X) - - col_to_max, col_to_min, col_to_median = get_range_values_per_column(X) - - if self.col_to_NINF_repl_preset is not None: - if not set(X.columns) >= set(self.col_to_NINF_repl_preset.keys()): - raise ValueError("Preset dictionary 'col_to_NINF_repl_preset' contain more keys " - "than the column names in X") - col_to_min.update(self.col_to_NINF_repl_preset) - self._col_to_NINF_repl = col_to_min - - if self.col_to_PINF_repl_preset is not None: - if not set(X.columns) >= set(self.col_to_PINF_repl_preset.keys()): - raise ValueError("Preset dictionary 'col_to_PINF_repl_preset' contain more keys " - "than the column names in X") - col_to_max.update(self.col_to_PINF_repl_preset) - self._col_to_PINF_repl = col_to_max - - if self.col_to_NAN_repl_preset is not None: - if not set(X.columns) >= set(self.col_to_NAN_repl_preset.keys()): - raise ValueError("Preset dictionary 'col_to_NAN_repl_preset' contain more keys " - "than the column names in X") - col_to_median.update(self.col_to_NAN_repl_preset) - self._col_to_NAN_repl = col_to_median - - return self - - def transform(self, X): - """ - Column-wise replace all ``NaNs``, ``-inf`` and ``+inf`` in the DataFrame `X` with average/extreme - values from the provided dictionaries. - - :param X: DataFrame to impute - :type X: pandas.DataFrame - - :return: imputed DataFrame - :rtype: pandas.DataFrame - :raise RuntimeError: if the replacement dictionaries are still of None type. - This can happen if the transformer was not fitted. - """ - - if not isinstance(X, pd.DataFrame): - X = pd.DataFrame(X) - - if self._col_to_NINF_repl is None or self._col_to_PINF_repl is None or self._col_to_NAN_repl is None: - raise NotFittedError("PerColumnImputer is not fitted") - - X = impute_dataframe_range(X, self._col_to_PINF_repl, self._col_to_NINF_repl, self._col_to_NAN_repl) - - return X + + if not isinstance(X, pd.DataFrame): + X = pd.DataFrame(X) + + if self._col_to_NINF_repl is None or self._col_to_PINF_repl is None or self._col_to_NAN_repl is None: + raise NotFittedError("PerColumnImputer is not fitted") + + X = impute_dataframe_range(X, self._col_to_PINF_repl, self._col_to_NINF_repl, self._col_to_NAN_repl) + + return X diff --git a/tsfresh/transformers/relevant_feature_augmenter.py b/tsfresh/transformers/relevant_feature_augmenter.py index 0f43f30d2..6c0540522 100644 --- a/tsfresh/transformers/relevant_feature_augmenter.py +++ b/tsfresh/transformers/relevant_feature_augmenter.py @@ -105,7 +105,6 @@ def __init__(self, fdr_level=defaults.FDR_LEVEL, hypotheses_independent=defaults.HYPOTHESES_INDEPENDENT, ml_task='auto'): - """ Create a new RelevantFeatureAugmenter instance. @@ -121,8 +120,8 @@ def __init__(self, :param kind_to_fc_parameters: mapping from kind names to objects of the same type as the ones for default_fc_parameters. If you put a kind as a key here, the fc_parameters - object (which is the value), will be used instead of the default_fc_parameters. This means that kinds, for - which kind_of_fc_parameters doe not have any entries, will be ignored by the feature selection. + object (which is the value), will be used instead of the default_fc_parameters. This means that kinds, + for which kind_of_fc_parameters doe not have any entries, will be ignored by the feature selection. :type kind_to_fc_parameters: dict :param column_id: The column with the id. See :mod:`~tsfresh.feature_extraction.extraction`. :type column_id: basestring @@ -162,20 +161,22 @@ def __init__(self, :param profiling_filename: Where to save the profiling results. :type profiling_filename: basestring - :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature (currently unused) + :param test_for_binary_target_binary_feature: Which test to be used for binary target, binary feature + (currently unused) :type test_for_binary_target_binary_feature: str :param test_for_binary_target_real_feature: Which test to be used for binary target, real feature :type test_for_binary_target_real_feature: str - :param test_for_real_target_binary_feature: Which test to be used for real target, binary feature (currently unused) + :param test_for_real_target_binary_feature: Which test to be used for real target, binary feature + (currently unused) :type test_for_real_target_binary_feature: str :param test_for_real_target_real_feature: Which test to be used for real target, real feature (currently unused) :type test_for_real_target_real_feature: str - :param fdr_level: The FDR level that should be respected, this is the theoretical expected percentage of irrelevant - features among all created features. + :param fdr_level: The FDR level that should be respected, this is the theoretical expected percentage + of irrelevant features among all created features. :type fdr_level: float :param hypotheses_independent: Can the significance of the features be assumed to be independent? @@ -306,7 +307,8 @@ def transform(self, X): After the fit step, it is known which features are relevant, Only extract those from the time series handed in with the function :func:`~set_timeseries_container`. - If filter_only_tsfresh_features is False, also delete the irrelevant, already present features in the data frame. + If filter_only_tsfresh_features is False, also delete the irrelevant, + already present features in the data frame. :param X: the data sample to add the relevant (and delete the irrelevant) features to. :type X: pandas.DataFrame or numpy.array diff --git a/tsfresh/utilities/__init__.py b/tsfresh/utilities/__init__.py index 7bd0e29b7..131410c58 100644 --- a/tsfresh/utilities/__init__.py +++ b/tsfresh/utilities/__init__.py @@ -1,4 +1,4 @@ """ This :mod:`~tsfresh.utilities` submodule contains several utility functions. Those should only be used internally inside tsfresh. -""" \ No newline at end of file +""" diff --git a/tsfresh/utilities/dataframe_functions.py b/tsfresh/utilities/dataframe_functions.py index 161711c15..d60e9f9c8 100644 --- a/tsfresh/utilities/dataframe_functions.py +++ b/tsfresh/utilities/dataframe_functions.py @@ -135,9 +135,9 @@ def impute_dataframe_range(df_impute, col_to_max, col_to_min, col_to_median): "to replace") # Make the replacement dataframes as large as the real one - col_to_max = pd.DataFrame([col_to_max]*len(df_impute), index=df_impute.index) - col_to_min = pd.DataFrame([col_to_min]*len(df_impute), index=df_impute.index) - col_to_median = pd.DataFrame([col_to_median]*len(df_impute), index=df_impute.index) + col_to_max = pd.DataFrame([col_to_max] * len(df_impute), index=df_impute.index) + col_to_min = pd.DataFrame([col_to_min] * len(df_impute), index=df_impute.index) + col_to_median = pd.DataFrame([col_to_median] * len(df_impute), index=df_impute.index) df_impute.where(df_impute.values != np.PINF, other=col_to_max, inplace=True) df_impute.where(df_impute.values != np.NINF, other=col_to_min, inplace=True) @@ -234,7 +234,8 @@ def get_ids(df_or_dict, column_id): # todo: add more testcases # todo: rewrite in a more straightforward way -def _normalize_input_to_internal_representation(timeseries_container, column_id, column_sort, column_kind, column_value): +def _normalize_input_to_internal_representation(timeseries_container, column_id, column_sort, + column_kind, column_value): """ Try to transform any given input to the internal representation of time series, which is a flat DataFrame (the first format from see :ref:`data-formats-label`). @@ -283,7 +284,7 @@ def _normalize_input_to_internal_representation(timeseries_container, column_id, try: timeseries_container = pd.concat(timeseries_container.values(), sort=True) - except TypeError: # pandas < 0.23.0 + except TypeError: # pandas < 0.23.0 timeseries_container = pd.concat(timeseries_container.values()) gc.collect() @@ -433,14 +434,14 @@ def roll_time_series(df_or_dict, column_id, column_sort, column_kind, rolling_di if column_id is not None: if column_id not in df: - raise AttributeError("The given column for the id is not present in the data.") + raise AttributeError("The given column for the id is not present in the data.") else: raise ValueError("You have to set the column_id which contains the ids of the different time series") if column_kind is not None: grouper = [column_kind, column_id] else: - grouper = [column_id,] + grouper = [column_id, ] if column_sort is not None and df[column_sort].dtype != np.object: @@ -555,4 +556,3 @@ def mask_first(x): df_shift = df_shift[mask] return df_shift, df["value"][1:] - diff --git a/tsfresh/utilities/distribution.py b/tsfresh/utilities/distribution.py index 8a43f5dfb..e13ac8b97 100644 --- a/tsfresh/utilities/distribution.py +++ b/tsfresh/utilities/distribution.py @@ -2,7 +2,7 @@ # This file as well as the whole tsfresh package are licenced under the MIT licence (see the LICENCE.txt) # Maximilian Christ (maximilianchrist.com), Blue Yonder Gmbh, 2017 """ -This module contains the Distributor class, such objects are used to distribute the calculation of features. +This module contains the Distributor class, such objects are used to distribute the calculation of features. Essentially, a Distributor organizes the application of feature calculators to data chunks. Design of this module by Nils Braun @@ -21,7 +21,7 @@ def _function_with_partly_reduce(chunk_list, map_function, kwargs): Small helper function to call a function (map_function) on a list of data chunks (chunk_list) and convert the results into a flattened list. - + This function is used to send chunks of data with a size larger than 1 to the workers in parallel and process these on the worker. @@ -29,7 +29,7 @@ def _function_with_partly_reduce(chunk_list, map_function, kwargs): :type chunk_list: list :param map_function: A function, which is called on each chunk in the list separately. :type map_function: callable - + :return: A list of the results of the function evaluated on each chunk and flattened. :rtype: list """ @@ -42,14 +42,14 @@ def _function_with_partly_reduce(chunk_list, map_function, kwargs): class DistributorBaseClass: """ The distributor abstract base class. - - The main purpose of the instances of the DistributorBaseClass subclasses is to evaluate a function (called map_function) - on a list of data items (called data). - + + The main purpose of the instances of the DistributorBaseClass subclasses is to evaluate a function + (called map_function) on a list of data items (called data). + This is done on chunks of the data, meaning, that the DistributorBaseClass classes will chunk the data into chunks, distribute the data and apply the feature calculator functions from :mod:`tsfresh.feature_extraction.feature_calculators` on the time series. - + Dependent on the implementation of the distribute function, this is done in parallel or using a cluster of nodes. """ @@ -84,9 +84,9 @@ def __init__(self): def calculate_best_chunk_size(self, data_length): """ - Calculates the best chunk size for a list of length data_length. The current implemented formula is more or + Calculates the best chunk size for a list of length data_length. The current implemented formula is more or less an empirical result for multiprocessing case on one machine. - + :param data_length: A length which defines how many calculations there need to be. :type data_length: int :return: the calculated chunk size @@ -102,12 +102,12 @@ def calculate_best_chunk_size(self, data_length): def map_reduce(self, map_function, data, function_kwargs=None, chunk_size=None, data_length=None): """ This method contains the core functionality of the DistributorBaseClass class. - + It maps the map_function to each element of the data and reduces the results to return a flattened list. - How the jobs are calculated, is determined by the classes - :func:`tsfresh.utilities.distribution.DistributorBaseClass.distribute` method, which can distribute the jobs in multiple - threads, across multiple processing units etc. + How the jobs are calculated, is determined by the classes + :func:`tsfresh.utilities.distribution.DistributorBaseClass.distribute` method, + which can distribute the jobs in multiple threads, across multiple processing units etc. To not transport each element of the data individually, the data is split into chunks, according to the chunk size (or an empirical guess if none is given). By this, worker processes not tiny but adequate sized parts of @@ -124,7 +124,7 @@ def map_reduce(self, map_function, data, function_kwargs=None, chunk_size=None, :param data_length: If the data is a generator, you have to set the length here. If it is none, the length is deduced from the len of the data. :type data_length: int - + :return: the calculated results :rtype: list """ @@ -162,8 +162,8 @@ def distribute(self, func, partitioned_chunks, kwargs): :type partitioned_chunks: iterable :param kwargs: parameters for the map function :type kwargs: dict of string to parameter - - :return: The result of the calculation as a list - each item should be the result of the application of func + + :return: The result of the calculation as a list - each item should be the result of the application of func to a single element. """ raise NotImplementedError diff --git a/tsfresh/utilities/profiling.py b/tsfresh/utilities/profiling.py index a3e003496..072ae2833 100644 --- a/tsfresh/utilities/profiling.py +++ b/tsfresh/utilities/profiling.py @@ -5,7 +5,9 @@ Contains methods to start and stop the profiler that checks the runtime of the different feature calculators """ -import cProfile, pstats, io +import cProfile +import pstats +import io import logging diff --git a/tsfresh/utilities/string_manipulation.py b/tsfresh/utilities/string_manipulation.py index bd92d933c..170307ad0 100644 --- a/tsfresh/utilities/string_manipulation.py +++ b/tsfresh/utilities/string_manipulation.py @@ -68,4 +68,3 @@ def add_parenthesis_if_string_value(x): return str(x) return "__".join(str(key) + "_" + add_parenthesis_if_string_value(param[key]) for key in sorted(param.keys())) -