From 0cc047096188f607d1dc83e8b69ace391da851af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Wed, 11 Oct 2023 21:33:28 +0200 Subject: [PATCH 01/27] [DOC] Improved PR template for new contributors (#5381) This PR makes the PR post template hopefully more informative and welcoming to new contributors: * more and corrected links to contributing guide, soft dependencies guide, discord * restructured PR review guide to make it less apologetic and more welcoming as well as instructive --- .github/PULL_REQUEST_TEMPLATE.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 47fb733727d..15dee5a8a8a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,7 @@ #### Reference Issues/PRs @@ -9,7 +10,8 @@ Example: Fixes #1234. See also #3456. Please use keywords (e.g., Fixes) to create link to the issues or pull requests you resolved, so that they will automatically be closed when your pull request -is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests +is merged. See https://github.com/blog/1506-closing-issues-via-pull-requests. +If no issue exists, you can open one here: https://github.com/sktime/sktime/issues --> @@ -21,7 +23,10 @@ A clear and concise description of what you have implemented. #### Does your contribution introduce a new dependency? If yes, which one? #### What should a reviewer concentrate their feedback on? @@ -35,7 +40,8 @@ If your contribution does add a new hard dependency, we may suggest to initially #### Any other comments? #### PR checklist @@ -57,7 +63,6 @@ Please go through the checklist below. Please feel free to remove points if they - [ ] If the estimator relies on a soft dependency, I've set the `python_dependencies` tag and ensured dependency isolation, see the [estimator dependencies guide](https://www.sktime.net/en/latest/developer_guide/dependencies.html#adding-a-soft-dependency). - From a3ef13ef85be6af2347c194e03b49ca140e47538 Mon Sep 17 00:00:00 2001 From: Benedikt Heidrich Date: Wed, 11 Oct 2023 22:13:32 +0200 Subject: [PATCH 02/27] [ENH] `ForecastX` enable fitting `forecaster_y` on forecasted `X` (#5334) Enable `ForecastX` to train `forecaster_y` on prediction of `forecaster_X` to enable `forecaster_y` to consider the uncertainty of the forecasted `X`. #### Reference Issues/PRs fixes partially #5225 #### What does this implement/fix? Explain your changes. This PR adds a parameter to `ForecastX` that enables to control if ForecastX should fit the internal `forecaster_y` on the ground truth data of `X` or of the forecasted `X`. Fitting on the forecasted `X` enables `forecaster_y` to account the uncertainty associated with forecast. #### Did you add any tests for the change? Yes. --- sktime/forecasting/compose/_pipeline.py | 77 +++++++++++++------ .../compose/tests/test_pipeline.py | 44 +++++++++++ 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/sktime/forecasting/compose/_pipeline.py b/sktime/forecasting/compose/_pipeline.py index fa77b432188..9edd3ba9957 100644 --- a/sktime/forecasting/compose/_pipeline.py +++ b/sktime/forecasting/compose/_pipeline.py @@ -1,7 +1,6 @@ # copyright: sktime developers, BSD-3-Clause License (see LICENSE file) """Implements pipelines for forecasting.""" -__author__ = ["mloning", "aiwalter"] __all__ = ["TransformedTargetForecaster", "ForecastingPipeline", "ForecastX"] import pandas as pd @@ -1166,49 +1165,57 @@ def _predict_interval(self, fh, X, coverage): class ForecastX(BaseForecaster): """Forecaster that forecasts exogeneous data for use in an endogeneous forecast. - In `predict`, this forecaster carries out a `predict` step on exogeneous `X`. - Then, a forecast is made for `y`, using exogeneous data plus its forecasts as `X`. - If `columns` argument is provided, will carry `predict` out only for the columns - in `columns`, and will use other columns in `X` unchanged. + In ``predict``, this forecaster carries out a ``predict`` step on exogeneous ``X``. + Then, a forecast is made for ``y``, + using exogeneous data plus its forecasts as ``X``. + If ``columns`` argument is provided, will carry ``predict`` out only for the columns + in ``columns``, and will use other columns in ``X`` unchanged. - The two forecasters and forecasting horizons (for forecasting `y` resp `X`) + The two forecasters and forecasting horizons (for forecasting ``y`` resp ``X``) can be selected independently, but default to the same. The typical use case is extending exogeneous data available only up until the cutoff into the future, for use by an exogeneous forecaster that requires such future data. - If no X is passed in `fit`, behaves like `forecaster_y`. + If no X is passed in ``fit``, behaves like ``forecaster_y``. In such a case (no exogeneous data), there is no benefit in using this compositor. Parameters ---------- forecaster_y : BaseForecaster - sktime forecaster to use for endogeneous data `y` + sktime forecaster to use for endogeneous data ``y`` forecaster_X : BaseForecaster - sktime forecaster to use for exogeneous data `X` + sktime forecaster to use for exogeneous data ``X`` fh_X : None, ForecastingHorizon, or valid input to construct ForecastingHorizon - optional, default = None = same as used for `y` in any instance. - valid inputs to construct ForecastingHorizon are: + optional, default = None = same as used for ``y`` in any instance. + valid inputs to construct ``ForecastingHorizon`` are: int, list of int, 1D np.ndarray, pandas.Index (see ForecastingHorizon) behaviour : str, one of "update" or "refit", optional, default = "update" - if "update", forecaster_X is fit to the data batch seen in `fit`, - and updated with any `X` seen in calls of `update`. - Forecast added to `X` in `predict` is obtained from this state. - if "refit", then forecaster_X is fit to `X` in `predict` only, - Forecast added to `X` in `predict` is obtained from this state. + if "update", ``forecaster_X`` is fit to the data batch seen in ``fit``, + and updated with any ``X`` seen in calls of ``update``. + Forecast added to ``X`` in ``predict`` is obtained from this state. + if "refit", then ``forecaster_X`` is fit to ``X`` in ``predict`` only, + Forecast added to ``X`` in ``predict`` is obtained from this state. columns : None, or pandas compatible index iterator (e.g., list of str), optional - default = None = all columns in X are used for forecast columns to which - `forecaster_X` is applied. if not ``None``, this must be a non-empty list of - valid column names (``[]`` and ``None`` do not imply the same) + default = None = all columns in ``X`` are used for forecast columns to which + ``forecaster_X`` is applied. + If not ``None``, must be a non-empty list of valid column names. + Note that ``[]`` and ``None`` do not imply the same. + fit_behaviour : str, one of "use_actual", "use_forecast", optional, + default = "use_actual" + if "use_actual", then ``forecaster_y`` uses the actual ``X`` as + exogenous features in `fit` + if "use_forecast", then ``forecaster_y`` uses the ``X`` predicted by + ``forecaster_X`` as exogenous features in ``fit`` Attributes ---------- forecaster_X_ : BaseForecaster - clone of forecaster_X, state updates with `fit` and `update` - created only if behaviour="update" and `X` passed is not None + clone of ``forecaster_X``, state updates with ``fit`` and ``update`` + created only if ``behaviour="update"`` and ``X`` passed is not None and ``forecaster_y`` has ``ignores-exogeneous-X`` tag as ``False`` forecaster_y_ : BaseForecaster - clone of forecaster_y, state updates with `fit` and `update` + clone of ``forecaster_y``, state updates with ``fit`` and ``update`` Examples -------- @@ -1255,8 +1262,19 @@ class ForecastX(BaseForecaster): } def __init__( - self, forecaster_y, forecaster_X, fh_X=None, behaviour="update", columns=None + self, + forecaster_y, + forecaster_X, + fh_X=None, + behaviour="update", + columns=None, + fit_behaviour="use_actual", ): + if fit_behaviour not in ["use_actual", "use_forecast"]: + raise ValueError( + 'fit_behaviour must be one of "use_actual", "use_forecast"' + ) + self.fit_behaviour = fit_behaviour if behaviour not in ["update", "refit"]: raise ValueError('behaviour must be one of "update", "refit"') @@ -1318,7 +1336,16 @@ def _fit(self, y, X, fh): self.forecaster_X_.fit(y=self._get_Xcols(X), fh=fh_X) self.forecaster_y_ = self.forecaster_y.clone() - self.forecaster_y_.fit(y=y, X=X, fh=fh) + if self.fit_behaviour == "use_actual": + self.forecaster_y_.fit(y=y, X=X, fh=fh) + elif self.fit_behaviour == "use_forecast": + if not self.forecaster_X_.get_tag("capability:insample"): + raise ValueError( + "forecaster_X does not have `capability:insample`. " + "Thus, it is not valid with `fit_behaviour=use_forecast`." + ) + x_insample = self.forecaster_X_.predict(fh=X.index, X=X) + self.forecaster_y_.fit(y=y, X=x_insample, fh=fh) return self @@ -1359,7 +1386,7 @@ def _get_forecaster_X_prediction(self, X=None, fh=None, method="predict"): forecaster = self.forecaster_X_c.clone() forecaster.fit(y=self._get_Xcols(self._X), fh=fh) - X_pred = getattr(forecaster, method)() + X_pred = getattr(forecaster, method)(fh=fh) if X is not None: X_pred = X_pred.combine_first(X) diff --git a/sktime/forecasting/compose/tests/test_pipeline.py b/sktime/forecasting/compose/tests/test_pipeline.py index d0a62b98f69..bb880c287e8 100644 --- a/sktime/forecasting/compose/tests/test_pipeline.py +++ b/sktime/forecasting/compose/tests/test_pipeline.py @@ -14,6 +14,7 @@ from sktime.datasets import load_airline, load_longley from sktime.datatypes import get_examples from sktime.datatypes._utilities import get_window +from sktime.forecasting.arima import ARIMA from sktime.forecasting.compose import ( ForecastingPipeline, TransformedTargetForecaster, @@ -478,6 +479,49 @@ def test_forecastx_logic(): assert np.allclose(y_pred, y_pred_manual) +@pytest.mark.skipif( + not _check_estimator_deps(ARIMA, severity="none"), + reason="skip test if required soft dependency is not available", +) +def test_forecastx_fit_behavior(): + from sktime.forecasting.compose import ForecastX + from sktime.forecasting.model_selection import temporal_train_test_split + + y, X = load_longley() + y_train, y_test, X_train, X_test = temporal_train_test_split(y, X) + + pipe = ForecastX( + forecaster_X=NaiveForecaster(), + forecaster_y=ARIMA(), + ) + pipe = pipe.fit(y_train, X=X_train, fh=y_test.index) + y_pred_forecast_X_use_gt = pipe.predict(fh=y_test.index) + + naive = NaiveForecaster() + naive.fit(X_train) + x_pred_train = naive.predict(fh=X_train.index) + arima = ARIMA() + arima.fit(y_train, X_train) + + y_pred = arima.predict(fh=y_test.index, X=naive.predict(fh=y_test.index)) + + pd.testing.assert_series_equal(y_pred_forecast_X_use_gt, y_pred) + + pipe = ForecastX( + forecaster_X=NaiveForecaster(), + forecaster_y=ARIMA(), + fit_behaviour="use_forecast", + ) + pipe = pipe.fit(y_train, X=X_train, fh=y_test.index) + y_pred_forecast_X_use_forecast = pipe.predict(fh=y_test.index) + + arima = ARIMA() + arima.fit(y_train, x_pred_train) + y_pred = arima.predict(fh=y_test.index, X=naive.predict(fh=y_test.index)) + + pd.testing.assert_series_equal(y_pred_forecast_X_use_forecast, y_pred) + + def test_forecastx_attrib_broadcast(): """Test ForecastX broadcasting and forecaster attributes.""" from sktime.forecasting.compose import ForecastX From c088c6f6c6986a171ed9312c70ef4595d8110ede Mon Sep 17 00:00:00 2001 From: Vasudeva Kilaru <70791259+Vasudeva-bit@users.noreply.github.com> Date: Thu, 12 Oct 2023 02:38:38 +0530 Subject: [PATCH 03/27] [BUG] Expected fix in `_check_predict_proba` #5349 (#5384) Fixes #5349 Changes a if (check) condition in `_check_predict_proba` function of `sktime/forecasting/tests/test_all_forecasters.py` to preserve column name in univariate forecaster predictions. Upon fixing this issue, `ARCH` doesn't require function `_predict_proba` as `BaseForecaster`'s `_predict_proba` is sufficient. Hence, removed that function. --- sktime/forecasting/arch/_uarch.py | 22 ------------------- .../forecasting/tests/test_all_forecasters.py | 5 ++++- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/sktime/forecasting/arch/_uarch.py b/sktime/forecasting/arch/_uarch.py index 7b400536573..c637902150e 100644 --- a/sktime/forecasting/arch/_uarch.py +++ b/sktime/forecasting/arch/_uarch.py @@ -464,28 +464,6 @@ def _predict_var(self, fh=None, X=None, cov=False): return pred_var - def _predict_proba(self, fh, X, marginal=True): - """Compute/return fully probabilistic forecasts. - - Parameters - ---------- - fh : int, list, np.array or ForecastingHorizon (not optional) - The forecasting horizon encoding the time stamps to forecast at. - if has not been passed in fit, must be passed, not optional - X : sktime time series object, optional (default=None) - Exogeneous time series for the forecast - marginal : bool, optional (default=True) - whether returned distribution is marginal by time index - - Returns - ------- - pred_dist : sktime BaseDistribution - predictive distribution - """ - pred_dist = super()._predict_proba(fh=fh, X=X, marginal=marginal) - pred_dist.columns = pd.Index([0]) - return pred_dist - def _get_fitted_params(self): """Get fitted parameters. diff --git a/sktime/forecasting/tests/test_all_forecasters.py b/sktime/forecasting/tests/test_all_forecasters.py index 8141ce25675..99c0db96996 100644 --- a/sktime/forecasting/tests/test_all_forecasters.py +++ b/sktime/forecasting/tests/test_all_forecasters.py @@ -528,7 +528,10 @@ def _check_predict_proba(self, pred_dist, y_train, fh_int): # check columns if isinstance(y_train, pd.Series): - assert (pred_cols == pd.Index([0])).all() + if y_train.name is not None: + assert (pred_cols == y_train.name).all() + else: + assert (pred_cols == pd.Index([0])).all() else: assert (pred_cols == y_train.columns).all() From dc0134814720d8383cb2b4ecd0bb5199da280a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Thu, 12 Oct 2023 12:50:16 +0200 Subject: [PATCH 04/27] [DOC] dynamic docstring for `set_config` (#5306) Towards https://github.com/sktime/sktime/issues/5302 - this PR adds a primitive dynamic docstring override to `set_config`. It works, but I'm not sure why... Adds the following configs to the dynamic documentation of `set_config`: * `backend:parallel` to transformers, forecasters * `backend:parallel:params` to transformers, forecasters * `input_conversion` and `output_conversion` for transformers * `warnings` for all estimators --- sktime/base/_base.py | 64 ++++++++++++++++++++++++++++++++ sktime/forecasting/base/_base.py | 28 ++++++++++++++ sktime/transformations/base.py | 43 +++++++++++++++++++++ 3 files changed, 135 insertions(+) diff --git a/sktime/base/_base.py b/sktime/base/_base.py index d5523fccf5f..381c5a5a7a9 100644 --- a/sktime/base/_base.py +++ b/sktime/base/_base.py @@ -73,6 +73,33 @@ class BaseObject(_BaseObject): Extends skbase BaseObject with additional features. """ + _config_doc = { + "display": """ + display : str, "diagram" (default), or "text" + how jupyter kernels display instances of self + + * "diagram" = html box diagram representation + * "text" = string printout + """, + "print_changed_only": """ + print_changed_only : bool, default=True + whether printing of self lists only self-parameters that differ + from defaults (False), or all parameter names and values (False) + does not nest, i.e., only affects self and not component estimators + """, + "warnings": """ + warnings : str, "on" (default), or "off" + whether to raise warnings, affects warnings from sktime only + + * "on" = will raise warnings from sktime + * "off" = will not raise warnings from sktime + """, + } + + def __init__(self): + super().__init__() + self.__class__.set_config.__doc__ = self._get_set_config_doc() + def __eq__(self, other): """Equality dunder. Checks equal class and parameters. @@ -91,6 +118,43 @@ def __eq__(self, other): return deep_equals(self_params, other_params) + @classmethod + def _get_set_config_doc(cls): + """Create docstring for set_config from self._config_doc. + + Returns + ------- + collected_config_docs : dict + Dictionary of doc name: docstring part. + Collected from _config_doc class attribute via nested inheritance. + """ + cfgs_dict = cls._get_class_flags(flag_attr_name="_config_doc") + + doc_start = """Set config flags to given values. + + Parameters + ---------- + config_dict : dict + Dictionary of config name : config value pairs. + Valid configs, values, and their meaning is listed below: + """ + + doc_end = """ + Returns + ------- + self : reference to self. + + Notes + ----- + Changes object state, copies configs in config_dict to self._config_dynamic. + """ + + doc = doc_start + for _, cfg_doc in cfgs_dict.items(): + doc += cfg_doc + doc += doc_end + return doc + def save(self, path=None): """Save serialized self to bytes-like object or to (.zip) file. diff --git a/sktime/forecasting/base/_base.py b/sktime/forecasting/base/_base.py index 0e553348bb4..7f0026ff7f1 100644 --- a/sktime/forecasting/base/_base.py +++ b/sktime/forecasting/base/_base.py @@ -106,6 +106,31 @@ class BaseForecaster(BaseEstimator): # None: no parallelization # "loky", "multiprocessing" and "threading": uses `joblib` Parallel loops # "dask": uses `dask`, requires `dask` package in environment + "backend:parallel:params": None, # params for parallelization backend + } + + _config_doc = { + "backend:parallel": """ + backend:parallel : str, optional, default="None" + backend to use for parallelization when broadcasting/vectorizing, one of + + - "None": executes loop sequentally, simple list comprehension + - "loky", "multiprocessing" and "threading": uses ``joblib`` ``Parallel`` + - "dask": uses ``dask``, requires ``dask`` package in environment + """, + "backend:parallel:params": """ + backend:parallel:params : dict, optional, default={} (no parameters passed) + additional parameters passed to the parallelization backend as config. + Valid keys depend on the value of ``backend:parallel``: + + - "None": no additional parameters, ``backend_params`` is ignored + - "loky", "multiprocessing" and "threading": + any valid keys for ``joblib.Parallel`` can be passed here, + e.g., ``n_jobs``, with the exception of ``backend`` which is directly + controlled by ``backend:parallel`` + - "dask": any valid keys for ``dask.compute`` + can be passed, e.g., ``scheduler`` + """, } def __init__(self): @@ -1747,6 +1772,7 @@ def _vectorize(self, methodname, **kwargs): rowname_default="forecasters", colname_default="forecasters", backend=self.get_config()["backend:parallel"], + backend_params=self.get_config()["backend:parallel:params"], ) else: forecasters_ = self.forecasters_ @@ -1755,6 +1781,7 @@ def _vectorize(self, methodname, **kwargs): forecasters_, method=methodname, backend=self.get_config()["backend:parallel"], + backend_params=self.get_config()["backend:parallel:params"], **kwargs, ) return self @@ -1770,6 +1797,7 @@ def _vectorize(self, methodname, **kwargs): method=methodname, return_type="list", backend=self.get_config()["backend:parallel"], + backend_params=self.get_config()["backend:parallel:params"], **kwargs, ) diff --git a/sktime/transformations/base.py b/sktime/transformations/base.py index f7153b459fc..b8f47eeb764 100644 --- a/sktime/transformations/base.py +++ b/sktime/transformations/base.py @@ -156,6 +156,46 @@ class BaseTransformer(BaseEstimator): # None: no parallelization # "loky", "multiprocessing" and "threading": uses `joblib` Parallel loops # "dask": uses `dask`, requires `dask` package in environment + "backend:parallel:params": None, # params for parallelization backend + } + + _config_doc = { + "backend:parallel": """ + backend:parallel : str, optional, default="None" + backend to use for parallelization when broadcasting/vectorizing, one of + + - "None": executes loop sequentally, simple list comprehension + - "loky", "multiprocessing" and "threading": uses ``joblib`` ``Parallel`` + - "dask": uses ``dask``, requires ``dask`` package in environment + """, + "backend:parallel:params": """ + backend:parallel:params : dict, optional, default={} (no parameters passed) + additional parameters passed to the parallelization backend as config. + Valid keys depend on the value of ``backend:parallel``: + + - "None": no additional parameters, ``backend_params`` is ignored + - "loky", "multiprocessing" and "threading": + any valid keys for ``joblib.Parallel`` can be passed here, + e.g., ``n_jobs``, with the exception of ``backend`` which is directly + controlled by ``backend:parallel`` + - "dask": any valid keys for ``dask.compute`` + can be passed, e.g., ``scheduler`` + """, + "input_conversion": """ + input_conversion : str, one of "on", "off", valid mtype string + controls input checks and conversions, + for _fit, _transform, _inverse_transform, _update + "on" - input check and conversion is carried out + "off" - input check and conversion not done before passing to inner methods + valid mtype string - input is assumed to specified mtype + """, + "output_conversion": """ + output_conversion : str, one of "on", "off", valid mtype string + controls output conversion for _transform, _inverse_transform + "on" - if input_conversion is "on", output conversion is carried out + "off" - output of _transform, _inverse_transform is directly returned + valid mtype string - output is converted to specified mtype + """, } # allowed mtypes for transformers - Series and Panel @@ -1251,6 +1291,7 @@ def _vectorize(self, methodname, **kwargs): transformers_, method=methodname, backend=self.get_config()["backend:parallel"], + backend_params=self.get_config()["backend:parallel:params"], **kwargs, ) return self @@ -1284,6 +1325,7 @@ def _vectorize(self, methodname, **kwargs): transformers_, method="fit", backend=self.get_config()["backend:parallel"], + backend_params=self.get_config()["backend:parallel:params"], **kwargs, ) @@ -1293,6 +1335,7 @@ def _vectorize(self, methodname, **kwargs): method=methodname, return_type="list", backend=self.get_config()["backend:parallel"], + backend_params=self.get_config()["backend:parallel:params"], **kwargs, ) Xt = X.reconstruct(Xts, overwrite_index=False) From b57aaf49d8246a12f2b8cb9c38f0d7cf3bb20d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Thu, 12 Oct 2023 12:53:00 +0200 Subject: [PATCH 05/27] [MNT] warnings config (#4536) This adds a config `"warnings"` to estimators and objects which allows to turn warnings off. It also replaces all calls from estimators to `warn` with an `sktime` version that is a conditional based on an object that is queried for a tag. If the tag is not present or the object does not have a tag system, the `sktime` `warn` behaves like `warnings.warn`. (does not affect warnings deprecated in 0.24.0, or outside classes) Documentation is in https://github.com/sktime/sktime/pull/5306 --- CONTRIBUTORS.md | 35 +++++++++-------- sktime/base/_base.py | 2 + sktime/classification/base.py | 7 ++-- .../classification/dictionary_based/_muse.py | 5 ++- .../classification/dictionary_based/_tde.py | 12 +++--- sktime/classification/ensemble/_ctsf.py | 8 ++-- .../feature_based/_tsfresh_classifier.py | 5 +-- sktime/clustering/dbscan.py | 6 +-- sktime/forecasting/base/_base.py | 19 ++++++--- .../forecasting/base/adapters/_statsmodels.py | 5 ++- .../compose/_hierarchy_ensemble.py | 9 +++-- sktime/forecasting/compose/_pipeline.py | 9 +++++ sktime/forecasting/compose/_reduce.py | 14 +++---- sktime/forecasting/compose/_stack.py | 5 +-- sktime/forecasting/conformal.py | 12 +++--- sktime/forecasting/naive.py | 15 ++++--- sktime/forecasting/reconcile.py | 6 +-- sktime/forecasting/squaring_residuals.py | 9 +++-- sktime/forecasting/theta.py | 8 ++-- sktime/param_est/base.py | 11 ++++-- .../forecasting/_classes.py | 8 ++-- sktime/regression/base.py | 4 +- sktime/regression/compose/_ensemble.py | 7 ++-- .../base/estimators/_ensemble.py | 7 +++- sktime/transformations/compose/_grouped.py | 4 +- sktime/transformations/compose/_invert.py | 9 +++-- .../transformations/hierarchical/aggregate.py | 15 ++++--- .../transformations/hierarchical/reconcile.py | 9 +++-- sktime/transformations/series/exponent.py | 6 +-- .../transformations/series/kalman_filter.py | 6 +-- sktime/transformations/series/lag.py | 9 +++-- sktime/transformations/series/scaledlogit.py | 4 +- .../series/tests/test_scaledlogit.py | 3 +- sktime/utils/warnings.py | 39 +++++++++++++++++++ 34 files changed, 214 insertions(+), 118 deletions(-) create mode 100644 sktime/utils/warnings.py diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 807e76a947c..333f5e18ca4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,7 +2,7 @@ Contributors ============ -[![All Contributors](https://img.shields.io/badge/all_contributors-234-orange.svg)](#contributors) +[![All Contributors](https://img.shields.io/badge/all_contributors-235-orange.svg)](#contributors) This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! @@ -131,11 +131,12 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Jason Lines
Jason Lines

πŸ’» πŸ’Ό πŸ“– 🎨 πŸ“‹ πŸ” πŸ€” πŸ“† πŸ’¬ πŸ‘€ πŸ“’ πŸ’‘ Jason Pong
Jason Pong

πŸ’» ⚠️ Jaume Mateu
Jaume Mateu

πŸ’» + Jonas Pirner
Jonas Pirner

πŸ“– JonathanBechtel
JonathanBechtel

πŸ’» πŸ€” ⚠️ Joren Hammudoglu
Joren Hammudoglu

πŸš‡ - Juan Orduz
Juan Orduz

βœ… πŸ“– + Juan Orduz
Juan Orduz

βœ… πŸ“– Julia Kraus
Julia Kraus

πŸ“– πŸ’» ⚠️ Julian Cooper
Julian Cooper

πŸ’» πŸ€” Julian Nowak
Julian Nowak

πŸ› πŸ’» @@ -144,9 +145,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Kai Lion
Kai Lion

πŸ’» ⚠️ πŸ“– Kavin Anand
Kavin Anand

πŸ“– Kejsi Take
Kejsi Take

πŸ’» - Kevin Lam
Kevin Lam

πŸ’» πŸ’‘ ⚠️ + Kevin Lam
Kevin Lam

πŸ’» πŸ’‘ ⚠️ Kirstie Whitaker
Kirstie Whitaker

πŸ€” πŸ” Kishan Manani
Kishan Manani

πŸ’» πŸ“– ⚠️ πŸ› πŸ€” Krum Arnaudov
Krum Arnaudov

πŸ› πŸ’» @@ -155,9 +156,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Lielle Ravid
Lielle Ravid

πŸ’» πŸ“– Logan Duffy
Logan Duffy

πŸ’» πŸ“– ⚠️ πŸ› πŸ€” Lorena Pantano
Lorena Pantano

πŸ€” - Lorenzo Toniazzi
Lorenzo Toniazzi

πŸ’» + Lorenzo Toniazzi
Lorenzo Toniazzi

πŸ’» Lovkush
Lovkush

πŸ’» ⚠️ πŸ€” πŸ§‘β€πŸ« πŸ“† Luca Miniati
Luca Miniati

πŸ’» πŸ“– Luis Ventura
Luis Ventura

πŸ’» @@ -166,9 +167,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Marc Rovira
Marc Rovira

πŸ“– Marcelo Trylesinski
Marcelo Trylesinski

πŸ“– Marco Gorelli
Marco Gorelli

πŸš‡ - Margaret Gorlin
Margaret Gorlin

πŸ’» πŸ’‘ ⚠️ + Margaret Gorlin
Margaret Gorlin

πŸ’» πŸ’‘ ⚠️ Mariam Jabara
Mariam Jabara

πŸ’» Marielle
Marielle

πŸ“– πŸ’» πŸ€” Markus LΓΆning
Markus LΓΆning

πŸ’» ⚠️ 🚧 πŸ“¦ πŸ‘€ πŸš‡ πŸ’‘ πŸ› βœ… πŸ’Ό πŸ“– 🎨 πŸ“‹ πŸ” πŸ€” πŸ“† πŸ’¬ πŸ“’ πŸ§‘β€πŸ« πŸ“Ή @@ -177,9 +178,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Mathias Creemers
Mathias Creemers

πŸ› πŸ’» Matthew Middlehurst
Matthew Middlehurst

πŸ’» πŸ“– ⚠️ βœ… πŸ‘€ πŸ› Max Patzelt
Max Patzelt

πŸ’» - Miao Cai
Miao Cai

πŸ› πŸ’» + Miao Cai
Miao Cai

πŸ› πŸ’» Michael Feil
Michael Feil

πŸ’» ⚠️ πŸ€” Michael Gaziani
Michael Gaziani

πŸ“– Michal Chromcak
Michal Chromcak

πŸ’» πŸ“– ⚠️ βœ… @@ -188,9 +189,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Mohammed Saif Kazamel
Mohammed Saif Kazamel

πŸ› Morad :)
Morad :)

πŸ’» ⚠️ πŸ“– Multivin12
Multivin12

πŸ’» ⚠️ - MΓ‘rcio A. Freitas Jr
MΓ‘rcio A. Freitas Jr

πŸ“– + MΓ‘rcio A. Freitas Jr
MΓ‘rcio A. Freitas Jr

πŸ“– Niek van der Laan
Niek van der Laan

πŸ’» Nikhil Gupta
Nikhil Gupta

πŸ’» πŸ› πŸ“– Nikola Shahpazov
Nikola Shahpazov

πŸ“– @@ -199,9 +200,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Noa Ben Ami
Noa Ben Ami

πŸ’» ⚠️ πŸ“– Oleksandr Shchur
Oleksandr Shchur

πŸ› πŸ’» Oleksii Kachaiev
Oleksii Kachaiev

πŸ’» ⚠️ - Oliver Matthews
Oliver Matthews

πŸ’» + Oliver Matthews
Oliver Matthews

πŸ’» Patrick Rockenschaub
Patrick Rockenschaub

πŸ’» 🎨 πŸ€” ⚠️ Patrick SchΓ€fer
Patrick SchΓ€fer

πŸ’» βœ… Paul
Paul

πŸ“– @@ -210,9 +211,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Piyush Gade
Piyush Gade

πŸ’» πŸ‘€ Pranav Prajapati
Pranav Prajapati

πŸ’» ⚠️ Pulkit Verma
Pulkit Verma

πŸ“– - Quaterion
Quaterion

πŸ› + Quaterion
Quaterion

πŸ› Rakshitha Godahewa
Rakshitha Godahewa

πŸ’» πŸ“– Ramon Bussing
Ramon Bussing

πŸ“– πŸ’» RavenRudi
RavenRudi

πŸ’» @@ -221,9 +222,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Rishi Kumar Ray
Rishi Kumar Ray

πŸš‡ Riya Elizabeth John
Riya Elizabeth John

πŸ’» ⚠️ πŸ“– Roman Lutz
Roman Lutz

πŸ“– - Ronnie Llamado
Ronnie Llamado

πŸ“– + Ronnie Llamado
Ronnie Llamado

πŸ“– Ryan Kuhns
Ryan Kuhns

πŸ’» πŸ“– βœ… πŸ’‘ πŸ€” πŸ‘€ ⚠️ Sagar Mishra
Sagar Mishra

⚠️ Sajaysurya Ganesh
Sajaysurya Ganesh

πŸ’» πŸ“– 🎨 πŸ’‘ πŸ€” ⚠️ βœ… @@ -232,9 +233,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Satya Prakash Pattnaik
Satya Prakash Pattnaik

πŸ“– Saurabh Dasgupta
Saurabh Dasgupta

πŸ’» Sebastiaan Koel
Sebastiaan Koel

πŸ’» πŸ“– - Sebastian Hagn
Sebastian Hagn

πŸ“– + Sebastian Hagn
Sebastian Hagn

πŸ“– Shivam Pathak
Shivam Pathak

πŸ“– Shivansh Subramanian
Shivansh Subramanian

πŸ“– πŸ’» Solomon Botchway
Solomon Botchway

🚧 @@ -243,9 +244,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d TNTran92
TNTran92

πŸ’» Taisei Yamamoto
Taisei Yamamoto

πŸ’» Taiwo Owoseni
Taiwo Owoseni

πŸ’» - Thach Le Nguyen
Thach Le Nguyen

πŸ’» ⚠️ + Thach Le Nguyen
Thach Le Nguyen

πŸ’» ⚠️ TheMathcompay Widget Factory Team
TheMathcompay Widget Factory Team

πŸ“– Thomas Buckley-Houston
Thomas Buckley-Houston

πŸ› Tom Xu
Tom Xu

πŸ’» πŸ“– @@ -254,9 +255,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Utsav Kumar Tiwari
Utsav Kumar Tiwari

πŸ’» πŸ“– Vasudeva Kilaru
Vasudeva Kilaru

πŸ’» πŸ“– Viktor Dremov
Viktor Dremov

πŸ’» - ViktorKaz
ViktorKaz

πŸ’» πŸ“– 🎨 + ViktorKaz
ViktorKaz

πŸ’» πŸ“– 🎨 Vyomkesh Vyas
Vyomkesh Vyas

πŸ’» πŸ“– πŸ’‘ ⚠️ William Templier
William Templier

πŸ“– William Zheng
William Zheng

πŸ’» ⚠️ @@ -265,9 +266,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Yi-Xuan Xu
Yi-Xuan Xu

πŸ’» ⚠️ 🚧 πŸ“– Ziyao Wei
Ziyao Wei

πŸ’» aa25desh
aa25desh

πŸ’» πŸ› - abandus
abandus

πŸ€” πŸ’» + abandus
abandus

πŸ€” πŸ’» adoherty21
adoherty21

πŸ› bethrice44
bethrice44

πŸ› πŸ’» πŸ‘€ ⚠️ big-o
big-o

πŸ’» ⚠️ 🎨 πŸ€” πŸ‘€ βœ… πŸ§‘β€πŸ« @@ -276,9 +277,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d btrtts
btrtts

πŸ“– chizzi25
chizzi25

πŸ“ chrisholder
chrisholder

πŸ’» ⚠️ πŸ“– 🎨 πŸ’‘ - danbartl
danbartl

πŸ› πŸ’» πŸ‘€ πŸ“’ ⚠️ βœ… πŸ“Ή + danbartl
danbartl

πŸ› πŸ’» πŸ‘€ πŸ“’ ⚠️ βœ… πŸ“Ή hamzahiqb
hamzahiqb

πŸš‡ hiqbal2
hiqbal2

πŸ“– jesellier
jesellier

πŸ’» @@ -287,9 +288,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d matteogales
matteogales

πŸ’» 🎨 πŸ€” oleskiewicz
oleskiewicz

πŸ’» πŸ“– ⚠️ pabworks
pabworks

πŸ’» ⚠️ - patiently pending world peace
patiently pending world peace

πŸ’» + patiently pending world peace
patiently pending world peace

πŸ’» raishubham1
raishubham1

πŸ“– simone-pignotti
simone-pignotti

πŸ’» πŸ› sophijka
sophijka

πŸ“– 🚧 @@ -298,6 +299,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d vedazeren
vedazeren

πŸ’» ⚠️ vincent-nich12
vincent-nich12

πŸ’» vollmersj
vollmersj

πŸ“– + + xiaobenbenecho
xiaobenbenecho

πŸ’» diff --git a/sktime/base/_base.py b/sktime/base/_base.py index 381c5a5a7a9..d2b9a747275 100644 --- a/sktime/base/_base.py +++ b/sktime/base/_base.py @@ -73,6 +73,8 @@ class BaseObject(_BaseObject): Extends skbase BaseObject with additional features. """ + _config = {"warnings": "on"} + _config_doc = { "display": """ display : str, "diagram" (default), or "text" diff --git a/sktime/classification/base.py b/sktime/classification/base.py index 33f2f7da129..40410efaa4a 100644 --- a/sktime/classification/base.py +++ b/sktime/classification/base.py @@ -24,7 +24,6 @@ class name: BaseClassifier import time from abc import ABC, abstractmethod -from warnings import warn import numpy as np import pandas as pd @@ -34,6 +33,7 @@ class name: BaseClassifier from sktime.utils.sklearn import is_sklearn_transformer from sktime.utils.validation import check_n_jobs from sktime.utils.validation._dependencies import _check_estimator_deps +from sktime.utils.warnings import warn class BaseClassifier(BaseEstimator, ABC): @@ -661,7 +661,7 @@ def _check_capabilities(self, missing, multivariate, unequal): # see discussion in PR 2366 why if len(problems) > 0: if self.is_composite(): - warn(msg) + warn(msg, obj=self) else: raise ValueError(msg) @@ -756,7 +756,8 @@ def _check_classifier_input( if len(np.unique(y)) == 1: warn( "only single class label seen in y passed to " - f"fit of classifier {type(self).__name__}" + f"fit of classifier {type(self).__name__}", + obj=self, ) return X_metadata diff --git a/sktime/classification/dictionary_based/_muse.py b/sktime/classification/dictionary_based/_muse.py index abc64c8c03f..46f37252d46 100644 --- a/sktime/classification/dictionary_based/_muse.py +++ b/sktime/classification/dictionary_based/_muse.py @@ -8,7 +8,6 @@ __all__ = ["MUSE"] import math -import warnings import numpy as np from joblib import Parallel, delayed @@ -18,6 +17,7 @@ from sktime.classification.base import BaseClassifier from sktime.transformations.panel.dictionary_based import SFAFast +from sktime.utils.warnings import warn class MUSE(BaseClassifier): @@ -198,9 +198,10 @@ def _fit(self, X, y): self.highest_dim_bit = (math.ceil(math.log2(self.n_dims))) + 1 if self.n_dims == 1: - warnings.warn( + warn( "MUSE Warning: Input series is univariate; MUSE is designed for" + " multivariate series. It is recommended WEASEL is used instead.", + obj=self, stacklevel=2, ) diff --git a/sktime/classification/dictionary_based/_tde.py b/sktime/classification/dictionary_based/_tde.py index 2acd04d9440..869db958a59 100644 --- a/sktime/classification/dictionary_based/_tde.py +++ b/sktime/classification/dictionary_based/_tde.py @@ -9,7 +9,6 @@ import math import time -import warnings from collections import defaultdict import numpy as np @@ -21,6 +20,7 @@ from sktime.classification.base import BaseClassifier from sktime.transformations.panel.dictionary_based import SFA from sktime.utils.validation.panel import check_X_y +from sktime.utils.warnings import warn class TemporalDictionaryEnsemble(BaseClassifier): @@ -234,10 +234,11 @@ def _fit(self, X, y): ending in "_" and sets is_fitted flag to True. """ if self.n_parameter_samples <= self.randomly_selected_params: - warnings.warn( + warn( "TemporalDictionaryEnsemble warning: n_parameter_samples <= " - "randomly_selected_params, ensemble member parameters will be fully " - "randomly selected.", + "randomly_selected_params, ensemble member parameters will be " + "fully randomly selected.", + obj=self, stacklevel=2, ) @@ -254,10 +255,11 @@ def _fit(self, X, y): if self.min_window >= max_window: self._min_window = max_window - warnings.warn( + warn( f"TemporalDictionaryEnsemble warning: min_window = " f"{self.min_window} is larger than max_window = {max_window}." f" min_window has been set to {max_window}.", + obj=self, stacklevel=2, ) diff --git a/sktime/classification/ensemble/_ctsf.py b/sktime/classification/ensemble/_ctsf.py index 335cfc3d25b..325f88a5bb0 100644 --- a/sktime/classification/ensemble/_ctsf.py +++ b/sktime/classification/ensemble/_ctsf.py @@ -4,7 +4,6 @@ __all__ = ["ComposableTimeSeriesForestClassifier"] import numbers -from warnings import warn import numpy as np from joblib import Parallel, delayed @@ -23,6 +22,7 @@ from sktime.transformations.panel.summarize import RandomIntervalFeatureExtractor from sktime.utils.slope_and_trend import _slope from sktime.utils.validation.panel import check_X, check_X_y +from sktime.utils.warnings import warn class ComposableTimeSeriesForestClassifier(BaseTimeSeriesForest, BaseClassifier): @@ -452,7 +452,8 @@ def _set_oob_score(self, X, y): warn( "Some inputs do not have OOB scores. " "This probably means too few trees were used " - "to compute any reliable oob estimates." + "to compute any reliable oob estimates.", + obj=self, ) decision = predictions[k] / predictions[k].sum(axis=1)[:, np.newaxis] @@ -511,7 +512,8 @@ def _validate_y_class_weight(self, y): "a large enough sample of the full training set " "target to properly estimate the class frequency " "distributions. Pass the resulting weights as the " - "class_weight parameter." + "class_weight parameter.", + obj=self, ) if self.class_weight != "balanced_subsample" or not self.bootstrap: diff --git a/sktime/classification/feature_based/_tsfresh_classifier.py b/sktime/classification/feature_based/_tsfresh_classifier.py index 7a6eedf7cf9..50216d7bd2c 100644 --- a/sktime/classification/feature_based/_tsfresh_classifier.py +++ b/sktime/classification/feature_based/_tsfresh_classifier.py @@ -6,8 +6,6 @@ __author__ = ["MatthewMiddlehurst"] __all__ = ["TSFreshClassifier"] -import warnings - import numpy as np from sklearn.ensemble import RandomForestClassifier @@ -17,6 +15,7 @@ TSFreshFeatureExtractor, TSFreshRelevantFeatureExtractor, ) +from sktime.utils.warnings import warn class TSFreshClassifier(BaseClassifier): @@ -152,7 +151,7 @@ def _fit(self, X, y): X_t = self._transformer.fit_transform(X, y) if X_t.shape[1] == 0: - warnings.warn( + warn( "TSFresh has extracted no features from the data. Returning the " "majority class in predictions. Setting " "relevant_feature_extractor=False will keep all features.", diff --git a/sktime/clustering/dbscan.py b/sktime/clustering/dbscan.py index 2c8419663e3..46e824b1961 100644 --- a/sktime/clustering/dbscan.py +++ b/sktime/clustering/dbscan.py @@ -2,14 +2,13 @@ __author__ = ["fkiraly"] -from warnings import warn - import numpy as np from sklearn.cluster import DBSCAN from sktime.clustering.base import BaseClusterer from sktime.datatypes import update_data from sktime.dists_kernels.base import BasePairwiseTransformerPanel +from sktime.utils.warnings import warn class TimeSeriesDBSCAN(BaseClusterer): @@ -155,7 +154,8 @@ def _predict(self, X, y=None): "sklearn and sktime DBSCAN estimators do not support different X " "in fit and predict, but a new X was passed in predict. " "Therefore, a clone of TimeSeriesDBSCAN will be fit, and results " - "returned, without updating the state of the fitted estimator." + "returned, without updating the state of the fitted estimator.", + obj=self, ) return self.clone().fit(all_X).labels_ diff --git a/sktime/forecasting/base/_base.py b/sktime/forecasting/base/_base.py index 7f0026ff7f1..e503eb8deb3 100644 --- a/sktime/forecasting/base/_base.py +++ b/sktime/forecasting/base/_base.py @@ -38,7 +38,6 @@ class name: BaseForecaster from copy import deepcopy from itertools import product -from warnings import warn import numpy as np import pandas as pd @@ -58,6 +57,7 @@ class name: BaseForecaster from sktime.utils.validation._dependencies import _check_estimator_deps from sktime.utils.validation.forecasting import check_alpha, check_cv, check_fh, check_X from sktime.utils.validation.series import check_equal_time_index +from sktime.utils.warnings import warn DEFAULT_ALPHA = 0.05 @@ -863,7 +863,10 @@ def update(self, y, X=None, update_params=True): self.check_is_fitted() if y is None or (hasattr(y, "__len__") and len(y) == 0): - warn("empty y passed to update, no update was carried out") + warn( + f"empty y passed to update of {self}, no update was carried out", + obj=self, + ) return self # input checks and minor coercions on X, y @@ -1054,7 +1057,11 @@ def update_predict_single( Series, Panel, Hierarchical scitype, same format (see above) """ if y is None or (hasattr(y, "__len__") and len(y) == 0): - warn("empty y passed to update_predict, no update was carried out") + warn( + f"empty y passed to update_predict of {self}, " + "no update was carried out", + obj=self, + ) return self.predict(fh=fh, X=X) self.check_is_fitted() @@ -1915,7 +1922,8 @@ def _update(self, y, X=None, update_params=True): f"{self.__class__.__name__} will be refit each time " f"`update` is called with update_params=True. " "To refit less often, use the wrappers in the " - "forecasting.stream module, e.g., UpdateEvery." + "forecasting.stream module, e.g., UpdateEvery.", + obj=self, ) # we need to overwrite the mtype last seen and converter store, since the _y # may have been converted @@ -1936,7 +1944,8 @@ def _update(self, y, X=None, update_params=True): f"NotImplementedWarning: {self.__class__.__name__} " f"does not have a custom `update` method implemented. " f"{self.__class__.__name__} will update all component cutoffs each time" - f" `update` is called with update_params=False." + f" `update` is called with update_params=False.", + obj=self, ) comp_forecasters = self._components(base_class=BaseForecaster) for comp in comp_forecasters.values(): diff --git a/sktime/forecasting/base/adapters/_statsmodels.py b/sktime/forecasting/base/adapters/_statsmodels.py index cfed3abf00b..0e3d91ecbc6 100644 --- a/sktime/forecasting/base/adapters/_statsmodels.py +++ b/sktime/forecasting/base/adapters/_statsmodels.py @@ -6,12 +6,12 @@ __all__ = ["_StatsModelsAdapter"] import inspect -from warnings import warn import numpy as np import pandas as pd from sktime.forecasting.base import BaseForecaster +from sktime.utils.warnings import warn class _StatsModelsAdapter(BaseForecaster): @@ -67,7 +67,8 @@ def _update(self, y, X=None, update_params=True): warn( f"NotImplementedWarning: {self.__class__.__name__} " f"can not accept new data when update_params=False. " - f"Call with update_params=True to refit with new data." + f"Call with update_params=True to refit with new data.", + obj=self, ) else: # only append unseen data to fitted forecaster diff --git a/sktime/forecasting/compose/_hierarchy_ensemble.py b/sktime/forecasting/compose/_hierarchy_ensemble.py index eced4135bbd..eb9ab6e539b 100644 --- a/sktime/forecasting/compose/_hierarchy_ensemble.py +++ b/sktime/forecasting/compose/_hierarchy_ensemble.py @@ -5,14 +5,13 @@ __all__ = ["HierarchyEnsembleForecaster"] -from warnings import warn - import pandas as pd from sktime.base._meta import flatten from sktime.forecasting.base._base import BaseForecaster from sktime.forecasting.base._meta import _HeterogenousEnsembleForecaster from sktime.transformations.hierarchical.aggregate import _check_index_no_total +from sktime.utils.warnings import warn class HierarchyEnsembleForecaster(_HeterogenousEnsembleForecaster): @@ -531,11 +530,13 @@ def _check_forecasters(self, y, z): else: nodes_l += inds warn( - f"Ideally, length of individual node should be " + f"Ideally, length of individual node " + f"in HierarchyEnsembleForecaster should be " f"equal to N-1 (where N is number of levels in " f"multi-index) and must not exceed N-1. The " f"forecaster will now be fitted to the " - f"following nodes : {list(inds)}" + f"following nodes : {list(inds)}", + obj=self, ) elif ( isinstance(nodes[i], tuple) diff --git a/sktime/forecasting/compose/_pipeline.py b/sktime/forecasting/compose/_pipeline.py index 9edd3ba9957..9b44a5f4c9b 100644 --- a/sktime/forecasting/compose/_pipeline.py +++ b/sktime/forecasting/compose/_pipeline.py @@ -78,6 +78,15 @@ def _check_steps(self, estimators, allow_postproc=False): ) raise TypeError(msg) + # if len(estimators) == 1: + # msg = ( + # f"in {self_name}, found steps of length 1, " + # f"this will result in the same behaviour " + # f"as not wrapping the single step in a pipeline. " + # f"Consider not wrapping steps in {self_name} as it is redundant." + # ) + # warn(msg, obj=self) + estimator_tuples = self._get_estimator_tuples(estimators, clone_ests=True) names, estimators = zip(*estimator_tuples) diff --git a/sktime/forecasting/compose/_reduce.py b/sktime/forecasting/compose/_reduce.py index 594704e4a78..ed4c0294f37 100644 --- a/sktime/forecasting/compose/_reduce.py +++ b/sktime/forecasting/compose/_reduce.py @@ -25,8 +25,6 @@ "YfromX", ] -from warnings import warn - import numpy as np import pandas as pd from sklearn.base import clone @@ -44,6 +42,7 @@ from sktime.utils.estimators.dispatch import construct_dispatch from sktime.utils.sklearn import is_sklearn_regressor from sktime.utils.validation import check_window_length +from sktime.utils.warnings import warn def _concat_y_X(y, X): @@ -464,11 +463,11 @@ def _fit(self, y, X, fh): if self.pooling == "local": if pd_format is True and isinstance(y, pd.MultiIndex): warn( - "Pooling has been changed by default to 'local', which" + "Pooling is by default 'local', which" + " means that separate models will be fit at the level of" + " each instance. If you wish to fit a single model to" + " all instances, please specify pooling = 'global'.", - DeprecationWarning, + obj=self, ) self.window_length_ = check_window_length( self.window_length, n_timepoints=len(y) @@ -774,11 +773,11 @@ def _fit(self, y, X, fh): if self.pooling == "local": if pd_format is True and isinstance(y, pd.MultiIndex): warn( - "Pooling has been changed by default to 'local', which" + "Pooling is by default 'local', which" + " means that separate models will be fit at the level of" + " each instance. If you wish to fit a single model to" + " all instances, please specify pooling = 'global'.", - DeprecationWarning, + obj=self, ) if self.transformers is not None: self.transformers_ = clone(self.transformers) @@ -1488,7 +1487,8 @@ def _infer_scitype(estimator): "The `scitype` of the given `estimator` cannot be inferred. " 'Assuming "tabular-regressor" = scikit-learn regressor interface. ' "If this warning is followed by an unexpected exception, " - "please consider report as a bug on the sktime issue tracker." + "please consider report as a bug on the sktime issue tracker.", + obj=estimator, ) return "tabular-regressor" diff --git a/sktime/forecasting/compose/_stack.py b/sktime/forecasting/compose/_stack.py index 11a7d33bde4..8bcc8e163af 100644 --- a/sktime/forecasting/compose/_stack.py +++ b/sktime/forecasting/compose/_stack.py @@ -5,14 +5,13 @@ __author__ = ["mloning", "fkiraly", "indinewton"] __all__ = ["StackingForecaster"] -from warnings import warn - import numpy as np import pandas as pd from sktime.forecasting.base._meta import _HeterogenousEnsembleForecaster from sktime.split import SingleWindowSplitter from sktime.utils.validation.forecasting import check_regressor +from sktime.utils.warnings import warn class StackingForecaster(_HeterogenousEnsembleForecaster): @@ -141,7 +140,7 @@ def _update(self, y, X=None, update_params=True): self : an instance of self """ if update_params: - warn("Updating `final regressor is not implemented") + warn("Updating `final regressor is not implemented", obj=self) for forecaster in self.forecasters_: forecaster.update(y, X, update_params=update_params) return self diff --git a/sktime/forecasting/conformal.py b/sktime/forecasting/conformal.py index 516a5b8ea9e..175e7543711 100644 --- a/sktime/forecasting/conformal.py +++ b/sktime/forecasting/conformal.py @@ -8,7 +8,6 @@ __author__ = ["fkiraly", "bethrice44"] from math import floor -from warnings import warn import numpy as np import pandas as pd @@ -18,6 +17,7 @@ from sktime.datatypes import MTYPE_LIST_SERIES, convert, convert_to from sktime.datatypes._utilities import get_slice from sktime.forecasting.base import BaseForecaster +from sktime.utils.warnings import warn class ConformalIntervals(BaseForecaster): @@ -456,8 +456,9 @@ def _get_residuals_matrix_row(forecaster, y, X, id): residuals = forecaster.predict_residuals(y_test, X_test) except IndexError: warn( - f"Couldn't predict after fitting on time series of length \ - {len(y_train)}.\n" + "Couldn't predict after fitting on time series of length" + f"{len(y_train)}.", + obj=forecaster, ) return residuals @@ -483,8 +484,9 @@ def _extend_residuals_matrix_row(y, X, id): residuals = forecaster_to_extend.predict_residuals(y_test, X_test) except IndexError: warn( - f"Couldn't predict with existing forecaster for cutoff {id} \ - with existing forecaster.\n" + f"Couldn't predict with existing forecaster for cutoff {id}" + " with existing forecaster.", + obj=forecaster, ) return residuals diff --git a/sktime/forecasting/naive.py b/sktime/forecasting/naive.py index 5f726bdb26e..cf518b1ba3b 100644 --- a/sktime/forecasting/naive.py +++ b/sktime/forecasting/naive.py @@ -15,7 +15,6 @@ ] import math -from warnings import warn import numpy as np import pandas as pd @@ -29,6 +28,7 @@ from sktime.utils.seasonality import _pivot_sp, _unpivot_sp from sktime.utils.validation import check_window_length from sktime.utils.validation.forecasting import check_sp +from sktime.utils.warnings import warn class NaiveForecaster(_BaseWindowForecaster): @@ -167,7 +167,10 @@ def _fit(self, y, X, fh): elif self.strategy == "drift": if sp != 1: - warn("For the `drift` strategy, the `sp` value will be ignored.") + warn( + "For the `drift` strategy, the `sp` value will be ignored.", + obj=self, + ) # window length we need for forecasts is just the # length of seasonal periodicity self.window_length_ = check_window_length(self.window_length, n_timepoints) @@ -844,15 +847,17 @@ def _compute_sliding_residuals(self, y, X, forecaster, initial_window): if self.verbose: warn( f"Couldn't fit the model on " - f"time series window length {len(y_train)}.\n" + f"time series window length {len(y_train)}.\n", + obj=self, ) continue try: residuals_matrix.loc[id] = forecaster.predict_residuals(y_test, X) except IndexError: warn( - f"Couldn't predict after fitting on time series of length \ - {len(y_train)}.\n" + f"Couldn't predict after fitting on time series of length " + f"{len(y_train)}.\n", + obj=self, ) return residuals_matrix diff --git a/sktime/forecasting/reconcile.py b/sktime/forecasting/reconcile.py index 8c2e485ff6f..2b4e89447ef 100644 --- a/sktime/forecasting/reconcile.py +++ b/sktime/forecasting/reconcile.py @@ -9,8 +9,6 @@ # todo: top down historical proportions? -> new _get_g_matrix_prop(self) -from warnings import warn - import numpy as np import pandas as pd from numpy.linalg import inv @@ -22,6 +20,7 @@ _get_s_matrix, _parent_child_df, ) +from sktime.utils.warnings import warn class ReconcilerForecaster(BaseForecaster): @@ -214,7 +213,8 @@ def _predict(self, fh, X): if base_fc.index.nlevels < 2: warn( "Reconciler is intended for use with y.index.nlevels > 1. " - "Returning predictions unchanged." + "Returning predictions unchanged.", + obj=self, ) return base_fc diff --git a/sktime/forecasting/squaring_residuals.py b/sktime/forecasting/squaring_residuals.py index 01b86827091..c9333d22ade 100644 --- a/sktime/forecasting/squaring_residuals.py +++ b/sktime/forecasting/squaring_residuals.py @@ -4,14 +4,13 @@ __all__ = ["SquaringResiduals"] __author__ = ["kcc-lion"] -from warnings import warn - import pandas as pd from sktime.datatypes._convert import convert_to from sktime.forecasting.base import BaseForecaster, ForecastingHorizon from sktime.forecasting.naive import NaiveForecaster from sktime.split import ExpandingWindowSplitter +from sktime.utils.warnings import warn class SquaringResiduals(BaseForecaster): @@ -349,7 +348,11 @@ def _predict_var(self, fh, X=None, cov=False): a square matrix of size len(fh) with predictive covariance matrix. """ if cov: - warn(f"cov={cov} is not supported. Defaulting to cov=False instead.") + warn( + f"cov={cov} is not supported in SquaringResiduals. " + "Defaulting to cov=False instead.", + obj=self, + ) fh_abs = fh.to_absolute(self.cutoff) fh_rel = fh.to_relative(self.cutoff) fh_rel_index = fh_rel.to_pandas() diff --git a/sktime/forecasting/theta.py b/sktime/forecasting/theta.py index ac133f92b5d..311832bdb13 100644 --- a/sktime/forecasting/theta.py +++ b/sktime/forecasting/theta.py @@ -3,8 +3,6 @@ __all__ = ["ThetaForecaster", "ThetaModularForecaster"] __author__ = ["big-o", "mloning", "kejsitake", "fkiraly", "GuzalBulatova"] -from warnings import warn - import numpy as np import pandas as pd from scipy.stats import norm @@ -20,6 +18,7 @@ from sktime.utils.slope_and_trend import _fit_trend from sktime.utils.validation._dependencies import _check_estimator_deps from sktime.utils.validation.forecasting import check_sp +from sktime.utils.warnings import warn class ThetaForecaster(ExponentialSmoothing): @@ -126,7 +125,10 @@ def _fit(self, y, X, fh): """ sp = check_sp(self.sp) if sp > 1 and not self.deseasonalize: - warn("`sp` is ignored when `deseasonalise`=False") + warn( + "`sp` in ThetaForecaster is ignored when `deseasonalise`=False", + obj=self, + ) if self.deseasonalize: self.deseasonalizer_ = Deseasonalizer(sp=self.sp, model="multiplicative") diff --git a/sktime/param_est/base.py b/sktime/param_est/base.py index 44da70e1fe3..d1a430e7603 100644 --- a/sktime/param_est/base.py +++ b/sktime/param_est/base.py @@ -21,8 +21,6 @@ class name: BaseParamFitter __all__ = ["BaseParamFitter"] -from warnings import warn - from sktime.base import BaseEstimator from sktime.datatypes import ( VectorizedDF, @@ -33,6 +31,7 @@ class name: BaseParamFitter ) from sktime.utils.sklearn import is_sklearn_transformer from sktime.utils.validation._dependencies import _check_estimator_deps +from sktime.utils.warnings import warn def _coerce_to_list(obj): @@ -197,7 +196,10 @@ def update(self, X): self.check_is_fitted() if X is None or (hasattr(X, "__len__") and len(X) == 0): - warn("empty y passed to update, no update was carried out") + warn( + f"empty y passed to update of {self}, no update was carried out", + obj=self, + ) return self # input checks and minor coercions on X, y @@ -370,7 +372,8 @@ def _update(self, X): f"NotImplementedWarning: {self.__class__.__name__} " f"does not have a custom `update` method implemented. " f"{self.__class__.__name__} will be refit each time " - f"`update` is called." + f"`update` is called.", + obj=self, ) # refit with updated data, not only passed data self.fit(X=self._X) diff --git a/sktime/performance_metrics/forecasting/_classes.py b/sktime/performance_metrics/forecasting/_classes.py index fbded42da4d..2d0a4fe0673 100644 --- a/sktime/performance_metrics/forecasting/_classes.py +++ b/sktime/performance_metrics/forecasting/_classes.py @@ -7,7 +7,6 @@ the lower the better. """ from inspect import getfullargspec, isfunction, signature -from warnings import warn import numpy as np import pandas as pd @@ -44,6 +43,7 @@ median_squared_scaled_error, relative_loss, ) +from sktime.utils.warnings import warn __author__ = ["mloning", "tch", "RNKuhns", "fkiraly"] __all__ = [ @@ -455,7 +455,8 @@ def _check_consistent_input(self, y_true, y_pred, multioutput, multilevel): warn( "y_pred and y_true do not have the same row index. " "This may indicate incorrect objects passed to the metric. " - "Indices of y_true will be used for y_pred." + "Indices of y_true will be used for y_pred.", + obj=self, ) y_pred_orig = y_pred_orig.copy() if isinstance(y_pred_orig, VectorizedDF): @@ -466,7 +467,8 @@ def _check_consistent_input(self, y_true, y_pred, multioutput, multilevel): warn( "y_pred and y_true do not have the same column index. " "This may indicate incorrect objects passed to the metric. " - "Indices of y_true will be used for y_pred." + "Indices of y_true will be used for y_pred.", + obj=self, ) y_pred_orig = y_pred_orig.copy() if isinstance(y_pred_orig, VectorizedDF): diff --git a/sktime/regression/base.py b/sktime/regression/base.py index 533c5c11110..0d4bc2bf8a9 100644 --- a/sktime/regression/base.py +++ b/sktime/regression/base.py @@ -24,7 +24,6 @@ class name: BaseRegressor import time from abc import ABC, abstractmethod -from warnings import warn import numpy as np import pandas as pd @@ -33,6 +32,7 @@ class name: BaseRegressor from sktime.datatypes import check_is_scitype, convert_to from sktime.utils.sklearn import is_sklearn_transformer from sktime.utils.validation import check_n_jobs +from sktime.utils.warnings import warn class BaseRegressor(BaseEstimator, ABC): @@ -354,7 +354,7 @@ def _check_capabilities(self, missing, multivariate, unequal): # see discussion in PR 2366 why if len(problems) > 0: if self.is_composite(): - warn(msg) + warn(msg, obj=self) else: raise ValueError(msg) diff --git a/sktime/regression/compose/_ensemble.py b/sktime/regression/compose/_ensemble.py index b2d8202d888..ff5e84ff595 100644 --- a/sktime/regression/compose/_ensemble.py +++ b/sktime/regression/compose/_ensemble.py @@ -6,7 +6,6 @@ __all__ = ["ComposableTimeSeriesForestRegressor"] import numbers -from warnings import warn import numpy as np from joblib import Parallel, delayed @@ -24,6 +23,7 @@ from sktime.transformations.panel.summarize import RandomIntervalFeatureExtractor from sktime.utils.slope_and_trend import _slope from sktime.utils.validation.panel import check_X, check_X_y +from sktime.utils.warnings import warn class ComposableTimeSeriesForestRegressor(BaseTimeSeriesForest, BaseRegressor): @@ -364,9 +364,10 @@ def _set_oob_score(self, X, y): if (n_predictions == 0).any(): warn( - "Some inputs do not have OOB scores. " + "Some inputs in ComposableTimeSeriesRegressor do not have OOB scores. " "This probably means too few trees were used " - "to compute any reliable oob estimates." + "to compute any reliable oob estimates.", + obj=self, ) n_predictions[n_predictions == 0] = 1 diff --git a/sktime/series_as_features/base/estimators/_ensemble.py b/sktime/series_as_features/base/estimators/_ensemble.py index a8624ab67fd..4ebce281df0 100644 --- a/sktime/series_as_features/base/estimators/_ensemble.py +++ b/sktime/series_as_features/base/estimators/_ensemble.py @@ -6,7 +6,7 @@ __all__ = ["BaseTimeSeriesForest"] from abc import abstractmethod -from warnings import catch_warnings, simplefilter, warn +from warnings import catch_warnings, simplefilter import numpy as np import pandas as pd @@ -24,6 +24,7 @@ from sktime.transformations.panel.summarize import RandomIntervalFeatureExtractor from sktime.utils.random_state import set_random_state +from sktime.utils.warnings import warn def _parallel_build_trees( @@ -163,6 +164,7 @@ def _fit(self, X, y, sample_weight=None): "(n_samples,), for example using ravel().", DataConversionWarning, stacklevel=2, + obj=self, ) if y.ndim == 1: @@ -212,7 +214,8 @@ def _fit(self, X, y, sample_weight=None): elif n_more_estimators == 0: warn( "Warm-start fitting without increasing n_estimators does not " - "fit new trees." + "fit new trees.", + obj=self, ) else: if self.warm_start and len(self.estimators_) > 0: diff --git a/sktime/transformations/compose/_grouped.py b/sktime/transformations/compose/_grouped.py index cb3220377cf..6dcfbf6ebca 100644 --- a/sktime/transformations/compose/_grouped.py +++ b/sktime/transformations/compose/_grouped.py @@ -1,10 +1,9 @@ # copyright: sktime developers, BSD-3-Clause License (see LICENSE file) """Implements compositors for performing transformations by group.""" -from warnings import warn - from sktime.datatypes import ALL_TIME_SERIES_MTYPES, mtype_to_scitype from sktime.transformations._delegate import _DelegatedTransformer +from sktime.utils.warnings import warn __author__ = ["fkiraly"] __all__ = ["TransformByLevel"] @@ -85,6 +84,7 @@ def __init__(self, transformer, groupby="local", raise_warnings=True): "transforms by instance already, wrapping in TransformByLevel " "will not change the estimator logic, compared to not wrapping it.", stacklevel=2, + obj=self, ) self.clone_tags(self.transformer_) diff --git a/sktime/transformations/compose/_invert.py b/sktime/transformations/compose/_invert.py index f4beba1c7f8..686b459ef2e 100644 --- a/sktime/transformations/compose/_invert.py +++ b/sktime/transformations/compose/_invert.py @@ -4,9 +4,8 @@ __author__ = ["fkiraly"] __all__ = ["InvertTransform"] -from warnings import warn - from sktime.transformations._delegate import _DelegatedTransformer +from sktime.utils.warnings import warn class InvertTransform(_DelegatedTransformer): @@ -75,7 +74,8 @@ def __init__(self, transformer): warn( "transformer does not have capability to inverse transform, " "according to capability:inverse_transform tag. " - "If the tag was correctly set, this transformer will likely crash" + "If the tag was correctly set, this transformer will likely crash", + obj=self, ) inner_output = transformer.get_tag("scitype:transform-output") if transformer.get_tag("scitype:transform-output") != "Series": @@ -83,7 +83,8 @@ def __init__(self, transformer): f"transformer output is not Series but {inner_output}, " "according to scitype:transform-output tag. " "The InvertTransform wrapper supports only Series output, therefore" - " this transformer will likely crash on input." + " this transformer will likely crash on input.", + obj=self, ) # attribute for _DelegatedTransformer, which then delegates diff --git a/sktime/transformations/hierarchical/aggregate.py b/sktime/transformations/hierarchical/aggregate.py index d1cc6e7a51e..6cee6787f4d 100644 --- a/sktime/transformations/hierarchical/aggregate.py +++ b/sktime/transformations/hierarchical/aggregate.py @@ -3,12 +3,11 @@ __author__ = ["ciaran-g"] -from warnings import warn - import numpy as np import pandas as pd from sktime.transformations.base import BaseTransformer +from sktime.utils.warnings import warn # todo: add any necessary sktime internal imports here @@ -93,14 +92,16 @@ def _transform(self, X, y=None): if X.index.nlevels == 1: warn( "Aggregator is intended for use with X.index.nlevels > 1. " - "Returning X unchanged." + "Returning X unchanged.", + obj=self, ) return X # check the tests are ok if not _check_index_no_total(X): warn( "Found elemnts in the index of X named '__total'. Removing " - "these levels and aggregating." + "these levels and aggregating.", + obj=self, ) X = self._inverse_transform(X) @@ -166,13 +167,15 @@ def _inverse_transform(self, X, y=None): if X.index.nlevels == 1: warn( "Aggregator is intended for use with X.index.nlevels > 1. " - "Returning X unchanged." + "Returning X unchanged.", + obj=self, ) return X if _check_index_no_total(X): warn( "Inverse is inteded to be used with aggregated data. " - "Returning X unchanged." + "Returning X unchanged.", + obj=self, ) else: for i in range(X.index.nlevels - 1): diff --git a/sktime/transformations/hierarchical/reconcile.py b/sktime/transformations/hierarchical/reconcile.py index c3fa206dab5..9c56dee7271 100644 --- a/sktime/transformations/hierarchical/reconcile.py +++ b/sktime/transformations/hierarchical/reconcile.py @@ -6,14 +6,13 @@ __author__ = ["ciaran-g", "eenticott-shell", "k1m190r"] -from warnings import warn - import numpy as np import pandas as pd from numpy.linalg import inv from sktime.transformations.base import BaseTransformer from sktime.transformations.hierarchical.aggregate import _check_index_no_total +from sktime.utils.warnings import warn # TODO: failing test which are escaped @@ -171,7 +170,8 @@ def _transform(self, X, y=None): if X.index.nlevels < 2: warn( "Reconciler is intended for use with X.index.nlevels > 1. " - "Returning X unchanged." + "Returning X unchanged.", + obj=self, ) return X @@ -180,7 +180,8 @@ def _transform(self, X, y=None): warn( "No elements of the index of X named '__total' found. Adding " "aggregate levels using the default Aggregator transformer " - "before reconciliation." + "before reconciliation.", + obj=self, ) X = self._add_totals(X) diff --git a/sktime/transformations/series/exponent.py b/sktime/transformations/series/exponent.py index f96ef70c1af..f66d05c5b90 100644 --- a/sktime/transformations/series/exponent.py +++ b/sktime/transformations/series/exponent.py @@ -5,12 +5,11 @@ __author__ = ["RNKuhns"] __all__ = ["ExponentTransformer", "SqrtTransformer"] -from warnings import warn - import numpy as np import pandas as pd from sktime.transformations.base import BaseTransformer +from sktime.utils.warnings import warn class ExponentTransformer(BaseTransformer): @@ -106,7 +105,8 @@ def __init__(self, power=0.5, offset="auto"): warn( "power close to zero passed to ExponentTransformer, " "inverse_transform will default to identity " - "if called, in order to avoid division by zero" + "if called, in order to avoid division by zero", + obj=self, ) self.set_tags(**{"skip-inverse-transform": True}) diff --git a/sktime/transformations/series/kalman_filter.py b/sktime/transformations/series/kalman_filter.py index dd7f85701fb..91ac5bbce98 100644 --- a/sktime/transformations/series/kalman_filter.py +++ b/sktime/transformations/series/kalman_filter.py @@ -12,12 +12,11 @@ "KalmanFilterTransformerFP", ] -from warnings import warn - import numpy as np from sktime.transformations.base import BaseTransformer from sktime.utils.validation._dependencies import _check_soft_dependencies +from sktime.utils.warnings import warn def _get_t_matrix(time_t, matrices, shape, time_steps): @@ -1194,7 +1193,8 @@ def _transform(self, X, y=None): "Class parameter `control_transition` was initiated with user data " "but received no data through `transform` argument, `y`. " "Therefore, omitting `control_transition` " - "when calculating the result. " + "when calculating the result. ", + obj=self, ) y = np.zeros(y_dim) diff --git a/sktime/transformations/series/lag.py b/sktime/transformations/series/lag.py index b234acc4778..3e359e8e468 100644 --- a/sktime/transformations/series/lag.py +++ b/sktime/transformations/series/lag.py @@ -3,13 +3,12 @@ __author__ = ["fkiraly"] -from warnings import warn - import numpy as np import pandas as pd from sktime.transformations.base import BaseTransformer from sktime.utils.multiindex import flatten_multiindex +from sktime.utils.warnings import warn # this function is needed since pandas DataFrame.shift @@ -496,7 +495,8 @@ def _fit(self, X, y=None): if len(lags) == 0 and y is None: warn( "no lags specified and no exogeneous data present, " - "empty reduction X. Returning all-zeros X." + "empty reduction X. Returning all-zeros X.", + obj=self, ) self.trafo_ = 0 return self @@ -555,7 +555,8 @@ def _transform(self, X, y=None): duplicates = list(varnames[varnames.duplicated()]) warn( f"duplicate variable names found in ReducerTransform: {duplicates}, " - "returning variables with transformer name prefix" + "returning variables with transformer name prefix", + obj=self, ) Xt.columns = flatten_multiindex(Xt.columns) diff --git a/sktime/transformations/series/scaledlogit.py b/sktime/transformations/series/scaledlogit.py index 44ed3487d12..741931c9b01 100644 --- a/sktime/transformations/series/scaledlogit.py +++ b/sktime/transformations/series/scaledlogit.py @@ -5,11 +5,11 @@ __all__ = ["ScaledLogitTransformer"] from copy import deepcopy -from warnings import warn import numpy as np from sktime.transformations.base import BaseTransformer +from sktime.utils.warnings import warn class ScaledLogitTransformer(BaseTransformer): @@ -131,6 +131,7 @@ def _transform(self, X, y=None): "X in ScaledLogitTransformer should not have values " "greater than upper_bound", RuntimeWarning, + obj=self, ) if self.lower_bound is not None and np.any(X <= self.lower_bound): @@ -138,6 +139,7 @@ def _transform(self, X, y=None): "X in ScaledLogitTransformer should not have values " "lower than lower_bound", RuntimeWarning, + obj=self, ) if self.upper_bound and self.lower_bound: diff --git a/sktime/transformations/series/tests/test_scaledlogit.py b/sktime/transformations/series/tests/test_scaledlogit.py index 29b40360d12..43fdc76a98f 100644 --- a/sktime/transformations/series/tests/test_scaledlogit.py +++ b/sktime/transformations/series/tests/test_scaledlogit.py @@ -3,13 +3,12 @@ __author__ = ["ltsaprounis"] -from warnings import warn - import numpy as np import pytest from sktime.datasets import load_airline from sktime.transformations.series.scaledlogit import ScaledLogitTransformer +from sktime.utils.warnings import warn TEST_SERIES = np.array([30, 40, 60]) diff --git a/sktime/utils/warnings.py b/sktime/utils/warnings.py new file mode 100644 index 00000000000..26fc21b9c10 --- /dev/null +++ b/sktime/utils/warnings.py @@ -0,0 +1,39 @@ +"""Warning related utilities.""" +# copyright: sktime developers, BSD-3-Clause License (see LICENSE file) + +from warnings import warn as _warn + +__author__ = ["fkiraly"] + + +def warn(msg, category=None, obj=None, stacklevel=2): + """Warn if obj has warnings turned on, otherwise not. + + Wraps ``warnings.warn`` in a conditional based on ``obj``. + If ``obj.get_config()["warnings"] == "on"``, warns, otherwise not. + + Developer note: this is for configurable user warnings only. + Deprecation warnings must always be raised. + + Parameters + ---------- + msg : str + warning message, passed on to ``warnings.warn`` + category : optional, warning class + class of the warning to be raised + obj : optional, any sktime object - cannot be class + stacklevel : int + stack level, passed on to ``warnings.warn`` + + Returns + ------- + is_sklearn_est : bool, whether obj is an sklearn estimator (class or instance) + """ + if obj is None or not hasattr(obj, "get_config"): + return _warn(msg, category=category, stacklevel=stacklevel) + + warn_on = obj.get_config()["warnings"] == "on" + if warn_on: + return _warn(msg, category=category, stacklevel=stacklevel) + else: + return None From 6a4605dcecb4e1dc3f9277717687434ed1b33271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Thu, 12 Oct 2023 13:26:25 +0200 Subject: [PATCH 06/27] [ENH] parallelization backend calls in utility module - part 3, backend parameter passing in base class broadcasting (#5405) This PR builds upon https://github.com/sktime/sktime/pull/5311 and adds a config parameter in base classes that allows passing of backend parameters via the `set_config` mechanism when forecasters or transformeres broadcast for hierarchical or multivariate data. The concomitant documentation will be added in https://github.com/sktime/sktime/pull/5306. From b0a6c156b1f8a1f20785b3c3cd5036f29b558090 Mon Sep 17 00:00:00 2001 From: Ansh Kumar <1928013@kiit.ac.in> Date: Thu, 12 Oct 2023 23:35:09 +0530 Subject: [PATCH 07/27] [DOC] update docstring of `temporal_train_test_split` (#4170) #### Reference Issues/PRs Fixes #4154 #### What does this implement/fix? Explain your changes. Updated the docstring of temporal_train_test_split for better understanding to the user. The previous docstring didn't mention all the accepted data types. Also previously it was unclear what **fh** does. --- .all-contributorsrc | 9 +++++++++ sktime/split/temporal_train_test_split.py | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index eaac5917150..64d1e20f60d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2415,5 +2415,14 @@ "doc" ] } + { + "login": "xansh", + "name": "Ansh Kumar", + "avatar_url": "https://avatars.githubusercontent.com/u/65403652?s=400&u=a45b5dcca057cfaef737d5fab99850aca6da1607&v=4", + "profile": "https://github.com/xansh", + "contributions": [ + "doc" + ] + }, ] } diff --git a/sktime/split/temporal_train_test_split.py b/sktime/split/temporal_train_test_split.py index 38189d123ea..2509b0ec9af 100644 --- a/sktime/split/temporal_train_test_split.py +++ b/sktime/split/temporal_train_test_split.py @@ -32,6 +32,9 @@ def temporal_train_test_split( ) -> SPLIT_TYPE: """Split time series data containers into a single train/test split. + Creates a single train/test split of endogenous time series ``y``, + an optionally exogeneous time series ``X``. + Splits time series ``y`` into a single temporally ordered train and test split. The split is based on ``test_size`` and ``train_size`` parameters, which can signify fractions of total number of indices, @@ -47,7 +50,9 @@ def temporal_train_test_split( Parameters ---------- y : time series in sktime compatible data container format + endogenous time series X : time series in sktime compatible data container format, optional, default=None + exogenous time series test_size : float, int or None, optional (default=None) If float, must be between 0.0 and 1.0, and is interpreted as the proportion of the dataset to include in the test split. Proportions are rounded to the From ce64c3362fe11d41d9100d4841c373b10de26de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Thu, 12 Oct 2023 20:12:29 +0200 Subject: [PATCH 08/27] [ENH] remove legacy except in `TestAllEstimators` for `predict_proba` (#5386) This removes the legacy except in `TestAllEstimators` for `predict_proba` which dates back to the time where it still depended on `tensorflow`. --- sktime/tests/test_all_estimators.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/sktime/tests/test_all_estimators.py b/sktime/tests/test_all_estimators.py index 166a7275e37..1a4dd708a17 100644 --- a/sktime/tests/test_all_estimators.py +++ b/sktime/tests/test_all_estimators.py @@ -53,10 +53,7 @@ from sktime.utils._testing.scenarios_getter import retrieve_scenarios from sktime.utils.random_state import set_random_state from sktime.utils.sampling import random_partition -from sktime.utils.validation._dependencies import ( - _check_dl_dependencies, - _check_soft_dependencies, -) +from sktime.utils.validation._dependencies import _check_soft_dependencies # whether to subsample estimators per os/version partition matrix design # default is False, can be set to True by pytest --matrixdesign True flag @@ -374,10 +371,8 @@ def _generate_method_nsc(self, test_name, **kwargs): # ensure cls is a class if "estimator_class" in kwargs.keys(): obj = kwargs["estimator_class"] - cls = obj elif "estimator_instance" in kwargs.keys(): obj = kwargs["estimator_instance"] - cls = type(obj) else: return [] @@ -387,12 +382,6 @@ def _generate_method_nsc(self, test_name, **kwargs): # subset to the methods that x has implemented nsc_list = [x for x in nsc_list if _has_capability(obj, x)] - # remove predict_proba for forecasters, if tensorflow-proba is not installed - # this ensures that predict_proba, which requires it, is not called in testing - if issubclass(cls, BaseForecaster): - if not _check_dl_dependencies(severity="none"): - nsc_list = list(set(nsc_list).difference(["predict_proba"])) - return nsc_list def _generate_method_nsc_arraylike(self, test_name, **kwargs): From bf7ba971d74499fd25c8c23e62d615c71257d7bb Mon Sep 17 00:00:00 2001 From: Adam Kells Date: Thu, 12 Oct 2023 22:05:27 +0100 Subject: [PATCH 09/27] [ENH] Refactor of `DateTimeFeatures` tests to `pytest` fixtures (#5397) #### Reference Issues/PRs Fixes #5155. #### What does this implement/fix? Explain your changes. This breaks apart the large if statement which creates the data objects for the tests into two classes consisting of pytest fixtures. These fixtures include the transformed data to be tested and the expected outputs. --- .all-contributorsrc | 9 + sktime/transformations/series/date.py | 2 +- .../transformations/series/tests/test_date.py | 322 +++++++++++------- 3 files changed, 215 insertions(+), 118 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 64d1e20f60d..2d051479219 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -2414,6 +2414,15 @@ "contributions": [ "doc" ] + }, + { + "login": "adamkells", + "name": "Adam Kells", + "avatar_url": "https://avatars.githubusercontent.com/u/19709277?v=4", + "profile": "https://github.com/adamkells", + "contributions": [ + "test" + ] } { "login": "xansh", diff --git a/sktime/transformations/series/date.py b/sktime/transformations/series/date.py index 772ad314c1b..412df68dfb0 100644 --- a/sktime/transformations/series/date.py +++ b/sktime/transformations/series/date.py @@ -34,7 +34,7 @@ class DateTimeFeatures(BaseTransformer): - """DateTime feature extraction for use in e.g. tree based models. + """DateTime feature extraction, e.g., for use as exogenous data in forecasting. DateTimeFeatures uses a date index column and generates date features identifying e.g. year, week of the year, day of the week. diff --git a/sktime/transformations/series/tests/test_date.py b/sktime/transformations/series/tests/test_date.py index cd89677d6e3..36d4710a7cb 100644 --- a/sktime/transformations/series/tests/test_date.py +++ b/sktime/transformations/series/tests/test_date.py @@ -13,33 +13,84 @@ from sktime.transformations.series.date import DateTimeFeatures from sktime.utils._testing.hierarchical import _make_hierarchical -if run_test_for_class(DateTimeFeatures): - # Load multivariate dataset longley and apply calendar extraction +@pytest.fixture +def load_split_data(): y, X = load_longley() y_train, y_test, X_train, X_test = temporal_train_test_split(y, X) + return X_train + +@pytest.fixture +def featurescope_step(load_split_data): # Test that comprehensive feature_scope works for weeks pipe = DateTimeFeatures( ts_freq="W", feature_scope="comprehensive", keep_original_columns=True ) - pipe.fit(X_train) - test_full_featurescope = pipe.transform(X_train).columns.to_list() + test_full_featurescope = pipe.fit_transform(load_split_data) + return test_full_featurescope + +@pytest.fixture +def featurescope_step_output(): + return [ + "GNPDEFL", + "GNP", + "UNEMP", + "ARMED", + "POP", + "year", + "quarter_of_year", + "month_of_year", + "week_of_year", + "month_of_quarter", + "week_of_quarter", + "week_of_month", + ] + + +@pytest.fixture +def reduced_featurescope_step(load_split_data): # Test that minimal feature_scope works for weeks pipe = DateTimeFeatures( ts_freq="W", feature_scope="minimal", keep_original_columns=True ) - pipe.fit(X_train) - test_reduced_featurescope = pipe.transform(X_train).columns.to_list() + test_reduced_featurescope = pipe.fit_transform(load_split_data) + return test_reduced_featurescope + +@pytest.fixture +def reduced_featurescope_step_output(): + return ["GNPDEFL", "GNP", "UNEMP", "ARMED", "POP", "year", "month_of_year"] + + +@pytest.fixture +def test_changing_frequency_step(load_split_data): # Test that comprehensive feature_scope works for months pipe = DateTimeFeatures( ts_freq="M", feature_scope="comprehensive", keep_original_columns=True ) - pipe.fit(X_train) - test_changing_frequency = pipe.transform(X_train).columns.to_list() + test_changing_frequency = pipe.fit_transform(load_split_data) + return test_changing_frequency + +@pytest.fixture +def test_changing_frequency_step_output(): + return [ + "GNPDEFL", + "GNP", + "UNEMP", + "ARMED", + "POP", + "year", + "quarter_of_year", + "month_of_year", + "month_of_quarter", + ] + + +@pytest.fixture +def test_manspec_with_tsfreq_step(load_split_data): # Test that manual_selection works for with provided arguments # Should ignore feature scope and raise warning for second_of_minute, # since ts_freq = "M" is provided. @@ -50,72 +101,71 @@ manual_selection=["year", "second_of_minute"], keep_original_columns=True, ) - pipe.fit(X_train) - test_manspec_with_tsfreq = pipe.transform(X_train).columns.to_list() + test_manspec_with_tsfreq = pipe.fit_transform(load_split_data) + return test_manspec_with_tsfreq - # Test that manual_selection works for with provided arguments - # Should ignore feature scope and raise no warning for second_of_minute, - # since ts_freq is not provided. - pipe = DateTimeFeatures( - manual_selection=["year", "second_of_minute"], keep_original_columns=True - ) - pipe.fit(X_train) - test_manspec_wo_tsfreq = pipe.transform(X_train).columns.to_list() +@pytest.fixture +def test_manspec_with_tsfreq_step_output(): + return ["GNPDEFL", "GNP", "UNEMP", "ARMED", "POP", "year", "second_of_minute"] - # Test that prior test works for with univariate dataset - y = load_airline() - y_train, y_test = temporal_train_test_split(y) +@pytest.fixture +def test_manspec_wo_tsfreq_step(load_split_data): + # Test that manual_selection works for with provided arguments + # Should ignore feature scope and raise no warning for second_of_minute, + # since ts_freq is not provided. pipe = DateTimeFeatures( manual_selection=["year", "second_of_minute"], keep_original_columns=True ) - pipe.fit(y_train) - test_univariate_data = pipe.transform(y_train).columns.to_list() + test_manspec_wo_tsfreq = pipe.fit_transform(load_split_data) + return test_manspec_wo_tsfreq - # Test that prior test also works when Index is converted to DateTime index - y.index = y.index.to_timestamp().astype("datetime64[ns]") - y_train, y_test = temporal_train_test_split(y) - pipe = DateTimeFeatures( - manual_selection=["year", "second_of_minute"], keep_original_columns=True - ) - pipe.fit(y_train) - test_diffdateformat = pipe.transform(y_train).columns.to_list() - pipe = DateTimeFeatures( - ts_freq="L", feature_scope="comprehensive", keep_original_columns=True - ) - pipe.fit(y_train) - y_train_t = pipe.transform(y_train) - test_full = y_train_t.columns.to_list() - test_types = y_train_t.select_dtypes(include=["int64"]).columns.to_list() - -else: - test_full_featurescope = [] - test_reduced_featurescope = [] - test_changing_frequency = [] - test_manspec_with_tsfreq = [] - test_manspec_wo_tsfreq = [] - test_univariate_data = [] - test_diffdateformat = [] - test_full = [] - test_types = [] - - -# Test `is_weekend` works in manual selection @pytest.fixture -def df_datetime_daily_idx(): - """Create timeseries with Datetime index, daily frequency.""" - return pd.DataFrame( - data={"y": [1, 1, 1, 1, 1, 1, 1]}, - index=pd.date_range(start="2000-01-01", freq="D", periods=7), - ) +def test_manspec_wo_tsfreq_step_output(): + return ["GNPDEFL", "GNP", "UNEMP", "ARMED", "POP", "year", "second_of_minute"] -@pytest.fixture() -def df_panel(): - """Create panel data of two time series using pd-multiindex mtype.""" - return _make_hierarchical(hierarchy_levels=(2,), min_timepoints=3, max_timepoints=3) +@pytest.mark.skipif( + not run_test_for_class(DateTimeFeatures), + reason="run test only if softdeps are present and incrementally (if requested)", +) +@pytest.mark.parametrize( + "test_input,expected", + [ + ( + "featurescope_step", + "featurescope_step_output", + ), + ( + "reduced_featurescope_step", + "reduced_featurescope_step_output", + ), + ( + "test_changing_frequency_step", + "test_changing_frequency_step_output", + ), + ( + "test_manspec_with_tsfreq_step", + "test_manspec_with_tsfreq_step_output", + ), + ( + "test_manspec_wo_tsfreq_step", + "test_manspec_wo_tsfreq_step_output", + ), + ], +) +def test_multivariate_eval(test_input, expected, request): + """Tests which columns are returned for different arguments. + + For a detailed description of what these arguments do, and how they interact, see + the docstring of DateTimeFeatures. + """ + test_input = request.getfixturevalue(test_input).columns.to_list() + expected = request.getfixturevalue(expected) + assert len(test_input) == len(expected) + assert all(a == b for a, b in zip(test_input, expected)) all_args = [ @@ -140,6 +190,70 @@ def df_panel(): ] +@pytest.fixture +def test_univariate_data_step(): + y = load_airline() + # Test that prior test works for with univariate dataset + y_train, y_test = temporal_train_test_split(y) + pipe = DateTimeFeatures( + manual_selection=["year", "second_of_minute"], keep_original_columns=True + ) + test_univariate_data = pipe.fit_transform(y_train) + return test_univariate_data + + +@pytest.fixture +def test_univariate_data_step_output(): + return ["Number of airline passengers", "year", "second_of_minute"] + + +@pytest.fixture() +def test_diffdateformat(): + y = load_airline() + # Test that prior test also works when Index is converted to DateTime index + y.index = y.index.to_timestamp().astype("datetime64[ns]") + y_train, y_test = temporal_train_test_split(y) + pipe = DateTimeFeatures( + manual_selection=["year", "second_of_minute"], keep_original_columns=True + ) + test_diffdateformat = pipe.fit_transform(y_train) + return test_diffdateformat + + +@pytest.fixture +def test_diffdateformat_output(): + return ["Number of airline passengers", "year", "second_of_minute"] + + +@pytest.fixture +def test_comprehensive_transform(): + y = load_airline() + # Test that prior test also works when Index is converted to DateTime index + y.index = y.index.to_timestamp().astype("datetime64[ns]") + y_train, y_test = temporal_train_test_split(y) + pipe = DateTimeFeatures( + ts_freq="L", feature_scope="comprehensive", keep_original_columns=True + ) + y_train_t = pipe.fit_transform(y_train) + return y_train_t + + +@pytest.fixture +def test_comprehensive_transform_output(): + return all_args + + +@pytest.fixture +def test_types(test_comprehensive_transform): + test_types = test_comprehensive_transform.select_dtypes(include=["int64"]) + return test_types + + +@pytest.fixture +def test_types_output(): + return all_args[1:] + + @pytest.mark.skipif( not run_test_for_class(DateTimeFeatures), reason="run test only if softdeps are present and incrementally (if requested)", @@ -148,74 +262,48 @@ def df_panel(): "test_input,expected", [ ( - test_full_featurescope, - [ - "GNPDEFL", - "GNP", - "UNEMP", - "ARMED", - "POP", - "year", - "quarter_of_year", - "month_of_year", - "week_of_year", - "month_of_quarter", - "week_of_quarter", - "week_of_month", - ], - ), - ( - test_reduced_featurescope, - ["GNPDEFL", "GNP", "UNEMP", "ARMED", "POP", "year", "month_of_year"], - ), - ( - test_changing_frequency, - [ - "GNPDEFL", - "GNP", - "UNEMP", - "ARMED", - "POP", - "year", - "quarter_of_year", - "month_of_year", - "month_of_quarter", - ], - ), - ( - test_manspec_with_tsfreq, - ["GNPDEFL", "GNP", "UNEMP", "ARMED", "POP", "year", "second_of_minute"], - ), - ( - test_manspec_wo_tsfreq, - ["GNPDEFL", "GNP", "UNEMP", "ARMED", "POP", "year", "second_of_minute"], + "test_univariate_data_step", + "test_univariate_data_step_output", ), ( - test_univariate_data, - ["Number of airline passengers", "year", "second_of_minute"], + "test_diffdateformat", + "test_diffdateformat_output", ), ( - test_diffdateformat, - ["Number of airline passengers", "year", "second_of_minute"], + "test_comprehensive_transform", + "test_comprehensive_transform_output", ), ( - test_full, - all_args, - ), - ( - test_types, - all_args[1:], + "test_types", + "test_types_output", ), ], ) -def test_eval(test_input, expected): +def test_uniivariate_eval(test_input, expected, request): """Tests which columns are returned for different arguments. - For a detailed description what these arguments do, and how they interact see - docstring of DateTimeFeatures. + For a detailed description of what these arguments do, and how they interact, see + the docstring of DateTimeFeatures. """ + test_input = request.getfixturevalue(test_input).columns.to_list() + expected = request.getfixturevalue(expected) assert len(test_input) == len(expected) - assert all([a == b for a, b in zip(test_input, expected)]) + assert all(a == b for a, b in zip(test_input, expected)) + + +@pytest.fixture +def df_datetime_daily_idx(): + """Create timeseries with Datetime index, daily frequency.""" + return pd.DataFrame( + data={"y": [1, 1, 1, 1, 1, 1, 1]}, + index=pd.date_range(start="2000-01-01", freq="D", periods=7), + ) + + +@pytest.fixture +def df_panel(): + """Create panel data of two time series using pd-multiindex mtype.""" + return _make_hierarchical(hierarchy_levels=(2,), min_timepoints=3, max_timepoints=3) @pytest.mark.skipif( From be58c6715c9948cd9e18ca3519dcd31787ba57bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Thu, 12 Oct 2023 23:20:28 +0200 Subject: [PATCH 10/27] Release 0.23.1 (#5402) Release PR for 0.23.1 * version number bump * changelog * additions to core developers on team page --- README.md | 2 +- docs/source/about/team.rst | 2 + docs/source/changelog.rst | 245 +++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- sktime/__init__.py | 2 +- 5 files changed, 250 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d25ce460dea..abc04b7388d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ > A unified interface for machine learning with time series -:rocket: **Version 0.23.0 out now!** [Check out the release notes here](https://www.sktime.net/en/latest/changelog.html). +:rocket: **Version 0.23.1 out now!** [Check out the release notes here](https://www.sktime.net/en/latest/changelog.html). sktime is a library for time series analysis in Python. It provides a unified interface for multiple time series learning tasks. Currently, this includes time series classification, regression, clustering, annotation, and forecasting. It comes with [time series algorithms](https://www.sktime.net/en/stable/estimator_overview.html) and [scikit-learn] compatible tools to build, tune and validate time series models. diff --git a/docs/source/about/team.rst b/docs/source/about/team.rst index d7811412f8c..dfe4aa25ebc 100644 --- a/docs/source/about/team.rst +++ b/docs/source/about/team.rst @@ -80,6 +80,8 @@ Core Developers - :user:`fkiraly` * - Freddy A Boulton - :user:`freddyaboulton` + * - Hazrul Akmal + - :user:`hazrulakmal` * - Jonathan Bechtel - :user:`jonathanbechtel` * - Kiril Ralinovski diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index b5713d6bfac..335b0fc7734 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -13,6 +13,251 @@ All notable changes to this project will be documented in this file. We keep tra For upcoming changes and next releases, see our `milestones `_. For our long-term plan, see our :ref:`roadmap`. + +Version 0.23.1 - 2023-10-12 +--------------------------- + +Highlights +~~~~~~~~~~ + +* all forecasters and transformers that broadcast can now use parallelization backends ``joblib``, ``dask`` via ``set_config`` (:pr:`5267`, :pr:`5268`, :pr:`5301`, :pr:`5311`, :pr:`5405`) :user:`fkiraly` +* ``PeakTimeFeatures`` transformer to generate indicator features for one or multiple peak hours, days, etc (:pr:`5191`) :user:`ali-parizad` +* ARCH forecaster interfacing ``arch`` package (:pr:`5326`) :user:`Vasudeva-bit` +* forecasting reducer ``YfromX`` now makes probabilistic forecasts when using ``skpro`` probabilistic tabular regressors (:pr:`5271`) :user:`fkiraly` +* forecasting compositors ``ForecastX`` now allows fitting ``forecaster_y`` on forecasted ``X`` (:pr:`5334`) :user:`benHeid` +* lucky dynamic time warping distance and aligner, for use in time series classifiers, regressors, clusterers (:pr:`5341`) :user:`fkiraly` +* splitters have now moved to their own module, ``sktime.split`` (:pr:`5017`) :user:`BensHamza` + +Dependency changes +~~~~~~~~~~~~~~~~~~ + +* ``attrs`` is no longer a soft dependency (time series annotation) of ``sktime`` +* ``arch`` is now a soft dependency (forecasting) of ``sktime`` +* ``skpro`` is now a soft dependency (forecasting) of ``sktime`` + +Core interface changes +~~~~~~~~~~~~~~~~~~~~~~ + +BaseObject and base framework +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* the ``sktime`` framework now inspects estimator type primarily via the tag ``object_type``. + This is not a breaking change as inheriting from respective base classes automatically sets the tag as well, + via the tag inheritance system. The type inspection utility ``scitype`` is also unaffected. + For extenders, the change enables polymorphic and dynamically typed estimators. +* warnings from ``sktime`` can now be silenced on a per-estimator basis via + the ``warnings`` config that can be set via ``set_config`` (see docstring). + +Forecasting +^^^^^^^^^^^ + +* hierarchical and multivariate forecasts can now use parallelization and distributed backends, + including ``joblib`` and ``dask``, if the forecast is obtained via broadcasting. + To enable parallelization, set the ``backend:parallel`` and/or the ``backend:parallel:params`` + configuration flags via ``set_config`` (see docstring) before fitting the forecaster. + This change instantaneously extends to all existing third party forecasters + that are interface conformant, via inheritance from the updated base framework. + +Time series regression +^^^^^^^^^^^^^^^^^^^^^^ + +* time series regressors now allow single-column ``pd.DataFrame`` as ``y``. + Current behaviour is unaffected, this is not a breaking change for existing code. + +Transformations +^^^^^^^^^^^^^^^ + +* hierarchical and multivariate transformers can now use parallelization and distributed backends, + including ``joblib`` and ``dask``, if the transformation is obtained via broadcasting. + To enable parallelization, set the ``backend:parallel`` and/or the ``backend:parallel:params`` + configuration flags via ``set_config`` (see docstring) before fitting the transformer. + This change instantaneously extends to all existing third party transformers + that are interface conformant, via inheritance from the updated base framework. + +Deprecations and removals +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Benchmarking, Metrics, Splitters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* time series splitters, i.e., descendants of ``BaseSplitter``, have moved from + ``sktime.forecasting.model_selection`` to ``sktime.`split``. + The old location ``model_selection`` is deprecated and will be removed in 0.25.0. + Until 0.25.0, it is still available but will raise an informative warning message. + +Enhancements +~~~~~~~~~~~~ + +BaseObject and base framework +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* [ENH] warnings config (:pr:`4536`) :user:`fkiraly` +* [ENH] add exports of common utilities in ``utils`` module (:pr:`5266`) :user:`fkiraly` +* [ENH] in scitype check, replace base class register logic with type tag inspection (:pr:`5288`) :user:`fkiraly` +* [ENH] parallelization backend calls in utility module - part 1, refactor to utility module (:pr:`5268`) :user:`fkiraly` +* [ENH] parallelization backend calls in utility module - part 2, backend parameter passing (:pr:`5311`) :user:`fkiraly` +* [ENH] parallelization backend calls in utility module - part 3, backend parameter passing in base class broadcasting (:pr:`5405`) :user:`fkiraly` + +Benchmarking, Metrics, Splitters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* [ENH] consolidating splitters as their own module with systematic tests and extension (:pr:`5017`, :pr:`5331`) :user:`BensHamza`, :user:`fkiraly` +* [ENH] allow ``evaluate`` to accept any combination of multiple metrics with correct predict method (:pr:`5192`) :user:`hazrulakmal` +* [ENH] add tests for ``temporal_train_test_split`` (:pr:`5332`) :user:`fkiraly` + +Data loaders +^^^^^^^^^^^^ + +* [ENH] dataset loaders module restructure (:pr:`5239`) :user:`hazrulakmal` + +Forecasting +^^^^^^^^^^^ + +* [ENH] Add a ``CurveFitForecaster`` based on ``scipy`` ``optimize_curve`` (:pr:`5240`) :user:`benHeid` +* [ENH] Restructure the ``trend`` forecasters module (:pr:`5242`) :user:`benHeid` +* [ENH] ``YfromX`` - probabilistic forecasts (:pr:`5271`) :user:`fkiraly` +* [ENH] Link ``test_interval_wrappers.py`` to changes in ``evaluate`` for conditional testing (:pr:`5337`) :user:`fkiraly` +* [ENH] ``joblib`` and ``dask`` backends in broadcasting of estimators in multivariate or hierarchical case - part 1, ``VectorizedDF.vectorize_est`` (:pr:`5267`) :user:`fkiraly` +* [ENH] ``joblib`` and ``dask`` backends in broadcasting of estimators in multivariate or hierarchical case - part 2, base class config (:pr:`5301`) :user:`fkiraly` +* [ENH] ARCH model interfacing ``arch`` package (:pr:`5326`) :user:`Vasudeva-bit` +* [ENH] in ``ForecastX``, enable fitting ``forecaster_y`` on forecasted ``X`` (:pr:`5334`) :user:`benHeid` +* [ENH] Skip unnecessary fit in ``ForecastX`` if inner ``forecaster_y`` ignores ``X`` (:pr:`5353`) :user:`yarnabrina` +* [ENH] remove legacy except in ``TestAllEstimators`` for ``predict_proba`` (:pr:`5386`) :user:`fkiraly` + +Time series alignment +^^^^^^^^^^^^^^^^^^^^^ + +* [ENH] lucky dynamic time warping aligner (:pr:`5341`) :user:`fkiraly` +* [ENH] sensible default ``_get_distance_matrix`` for time series aligners (:pr:`5347`) :user:`fkiraly` + +Time series distances and kernels +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* [ENH] delegator for pairwise time series distances and kernels (:pr:`5340`) :user:`fkiraly` +* [ENH] lucky dynamic time warping distance (:pr:`5341`) :user:`fkiraly` +* [ENH] simplified delegator interface to ``dtw-python`` based dynamic time warping distances (:pr:`5348`) :user:`fkiraly` + +Time series regression +^^^^^^^^^^^^^^^^^^^^^^ + +* [ENH] in ``BaseRegressor``, allow ``y`` to be 1D ``pd.DataFrame`` (:pr:`5282`) :user:`mdsaad2305` + +Transformations +^^^^^^^^^^^^^^^ + +* [ENH] ``PeakTimeFeatures`` transformer to generate indicator features for one/multiple peak/hours-day-week-, working hours, etc (:pr:`5191`) :user:`ali-parizad` +* [ENH] ``VmdTransformer``, add decompose-forecast-recompose as a docstring example and test (:pr:`5250`) :user:`fkiraly`* [ENH] improve ``evaluate`` failure error message (:pr:`5269`) :user:`fkiraly` +* [ENH] add proper ``inverse_transform`` to ``STLTransformer`` (:pr:`5300`) :user:`fkiraly` +* [ENH] ``joblib`` and ``dask`` backends in broadcasting of estimators in multivariate or hierarchical case - part 1, ``VectorizedDF.vectorize_est`` (:pr:`5267`) :user:`fkiraly` +* [ENH] ``joblib`` and ``dask`` backends in broadcasting of estimators in multivariate or hierarchical case - part 2, base class config (:pr:`5301`) :user:`fkiraly` +* [ENH] Refactor of `DateTimeFeatures` tests to `pytest` fixtures (:pr:`5397`) :user:`adamkells` + +Testing framework +^^^^^^^^^^^^^^^^^ + +* [ENH] add error message return to ``deep_equals`` assert in ``test_reconstruct_identical`` (:pr:`4927`) :user:`fkiraly` +* [ENH] incremental testing to also test if any parent class in sktime has changed (:pr:`5379`) :user:`fkiraly` + +Maintenance +~~~~~~~~~~~ + +* [MNT] revert update numba requirement from <0.58,>=0.53 to >=0.53,<0.59" (:pr:`5297`) :user:`fkiraly` +* [MNT] bound ``numba<0.58`` (:pr:`5303`) :user:`fkiraly` +* [MNT] Remove ``attrs`` dependency (:pr:`5296`) :user:`Alex-JG3` +* [MNT] simplified CI - merge windows CI step with test matrix (:pr:`5362`) :user:`fkiraly` +* [MNT] towards 3.12 compatibility - replace ``distutils`` calls with equivalent functionality (:pr:`5376`) :user:`fkiraly` +* [MNT] ``skpro`` as a soft dependency (:pr:`5273`) :user:`fkiraly` +* [MNT] removed ``py37.dockerfile`` and update doc entry for CI (:pr:`5356`) :user:`kurayami07734` +* [MNT] [Dependabot](deps): Bump styfle/cancel-workflow-action from 0.11.0 to 0.12.0 (:pr:`5355`) :user:`dependabot[bot]` +* [MNT] [Dependabot](deps): Bump stefanzweifel/git-auto-commit-action from 4 to 5 (:pr:`5373`) :user:`dependabot[bot]` +* [MNT] [Dependabot](deps-dev): Update holidays requirement from <0.33,>=0.29 to >=0.29,<0.34 (:pr:`5276`) :user:`dependabot[bot]` +* [MNT] [Dependabot](deps-dev): Update numpy requirement from <1.26,>=1.21.0 to >=1.21.0,<1.27 (:pr:`5275`) :user:`dependabot[bot]` +* [MNT] [Dependabot](deps-dev): Update arch requirement from <6.2.0,>=5.6.0 to >=5.6.0,<6.3.0 (:pr:`5392`) :user:`dependabot[bot]` + +Documentation +~~~~~~~~~~~~~ + +* [DOC] prevent line break in ``README.md`` badges table (:pr:`5263`) :user:`fkiraly` +* [DOC] forecasting extension template - add insample capability tags (:pr:`5272`) :user:`fkiraly` +* [DOC] add ``blog`` badge for ``fkiraly``, for ODSC blog post (:pr:`5291`) :user:`fkiraly` +* [DOC] speed improvement of ``partition_based_clustering`` notebook (:pr:`5278`) :user:`alexfilothodoros` +* [DOC] Documented ax argument and the figure in plot_series (:pr:`5325`) :user:`ShreeshaM07` +* [DOC] Improve Readability of Notebook 2 - Classification, Regression & Clustering (:pr:`5312`) :user:`achieveordie` +* [DOC] Added all feature names to docstring for DateTimeFeatures class (:pr:`5283`) :user:`Abhay-Lejith` +* [DOC] ``sktime`` intro notebook (:pr:`3793`) :user:`fkiraly` +* [DOC] Correct code block formatting for pre-commit install command (:pr:`5377`) :user:`alhridoy` +* [DOC] fix broken docstring example of ``AlignerDtwNumba`` (:pr:`5374`) :user:`fkiraly` +* [DOC] fix typo in classification notebook (:pr:`5390`) :user:`pirnerjonas` +* [DOC] Improved PR template for new contributors (:pr:`5381`) :user:`fkiraly` +* [DOC] dynamic docstring for ``set_config`` (:pr:`5306`) :user:`fkiraly` +* [DOC] update docstring of ``temporal_train_test_split`` (:pr:`4170`) :user:`xansh` +* [DOC] Document ``ax`` argument and the figure in ``plot_series`` (:pr:`5325`) :user:`ShreeshaM07` + +Fixes +~~~~~ + +Benchmarking, Metrics, Splitters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* [BUG] fix ``temporal_train_test_split`` for hierarchical and panel data in case where ``fh`` is not passed (:pr:`5330`) :user:`fkiraly` +* [BUG] allow ``alpha`` and ``coverage`` to be passed again via metrics to ``evaluate`` (:pr:`5354`) :user:`fkiraly`, :user:`benheid` + +Forecasting +^^^^^^^^^^^ + +* [BUG] fix ``STLForecaster`` tag ``ignores-exogenous-X`` to be correctly set for composites (:pr:`5365`) :user:`yarnabrina` +* [BUG] ``statsforecast 1.6.0`` compatibility - in ``statsforecast`` adapter, fixing ``RuntimeError: dictionary changed size during iteration`` (:pr:`5317`) :user:`arnaujc91` +* [BUG] ``statsforecast 1.6.0`` compatibility - fix argument differences between ``sktime`` and ``statsforecast`` (:pr:`5393`) :user:`luca-miniati` +* [BUG] Fix ``ARCH._check_predict_proba`` (:pr:`5384`) :user:`Vasudeva-bit` + +Time series alignment +^^^^^^^^^^^^^^^^^^^^^ + +* [BUG] minor fixes to ``NaiveAligner`` (:pr:`5344`) :user:`fkiraly` + +Time series distances and kernels +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* [BUG] Fix ``numba`` errors when calling ``tslearn`` ``lcss`` (:pr:`5368`) :user:`benHeid`, :user:`BensHamza`, :user:`fkiraly` + + +Transformations +^^^^^^^^^^^^^^^ + +* [BUG] in ``Imputer``, fix ``y`` not being passed in ``method="forecaster"`` (:pr:`5287`) :user:`fkiraly` +* [BUG] ensure ``Catch22`` parameter setting ``n_jobs = -1`` uses all cores (:pr:`5361`) :user:`julnow` + +Visualization +^^^^^^^^^^^^^ + +* [BUG] Fix inconsistent date/time index in ``plot_windows`` #4919 (:pr:`5321`) :user:`geronimos` + +Contributors +~~~~~~~~~~~~ + +:user:`Abhay-Lejith`, +:user:`achieveordie`, +:user:`adamkells`, +:user:`Alex-JG3`, +:user:`alexfilothodoros`, +:user:`alhridoy`, +:user:`ali-parizad`, +:user:`arnaujc91`, +:user:`benHeid`, +:user:`BensHamza`, +:user:`fkiraly`, +:user:`geronimos`, +:user:`hazrulakmal`, +:user:`julnow`, +:user:`kurayami07734`, +:user:`luca-miniati`, +:user:`mdsaad2305`, +:user:`pirnerjonas`, +:user:`ShreeshaM07`, +:user:`Vasudeva-bit`, +:user:`xansh`, +:user:`yarnabrina` + Version 0.23.0 - 2023-09-17 --------------------------- diff --git a/pyproject.toml b/pyproject.toml index ca1b7545d3c..2690a82610e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sktime" -version = "0.23.0" +version = "0.23.1" description = "A unified framework for machine learning with time series" authors = [ {name = "sktime developers", email = "sktime.toolbox@gmail.com"}, diff --git a/sktime/__init__.py b/sktime/__init__.py index 819489328de..269ba7b7310 100644 --- a/sktime/__init__.py +++ b/sktime/__init__.py @@ -1,6 +1,6 @@ """sktime.""" -__version__ = "0.23.0" +__version__ = "0.23.1" __all__ = ["show_versions"] From 211afe77b89c0ee4c5413ceb47986a497497329e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Thu, 12 Oct 2023 23:26:24 +0200 Subject: [PATCH 11/27] Release 0.23.1 - omitted changelog commit (#5413) Adds an omitted changelog commit to release 0.23.1 --- docs/source/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 335b0fc7734..5b2374816c3 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -20,7 +20,7 @@ Version 0.23.1 - 2023-10-12 Highlights ~~~~~~~~~~ -* all forecasters and transformers that broadcast can now use parallelization backends ``joblib``, ``dask`` via ``set_config`` (:pr:`5267`, :pr:`5268`, :pr:`5301`, :pr:`5311`, :pr:`5405`) :user:`fkiraly` +* all hierarchical/multivariate forecaster and transformer broadcasting can now use parallelization backends ``joblib``, ``dask`` via ``set_config`` (:pr:`5267`, :pr:`5268`, :pr:`5301`, :pr:`5311`, :pr:`5405`) :user:`fkiraly` * ``PeakTimeFeatures`` transformer to generate indicator features for one or multiple peak hours, days, etc (:pr:`5191`) :user:`ali-parizad` * ARCH forecaster interfacing ``arch`` package (:pr:`5326`) :user:`Vasudeva-bit` * forecasting reducer ``YfromX`` now makes probabilistic forecasts when using ``skpro`` probabilistic tabular regressors (:pr:`5271`) :user:`fkiraly` From 8b8cea03b084833776d5b6009a8f9a899a1c3dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 15:47:42 +0200 Subject: [PATCH 12/27] [MNT] instant release action (#5265) This PR changes the release action to an instant release. This might also be useful for changing the current workflow to one where all tests are run manually, or in CRON batches. Reverts sktime/sktime#5264. Same as https://github.com/sktime/sktime/pull/5262 (was temporary) --- .github/workflows/wheels.yml | 104 +---------------------------------- 1 file changed, 1 insertion(+), 103 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 32618779298..b63549a1ea0 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -27,112 +27,10 @@ jobs: name: wheels path: wheelhouse/* - test_unix_wheels: - needs: build_wheels - name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false # to not fail all combinations if just one fail - matrix: - os: [ubuntu-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11'] - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - uses: actions/download-artifact@v3 - with: - name: wheels - path: wheelhouse - - - name: Get wheel filename - run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $GITHUB_ENV - - - name: Install wheel and extras - run: python -m pip install "${{ env.WHEELNAME }}[all_extras_pandas2,dev]" - - - name: Run tests - run: make test - - test_windows_wheels: - needs: build_wheels - name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} - runs-on: windows-latest - strategy: - fail-fast: false # to not fail all combinations if just one fail - matrix: - include: - # Window 64 bit - - os: windows-latest - python: 38 - python-version: '3.8' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 39 - python-version: '3.9' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 310 - python-version: '3.10' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 311 - python-version: '3.11' - bitness: 64 - platform_id: win_amd64 - - steps: - - uses: actions/checkout@v4 - - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: test - auto-update-conda: true - python-version: ${{ matrix.python-version }} - channels: anaconda, conda-forge, - - - run: conda --version - - run: which python - - - uses: actions/download-artifact@v3 - with: - name: wheels - path: wheelhouse - - - name: Install conda libpython - run: conda install -c anaconda -n test -y libpython - - - name: Display downloaded artifacts - run: ls -l wheelhouse - - - name: Get wheel filename - run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $env:GITHUB_ENV - - - name: Activate conda env - run: conda activate test - - - name: Install wheel and extras - run: python -m pip install "${env:WHEELNAME}[all_extras_pandas2,dev]" - - - name: Show conda packages - run: conda list -n test - - - name: Run tests - run: | - mkdir -p testdir/ - cp .coveragerc testdir/ - cp setup.cfg testdir/ - python -m pytest - upload_wheels: name: Upload wheels to PyPI runs-on: ubuntu-latest - needs: [build_wheels,test_unix_wheels,test_windows_wheels] + needs: build_wheels steps: - uses: actions/download-artifact@v3 From a6d61ecd1346c292bf793ce39f0fef8bd7be546d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 15:53:27 +0200 Subject: [PATCH 13/27] [MNT] update numba requirement from <0.58,>=0.53 to >=0.53,<0.59"" (#5299) Bumps the `numba` version bound to `<0.59`, allowing `0.58`. This should be merged only if https://github.com/sktime/sktime/issues/5298 is resolved, or it will cause failures on `main`. The tests are passing since no estimators are changed, instead the tests should be run on all estimator to ascertain compatibility. --- pyproject.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2690a82610e..6fd8abf67f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,22 +55,22 @@ dependencies = [ [project.optional-dependencies] alignment = [ "dtw-python>=1.3,<1.4", - "numba>=0.53,<0.58", + "numba>=0.53,<0.59", ] annotation = [ "hmmlearn>=0.2.7,<0.4", - "numba>=0.53,<0.58", + "numba>=0.53,<0.59", "pyod>=0.8.0,<1.2", ] classification = [ "esig>=0.9.7,<0.10", "mrsqm>=0.0.3,<0.1", - "numba>=0.53,<0.58", + "numba>=0.53,<0.59", "tensorflow>=2,<=2.14", "tsfresh>=0.17,<0.21", ] clustering = [ - "numba>=0.53,<0.58", + "numba>=0.53,<0.59", "tslearn>=0.5.2,<0.6.3", ] forecasting = [ @@ -91,7 +91,7 @@ param_est = [ "statsmodels>=0.12.1,<0.15", ] regression = [ - "numba>=0.53,<0.58", + "numba>=0.53,<0.59", "tensorflow>=2,<=2.14", ] transformations = [ @@ -99,7 +99,7 @@ transformations = [ "filterpy>=1.4.5,<1.5", "holidays>=0.29,<0.34", "mne>=1.5,<1.6", - "numba>=0.53,<0.58", + "numba>=0.53,<0.59", "pycatch22>=0.4,<0.5", "pykalman>=0.9.5,<0.10", "statsmodels>=0.12.1,<0.15", From c5d01106b7e6db2fc88bfa689cc6133f14b9b140 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:53:57 +0200 Subject: [PATCH 14/27] [MNT] [Dependabot](deps-dev): Update numba requirement from <0.58,>=0.53 to >=0.53,<0.59 (#5319) Updates the requirements on [numba](https://github.com/numba/numba) to permit the latest version. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6fd8abf67f0..0d1c14d8a25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -182,7 +182,7 @@ all_extras_pandas2 = [ cython_extras = [ "mrseql", "mrsqm; python_version < '3.11'", - "numba<0.58", + "numba<0.59", ] dev = [ From fb38342b2cc6ac4de03a2adfc803678e71f33e87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:54:14 +0200 Subject: [PATCH 15/27] [MNT] [Dependabot](deps-dev): Update skpro requirement from <2.1.0,>=2.0.0 to >=2.0.0,<2.2.0 (#5396) Updates the requirements on [skpro](https://github.com/sktime/skpro) to permit the latest version. --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0d1c14d8a25..9d750c69ba1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ clustering = [ forecasting = [ "pmdarima>=1.8,!=1.8.1,<2.1", "prophet>=1.1,<1.2", - "skpro>=2.0.0,<2.1.0", + "skpro>=2.0.0,<2.2.0", "statsforecast>=0.5.2,<1.7", "statsmodels>=0.12.1,<0.15", "tbats>=1.1,<1.2", @@ -131,7 +131,7 @@ all_extras = [ "scikit_posthocs>=0.6.5", "seaborn>=0.11.0", "seasonal", - "skpro>=2.0.0,<2.1.0", + "skpro>=2.0.0,<2.2.0", "statsforecast>=0.5.2,<1.7.0", "statsmodels>=0.12.1", "stumpy>=1.5.1; python_version < '3.11'", @@ -167,7 +167,7 @@ all_extras_pandas2 = [ "scikit_posthocs>=0.6.5", "seaborn>=0.11.0", "seasonal", - "skpro>=2.0.0,<2.1.0", + "skpro>=2.0.0,<2.2.0", "statsforecast>=0.5.2,<1.7.0", "statsmodels>=0.12.1", "stumpy>=1.5.1; python_version < '3.11'", From 5f7012610efa587f73948f3de092ce35e7d778d3 Mon Sep 17 00:00:00 2001 From: Maksym Balatsko Date: Fri, 13 Oct 2023 15:56:08 +0200 Subject: [PATCH 16/27] [MNT] Migrate from `pykalman` to `pykalman-bardo` (#5277) Consider migration from `pykalman` to `pykalman-bardo`, as [pykalman](https://github.com/pykalman/pykalman) project is no longer maintained. There were some issues that were fixed, see: https://github.com/pybardo/pykalman/blob/v0.9.6/CHANGELOG I'm going to maintain [pykalman-bardo](https://github.com/pybardo/pykalman), react to issues, and fix bugs. For now the API is the same as in the initial package, but it might evolve in time. I hope you will find it useful for your project! #### Reference Issues/PRs `pykalman` compatibility with python 3.11 #### Does your contribution introduce a new dependency? If yes, which one? `pykalman`-> `pykalman-bardo` --- pyproject.toml | 6 +- .../transformations/series/kalman_filter.py | 59 ++++++++++++------- .../series/tests/test_kalman_filter.py | 45 ++++++++++---- 3 files changed, 73 insertions(+), 37 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9d750c69ba1..ed2ea95767b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,7 +101,7 @@ transformations = [ "mne>=1.5,<1.6", "numba>=0.53,<0.59", "pycatch22>=0.4,<0.5", - "pykalman>=0.9.5,<0.10", + "pykalman-bardo>=0.9.7,<0.10", "statsmodels>=0.12.1,<0.15", "stumpy>=1.5.1,<1.13", "tsfresh>=0.17,<0.21", @@ -125,7 +125,7 @@ all_extras = [ "pmdarima>=1.8.0,!=1.8.1,<3.0.0", "prophet>=1.1", "pycatch22", - "pykalman>=0.9.5; python_version < '3.11'", + "pykalman-bardo>=0.9.7,<0.10", "pyod>=0.8.0; python_version < '3.11'", "scikit-optimize", "scikit_posthocs>=0.6.5", @@ -162,7 +162,7 @@ all_extras_pandas2 = [ "pmdarima>=1.8.0,!=1.8.1,<3.0.0", "prophet>=1.1", "pycatch22", - "pykalman>=0.9.5; python_version < '3.11'", + "pykalman-bardo>=0.9.7,<0.10", "pyod>=0.8.0; python_version < '3.11'", "scikit_posthocs>=0.6.5", "seaborn>=0.11.0", diff --git a/sktime/transformations/series/kalman_filter.py b/sktime/transformations/series/kalman_filter.py index 91ac5bbce98..360e5069eb7 100644 --- a/sktime/transformations/series/kalman_filter.py +++ b/sktime/transformations/series/kalman_filter.py @@ -2,7 +2,7 @@ """Kalman Filter Transformers. Series based transformers, based on Kalman Filter algorithm. Contains Base class and two -transformers which are each Adapters for external packages pykalman and FilterPy. +transformers which are each Adapters for external packages pykalman-bardo and FilterPy. """ __author__ = ["NoaBenAmi", "lielleravid"] @@ -107,7 +107,9 @@ def _init_matrix(matrices, transform_func, default_val): return transform_func(matrices) -def _check_conditional_dependency(obj, condition, package, severity, msg=None): +def _check_conditional_dependency( + obj, condition, package, severity, package_import_alias=None, msg=None +): """If `condition` applies, check the soft dependency `package` installation. Call _check_soft_dependencies. @@ -122,6 +124,9 @@ def _check_conditional_dependency(obj, condition, package, severity, msg=None): Error message to attach to ModuleNotFoundError. package : str Package name for soft dependency check. + package_import_alias : dict with str keys and values or None, optional, default=None + import name is str used in python import, i.e., from import_name import ... + should be provided if import name differs from package name severity : str 'error' or 'warning'. @@ -139,7 +144,12 @@ def _check_conditional_dependency(obj, condition, package, severity, msg=None): f"install the `{package}` package. " ) try: - _check_soft_dependencies(package, severity=severity, obj=obj) + _check_soft_dependencies( + package, + package_import_alias=package_import_alias, + severity=severity, + obj=obj, + ) except ModuleNotFoundError as e: raise ModuleNotFoundError(msg) from e @@ -392,12 +402,12 @@ class KalmanFilterTransformerPK(BaseKalmanFilter, BaseTransformer): an estimate of the state of a process. - This class is the adapter for the `pykalman` package into `sktime`. + This class is the adapter for the `pykalman-bardo` package into `sktime`. `KalmanFilterTransformerPK` implements hidden inferred states and denoising, depending on the boolean input parameter `denoising`. In addition, `KalmanFilterTransformerPK` provides parameter optimization via Expectation-Maximization (EM) algorithm [2]_, - implemented by `pykalman`. + implemented by `pykalman-bardo`. Parameters ---------- @@ -431,21 +441,21 @@ class KalmanFilterTransformerPK(BaseKalmanFilter, BaseTransformer): Initial estimated system state covariance, also referred to as `P0`. transition_offsets : np.ndarray, optional (default=None) of shape (state_dim,) or (time_steps, state_dim). - State offsets, also referred to as `b`, as described in `pykalman`. + State offsets, also referred to as `b`, as described in `pykalman-bardo`. measurement_offsets : np.ndarray, optional (default=None) of shape (measurement_dim,) or (time_steps, measurement_dim). Observation (measurement) offsets, also referred to as `d`, - as described in `pykalman`. + as described in `pykalman-bardo`. denoising : bool, optional (default=False). This parameter affects `transform`. If False, then `transform` will be inferring - hidden state. If True, uses `pykalman` `smooth` for denoising. + hidden state. If True, uses `pykalman-bardo` `smooth` for denoising. estimate_matrices : str or list of str, optional (default=None). Subset of [`state_transition`, `measurement_function`, `process_noise`, `measurement_noise`, `initial_state`, `initial_state_covariance`, `transition_offsets`, `measurement_offsets`] or - `all`. If `estimate_matrices` is an iterable of strings, only matrices in `estimate_matrices` will be estimated using EM algorithm, - like described in `pykalman`. If `estimate_matrices` is `all`, + like described in `pykalman-bardo`. If `estimate_matrices` is `all`, then all matrices will be estimated using EM algorithm. Note - parameters estimated by EM algorithm assumed to be constant. @@ -457,7 +467,7 @@ class KalmanFilterTransformerPK(BaseKalmanFilter, BaseTransformer): Notes ----- - `pykalman` KalmanFilter documentation : + `pykalman-bardo` KalmanFilter documentation : https://pykalman.github.io/#kalmanfilter References @@ -532,7 +542,8 @@ class KalmanFilterTransformerPK(BaseKalmanFilter, BaseTransformer): "capability:missing_values:removes": False, # is transform result always guaranteed to contain no missing values? "scitype:instancewise": True, # is this an instance-wise transform? - "python_dependencies": "pykalman", + "python_dependencies": "pykalman-bardo", + "python_dependencies_alias": {"pykalman-bardo": "pykalman"}, } def __init__( @@ -683,8 +694,9 @@ def _transform(self, X, y=None): This method performs the transformation of the input data according to the constructor input parameter `denoising`. - If `denoising` is True - then denoise data using `pykalman`'s `smooth` function. - Else, infer hidden state using `pykalman`'s `filter` function. + If `denoising` is True - then denoise data using + `pykalman-bardo`'s `smooth` function. + Else, infer hidden state using `pykalman-bardo`'s `filter` function. Parameters ---------- @@ -745,7 +757,7 @@ def _em(self, X, measurement_dim, state_dim): """Estimate matrices algorithm if requested by user. If input matrices are specified in `estimate_matrices`, - this method will use the `pykalman` EM algorithm function + this method will use the `pykalman-bardo` EM algorithm function to estimate said matrices needed to calculate the Kalman Filter. Algorithm explained in References[2]. If `estimate_matrices` is None no matrices will be estimated. @@ -798,7 +810,7 @@ def _em(self, X, measurement_dim, state_dim): return F, H, Q, R, transition_offsets, measurement_offsets, X0, P0 def _get_estimate_matrices(self): - """Map parameter names to `pykalman` names for use of `pykalman` `em`. + """Map parameter names to `pykalman-bardo` names for use of `em`. Returns ------- @@ -910,7 +922,8 @@ class KalmanFilterTransformerFP(BaseKalmanFilter, BaseTransformer): See Also -------- KalmanFilterTransformerPK : - Kalman Filter transformer, adapter for the `pykalman` package into `sktime`. + Kalman Filter transformer, adapter for the `pykalman-bardo` package + into `sktime`. Notes ----- @@ -1047,22 +1060,24 @@ def _fit(self, X, y=None): self: reference to self """ # The below call to `_check_conditional_dependency` checks the installation - # of `pykalman` package, if needed. `pykalman` is used when the user requires - # matrices estimation (`estimate_matrices` is not None). + # of `pykalman-bardo` package, if needed. `pykalman-bardo` + # is used when the user requires matrices estimation + # (`estimate_matrices` is not None). # This conditioned dependency check can be performed in # `__init__` for early user feedback. _check_conditional_dependency( obj=self, condition=(self.estimate_matrices is not None), - package="pykalman", + package="pykalman-bardo", + package_import_alias={"pykalman-bardo": "pykalman"}, severity="error", msg=( f"{self.__class__.__name__}'s matrix parameter estimation " f"is performed when `estimate_matrices` " f"is {self.estimate_matrices}, " - f"and requires `pykalman` installed. Please run: " - f"`pip install pykalman` to " - f"install the `pykalman` package. " + f"and requires `pykalman-bardo` installed. Please run: " + f"`pip install pykalman-bardo` to " + f"install the `pykalman-bardo` package. " ), ) diff --git a/sktime/transformations/series/tests/test_kalman_filter.py b/sktime/transformations/series/tests/test_kalman_filter.py index bd00f6fd7be..f22ed2528d2 100644 --- a/sktime/transformations/series/tests/test_kalman_filter.py +++ b/sktime/transformations/series/tests/test_kalman_filter.py @@ -117,7 +117,7 @@ def get_params_mapping(params): """Transform parameters names. From KalmanFilterTransformerPK, KalmanFilterTransformerFP naming forms to - `pykalman`'s naming form. + `pykalman-bardo`'s naming form. """ params_mapping = { "state_transition": "transition_matrices", @@ -150,7 +150,7 @@ def init_kf_pykalman( estimate_matrices=None, denoising=False, ): - """Initiate instance of `pykalman`'s `KalmanFilter`.""" + """Initiate instance of `pykalman-bardo`'s `KalmanFilter`.""" from pykalman.standard import KalmanFilter em_vars = get_params_mapping(params=estimate_matrices) @@ -200,8 +200,12 @@ def init_kf_filterpy(measurements, adapter, n=10, y=None): @pytest.mark.skipif( - not _check_soft_dependencies("pykalman", severity="none"), - reason="skip test if required soft dependency pykalman not available", + not _check_soft_dependencies( + "pykalman-bardo", + package_import_alias={"pykalman-bardo": "pykalman"}, + severity="none", + ), + reason="skip test if required soft dependency pykalman-bardo not available", ) @pytest.mark.parametrize( "params, measurements", @@ -316,7 +320,7 @@ def test_transform_and_smooth_pk(params, measurements): Creating two instances of KalmanFilterTransformerPK, one instance with parameter `denoising` set to False, and the other's set to True. Compare result with - `pykalman`'s `filter` and `smooth`. + `pykalman-bardo`'s `filter` and `smooth`. """ mask_measurements = np.ma.masked_invalid(np.copy(measurements)) @@ -345,8 +349,16 @@ def test_transform_and_smooth_pk(params, measurements): @pytest.mark.skipif( - not _check_soft_dependencies("pykalman", "filterpy", severity="none"), - reason="skip test if required soft dependencies pykalman, filterpy not available", + not _check_soft_dependencies( + "pykalman-bardo", + "filterpy", + package_import_alias={"pykalman-bardo": "pykalman"}, + severity="none", + ), + reason=( + "skip test if required soft dependencies pykalman-bardo, " + "filterpy not available" + ), ) @pytest.mark.parametrize( "classes, params, measurements", @@ -548,9 +560,10 @@ def test_transform_and_smooth_pk(params, measurements): def test_em(classes, params, measurements): """Test adapters matrix estimation. - Call `fit` of input adapter/s, and compare all matrix parameters with `pykalman`'s - matrix parameters returned from `em`. This test is useful for both - KalmanFilterTransformerPK and KalmanFilterTransformerFP. + Call `fit` of input adapter/s, and compare all matrix parameters with + `pykalman-bardo`'s matrix parameters returned from `em`. This test + is useful for both KalmanFilterTransformerPK and + KalmanFilterTransformerFP. """ mask_measurements = np.ma.masked_invalid(np.copy(measurements)) @@ -579,8 +592,16 @@ def test_em(classes, params, measurements): @pytest.mark.skipif( - not _check_soft_dependencies("pykalman", "filterpy", severity="none"), - reason="skip test if required soft dependencies pykalman, filterpy not available", + not _check_soft_dependencies( + "pykalman-bardo", + "filterpy", + package_import_alias={"pykalman-bardo": "pykalman"}, + severity="none", + ), + reason=( + "skip test if required soft dependencies pykalman-bardo, " + "filterpy not available" + ), ) @pytest.mark.parametrize( "classes, params, measurements", From 4a4e4a9973b9a7698140354f0d2430eb7de1ea7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:05:45 +0200 Subject: [PATCH 17/27] [MNT] [Dependabot](deps-dev): Update holidays requirement from <0.34,>=0.29 to >=0.29,<0.35 (#5342) Updates the requirements on [holidays](https://github.com/vacanza/python-holidays) to permit the latest version. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ed2ea95767b..e0b520796d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ regression = [ transformations = [ "esig>=0.9.7,<0.10", "filterpy>=1.4.5,<1.5", - "holidays>=0.29,<0.34", + "holidays>=0.29,<0.35", "mne>=1.5,<1.6", "numba>=0.53,<0.59", "pycatch22>=0.4,<0.5", From e782dd81fc365b17ecd998fe2d2e23a2c3e8af45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 16:06:28 +0200 Subject: [PATCH 18/27] [MNT] 0.24.0 deprecations and change actions (#5404) 0.24.0 release deprecations and change actions: * in forecasting tuners, change of `tune_by_variable` default to `False` * bump tracking todos --- .../_probability_threshold.py | 2 +- sktime/forecasting/base/_fh.py | 2 +- sktime/forecasting/compose/_pipeline.py | 2 +- sktime/forecasting/model_selection/_tune.py | 31 +++++-------------- .../model_selection/tests/test_tune.py | 8 ----- 5 files changed, 10 insertions(+), 35 deletions(-) diff --git a/sktime/classification/early_classification/_probability_threshold.py b/sktime/classification/early_classification/_probability_threshold.py index 1f34aa9fa8d..b9c6bc4f11a 100644 --- a/sktime/classification/early_classification/_probability_threshold.py +++ b/sktime/classification/early_classification/_probability_threshold.py @@ -20,7 +20,7 @@ from sktime.utils.validation.panel import check_X -# TODO: fix this in 0.24.0 +# TODO: fix this in 0.25.0 # base class should have been changed to BaseEarlyClassifier class ProbabilityThresholdEarlyClassifier(BaseClassifier): """Probability Threshold Early Classifier. diff --git a/sktime/forecasting/base/_fh.py b/sktime/forecasting/base/_fh.py index 18ccece0359..e179f830dff 100644 --- a/sktime/forecasting/base/_fh.py +++ b/sktime/forecasting/base/_fh.py @@ -728,7 +728,7 @@ def _to_relative(fh: ForecastingHorizon, cutoff=None) -> ForecastingHorizon: absolute = _coerce_to_period(absolute, freq=fh.freq) cutoff = _coerce_to_period(cutoff, freq=fh.freq) - # TODO: 0.24.0: + # TODO: 0.25.0: # Check at every minor release whether lower pandas bound >=0.15.0 # if yes, can remove the workaround in the "else" condition and the check # diff --git a/sktime/forecasting/compose/_pipeline.py b/sktime/forecasting/compose/_pipeline.py index 9b44a5f4c9b..b0bae97ba53 100644 --- a/sktime/forecasting/compose/_pipeline.py +++ b/sktime/forecasting/compose/_pipeline.py @@ -170,7 +170,7 @@ def _get_inverse_transform(self, transformers, y, X=None, mode=None): if len(levels) == 1: levels = levels[0] yt[ix] = y.xs(ix, level=levels, axis=1) - # todo 0.24.0 - check why this cannot be easily removed + # todo 0.25.0 - check why this cannot be easily removed # in theory, we should get rid of the "Coverage" case treatment # (the legacy naming convention was removed in 0.23.0) # deal with the "Coverage" case, we need to get rid of this diff --git a/sktime/forecasting/model_selection/_tune.py b/sktime/forecasting/model_selection/_tune.py index a84f5fb795e..97f3191a864 100644 --- a/sktime/forecasting/model_selection/_tune.py +++ b/sktime/forecasting/model_selection/_tune.py @@ -11,7 +11,6 @@ from collections.abc import Sequence from typing import Dict, List, Optional, Union -from warnings import warn import numpy as np import pandas as pd @@ -37,8 +36,6 @@ class BaseGridSearch(_DelegatedForecaster): "capability:pred_int:insample": True, } - # todo 0.24.0: replace all tune_by_variable defaults in this file with False - # remove deprecation message in BaseGridSearch.__init__ def __init__( self, forecaster, @@ -54,7 +51,7 @@ def __init__( update_behaviour="full_refit", error_score=np.nan, tune_by_instance=False, - tune_by_variable=None, + tune_by_variable=False, ): self.forecaster = forecaster self.cv = cv @@ -73,20 +70,6 @@ def __init__( super().__init__() - # todo 0.24.0: remove this - if tune_by_variable is None: - warn( - f"in {self.__class__.__name__}, the default for tune_by_variable " - "will change from True to False in 0.24.0. " - "This will tune one parameter setting for all variables, while " - "currently it tunes one parameter per variable. " - "In order to maintain the current behaviour, ensure to set " - "the parameter tune_by_variable to True explicitly before upgrading " - "to version 0.24.0.", - DeprecationWarning, - ) - tune_by_variable = True - tags_to_clone = [ "requires-fh-in-fit", "capability:pred_int", @@ -468,7 +451,7 @@ class ForecastingGridSearchCV(BaseGridSearch): and are available in fields of the forecasters_ attribute. Has the same effect as applying ForecastByLevel wrapper to self. If False, the same best parameter is selected for all instances. - tune_by_variable : bool, optional (default=True) + tune_by_variable : bool, optional (default=False) Whether to tune parameter by each time series variable separately, in case of multivariate data passed to the tuning estimator. Only applies if time series passed are strictly multivariate. @@ -585,7 +568,7 @@ def __init__( update_behaviour="full_refit", error_score=np.nan, tune_by_instance=False, - tune_by_variable=None, + tune_by_variable=False, ): super().__init__( forecaster=forecaster, @@ -777,7 +760,7 @@ class ForecastingRandomizedSearchCV(BaseGridSearch): and are available in fields of the forecasters_ attribute. Has the same effect as applying ForecastByLevel wrapper to self. If False, the same best parameter is selected for all instances. - tune_by_variable : bool, optional (default=True) + tune_by_variable : bool, optional (default=False) Whether to tune parameter by each time series variable separately, in case of multivariate data passed to the tuning estimator. Only applies if time series passed are strictly multivariate. @@ -828,7 +811,7 @@ def __init__( update_behaviour="full_refit", error_score=np.nan, tune_by_instance=False, - tune_by_variable=None, + tune_by_variable=False, ): super().__init__( forecaster=forecaster, @@ -1008,7 +991,7 @@ class ForecastingSkoptSearchCV(BaseGridSearch): and are available in fields of the forecasters_ attribute. Has the same effect as applying ForecastByLevel wrapper to self. If False, the same best parameter is selected for all instances. - tune_by_variable : bool, optional (default=True) + tune_by_variable : bool, optional (default=False) Whether to tune parameter by each time series variable separately, in case of multivariate data passed to the tuning estimator. Only applies if time series passed are strictly multivariate. @@ -1098,7 +1081,7 @@ def __init__( update_behaviour: str = "full_refit", error_score=np.nan, tune_by_instance=False, - tune_by_variable=None, + tune_by_variable=False, ): self.param_distributions = param_distributions self.n_iter = n_iter diff --git a/sktime/forecasting/model_selection/tests/test_tune.py b/sktime/forecasting/model_selection/tests/test_tune.py index ccad875b2ff..0de49f274ff 100644 --- a/sktime/forecasting/model_selection/tests/test_tune.py +++ b/sktime/forecasting/model_selection/tests/test_tune.py @@ -142,10 +142,6 @@ def test_gscv(forecaster, param_grid, cv, scoring, error_score, multivariate): cv=cv, scoring=scoring, error_score=error_score, - # todo 0.24.0: remove this - # and/or add a test for tune_by_variable=True - # in this case, the forecaster is expeceted to vectorize over columns - tune_by_variable=False, ) gscv.fit(y, X) @@ -211,10 +207,6 @@ def test_gscv_hierarchical(forecaster, param_grid, cv, scoring, error_score, n_c cv=cv, scoring=scoring, error_score=error_score, - # todo 0.24.0: remove this - # and/or add a test for tune_by_variable=True - # in this case, the forecaster is expeceted to vectorize over columns - tune_by_variable=False, ) gscv.fit(y, X) From 9f41595458dc180a005bd2b7dcd2510960028604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 16:09:01 +0200 Subject: [PATCH 19/27] Revert "[MNT] instant release action" (#5415) This PR changes the release action back to an release after testing wheels. This might be useful for changing the current workflow to one where all tests are run manually, or in CRON batches. Reverts sktime/sktime#5265. Same as removing https://github.com/sktime/sktime/pull/5262 (was temporary) --- .github/workflows/wheels.yml | 104 ++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b63549a1ea0..32618779298 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -27,10 +27,112 @@ jobs: name: wheels path: wheelhouse/* + test_unix_wheels: + needs: build_wheels + name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false # to not fail all combinations if just one fail + matrix: + os: [ubuntu-latest, macos-latest] + python-version: ['3.8', '3.9', '3.10', '3.11'] + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/download-artifact@v3 + with: + name: wheels + path: wheelhouse + + - name: Get wheel filename + run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $GITHUB_ENV + + - name: Install wheel and extras + run: python -m pip install "${{ env.WHEELNAME }}[all_extras_pandas2,dev]" + + - name: Run tests + run: make test + + test_windows_wheels: + needs: build_wheels + name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} + runs-on: windows-latest + strategy: + fail-fast: false # to not fail all combinations if just one fail + matrix: + include: + # Window 64 bit + - os: windows-latest + python: 38 + python-version: '3.8' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 39 + python-version: '3.9' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 310 + python-version: '3.10' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 311 + python-version: '3.11' + bitness: 64 + platform_id: win_amd64 + + steps: + - uses: actions/checkout@v4 + - uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: test + auto-update-conda: true + python-version: ${{ matrix.python-version }} + channels: anaconda, conda-forge, + + - run: conda --version + - run: which python + + - uses: actions/download-artifact@v3 + with: + name: wheels + path: wheelhouse + + - name: Install conda libpython + run: conda install -c anaconda -n test -y libpython + + - name: Display downloaded artifacts + run: ls -l wheelhouse + + - name: Get wheel filename + run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $env:GITHUB_ENV + + - name: Activate conda env + run: conda activate test + + - name: Install wheel and extras + run: python -m pip install "${env:WHEELNAME}[all_extras_pandas2,dev]" + + - name: Show conda packages + run: conda list -n test + + - name: Run tests + run: | + mkdir -p testdir/ + cp .coveragerc testdir/ + cp setup.cfg testdir/ + python -m pytest + upload_wheels: name: Upload wheels to PyPI runs-on: ubuntu-latest - needs: build_wheels + needs: [build_wheels,test_unix_wheels,test_windows_wheels] steps: - uses: actions/download-artifact@v3 From 8b4737bfd6af50143d85f967f968cccc4dd3108b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 17:54:38 +0200 Subject: [PATCH 20/27] =?UTF-8?q?=F0=9F=9A=80=20python=203.12=20?= =?UTF-8?q?=F0=9F=9A=80=20=20(#5345)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR to add python 3.12 to the list of supported python 3.12 versions. This adds python 3.12 in CI, `pyproject.toml`, and documentation. Out of scope is the update of docker testing environments. This PR CI will run only the basic suite of tests, the full suite of tests will be run in a separate PR. Dependencies that also needed an upgrade: * `scikit-base` upper bound to allow an 3.12 compatible version * many soft dependencies are not 3.12 compatible yet, these needed a conditional bound --- .github/workflows/test.yml | 4 +-- .github/workflows/wheels.yml | 7 ++++- README.md | 2 +- docs/source/get_started.rst | 2 +- docs/source/installation.rst | 2 +- pyproject.toml | 61 ++++++++++++++++++------------------ 6 files changed, 42 insertions(+), 36 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 62731f33147..0ee0b597340 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -174,7 +174,7 @@ jobs: strategy: fail-fast: false # to not fail all combinations if just one fail matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} steps: @@ -213,7 +213,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 32618779298..c482476a130 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -35,7 +35,7 @@ jobs: fail-fast: false # to not fail all combinations if just one fail matrix: os: [ubuntu-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v4 @@ -86,6 +86,11 @@ jobs: python-version: '3.11' bitness: 64 platform_id: win_amd64 + - os: windows-latest + python: 312 + python-version: '3.12' + bitness: 64 + platform_id: win_amd64 steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index abc04b7388d..792aa68d8e0 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ For **deep learning**, see our companion package: [sktime-dl](https://github.com For troubleshooting and detailed installation instructions, see the [documentation](https://www.sktime.net/en/latest/installation.html). - **Operating system**: macOS X Β· Linux Β· Windows 8.1 or higher -- **Python version**: Python 3.8, 3.9, 3.10, and 3.11 (only 64-bit) +- **Python version**: Python 3.8, 3.9, 3.10, 3.11, and 3.12 (only 64-bit) - **Package managers**: [pip] Β· [conda] (via `conda-forge`) [pip]: https://pip.pypa.io/en/stable/ diff --git a/docs/source/get_started.rst b/docs/source/get_started.rst index c075df1f1b2..cce05ec1370 100644 --- a/docs/source/get_started.rst +++ b/docs/source/get_started.rst @@ -11,7 +11,7 @@ Installation ``sktime`` currently supports: -* environments with python version 3.8, 3.9, 3.10, or 3.11. +* environments with python version 3.8, 3.9, 3.10, 3.11, or 3.12. * operating systems Mac OS X, Unix-like OS, Windows 8.1 and higher * installation via ``PyPi`` or ``conda`` diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 0a0f507548c..f5340428f33 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -5,7 +5,7 @@ Installation ``sktime`` currently supports: -* Python versions 3.8, 3.9, 3.10, and 3.11. +* Python versions 3.8, 3.9, 3.10, 3.11, and 3.12. * Operating systems Mac OS X, Unix-like OS, Windows 8.1 and higher See here for a `full list of precompiled wheels available on PyPI `_. diff --git a/pyproject.toml b/pyproject.toml index e0b520796d4..fdf0f081e7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,13 +41,14 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] -requires-python = ">=3.8,<3.12" +requires-python = ">=3.8,<3.13" dependencies = [ "numpy>=1.21.0,<1.27", "pandas>=1.1.0,<2.2.0", "packaging", - "scikit-base<0.6.0", + "scikit-base<0.7.0", "scikit-learn>=0.24.0,<1.4.0", "scipy<2.0.0,>=1.2.0", ] @@ -55,32 +56,32 @@ dependencies = [ [project.optional-dependencies] alignment = [ "dtw-python>=1.3,<1.4", - "numba>=0.53,<0.59", + "numba>=0.53,<0.59; python_version < '3.12'", ] annotation = [ "hmmlearn>=0.2.7,<0.4", - "numba>=0.53,<0.59", + "numba>=0.53,<0.59; python_version < '3.12'", "pyod>=0.8.0,<1.2", ] classification = [ "esig>=0.9.7,<0.10", "mrsqm>=0.0.3,<0.1", - "numba>=0.53,<0.59", + "numba>=0.53,<0.59; python_version < '3.12'", "tensorflow>=2,<=2.14", - "tsfresh>=0.17,<0.21", + "tsfresh>=0.17,<0.21; python_version < '3.12'", ] clustering = [ - "numba>=0.53,<0.59", + "numba>=0.53,<0.59; python_version < '3.12'", "tslearn>=0.5.2,<0.6.3", ] forecasting = [ - "pmdarima>=1.8,!=1.8.1,<2.1", - "prophet>=1.1,<1.2", + "arch>=5.6.0,<6.3.0", + "pmdarima>=1.8,!=1.8.1,<2.1; python_version < '3.12'", + "prophet>=1.1,<1.2; python_version < '3.12'", "skpro>=2.0.0,<2.2.0", - "statsforecast>=0.5.2,<1.7", + "statsforecast>=0.5.2,<1.7.0; python_version < '3.12'", "statsmodels>=0.12.1,<0.15", - "tbats>=1.1,<1.2", - "arch>=5.6.0,<6.3.0", + "tbats>=1.1,<1.2; python_version < '3.12'", ] networks = [ "keras-self-attention>=0.51,<0.52", @@ -91,7 +92,7 @@ param_est = [ "statsmodels>=0.12.1,<0.15", ] regression = [ - "numba>=0.53,<0.59", + "numba>=0.53,<0.59; python_version < '3.12'", "tensorflow>=2,<=2.14", ] transformations = [ @@ -99,12 +100,12 @@ transformations = [ "filterpy>=1.4.5,<1.5", "holidays>=0.29,<0.35", "mne>=1.5,<1.6", - "numba>=0.53,<0.59", + "numba>=0.53,<0.59; python_version < '3.12'", "pycatch22>=0.4,<0.5", "pykalman-bardo>=0.9.7,<0.10", "statsmodels>=0.12.1,<0.15", - "stumpy>=1.5.1,<1.13", - "tsfresh>=0.17,<0.21", + "stumpy>=1.5.1,<1.13; python_version < '3.12'", + "tsfresh>=0.17,<0.21; python_version < '3.12'", ] all_extras = [ "cloudpickle", @@ -114,16 +115,16 @@ all_extras = [ "esig==0.9.7; python_version < '3.10'", "filterpy>=1.4.5; python_version < '3.11'", "gluonts>=0.9.0", - "h5py", + "h5py; python_version < '3.12'", "hmmlearn>=0.2.7; python_version < '3.11'", "holidays", "keras-self-attention; python_version < '3.11'", "kotsu>=0.3.1", "matplotlib>=3.3.2", "mne", - "numba>=0.53,<0.58", - "pmdarima>=1.8.0,!=1.8.1,<3.0.0", - "prophet>=1.1", + "numba>=0.53,<0.59; python_version < '3.12'", + "pmdarima>=1.8.0,!=1.8.1,<3.0.0; python_version < '3.12'", + "prophet>=1.1; python_version < '3.12'", "pycatch22", "pykalman-bardo>=0.9.7,<0.10", "pyod>=0.8.0; python_version < '3.11'", @@ -132,12 +133,12 @@ all_extras = [ "seaborn>=0.11.0", "seasonal", "skpro>=2.0.0,<2.2.0", - "statsforecast>=0.5.2,<1.7.0", + "statsforecast>=0.5.2,<1.7.0; python_version < '3.12'", "statsmodels>=0.12.1", "stumpy>=1.5.1; python_version < '3.11'", - "tbats>=1.1.0", + "tbats>=1.1.0; python_version < '3.12'", "tensorflow; python_version < '3.11'", - "tsfresh>=0.17.0", + "tsfresh>=0.17.0; python_version < '3.12'", "tslearn>=0.5.2,<0.6.0; python_version < '3.11'", "xarray", "arch>=5.6.0,<6.3.0", @@ -151,16 +152,16 @@ all_extras_pandas2 = [ "esig==0.9.7; python_version < '3.10'", "filterpy>=1.4.5; python_version < '3.11'", "gluonts>=0.9.0", - "h5py", + "h5py; python_version < '3.12'", "hmmlearn>=0.2.7; python_version < '3.11'", "holidays", "keras-self-attention; python_version < '3.11'", "kotsu>=0.3.1", "matplotlib>=3.3.2", "mne", - "numba>=0.53,<0.58", - "pmdarima>=1.8.0,!=1.8.1,<3.0.0", - "prophet>=1.1", + "numba>=0.53,<0.59; python_version < '3.12'", + "pmdarima>=1.8.0,!=1.8.1,<3.0.0; python_version < '3.12'", + "prophet>=1.1; python_version < '3.12'", "pycatch22", "pykalman-bardo>=0.9.7,<0.10", "pyod>=0.8.0; python_version < '3.11'", @@ -168,12 +169,12 @@ all_extras_pandas2 = [ "seaborn>=0.11.0", "seasonal", "skpro>=2.0.0,<2.2.0", - "statsforecast>=0.5.2,<1.7.0", + "statsforecast>=0.5.2,<1.7.0; python_version < '3.12'", "statsmodels>=0.12.1", "stumpy>=1.5.1; python_version < '3.11'", - "tbats>=1.1.0", + "tbats>=1.1.0; python_version < '3.12'", "tensorflow; python_version < '3.11'", - "tsfresh>=0.17.0", + "tsfresh>=0.17.0; python_version < '3.12'", "tslearn>=0.5.2,<0.6.0; python_version < '3.11'", "xarray", "arch>=5.6.0,<6.3.0", From 651828a943975336f1909b6725c97182fee7b6a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 21:20:40 +0200 Subject: [PATCH 21/27] Release 0.24.0 (#5403) Release PR for 0.24.0 * version number bump * changelog --- README.md | 2 +- docs/source/changelog.rst | 48 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- sktime/__init__.py | 2 +- 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 792aa68d8e0..bcb9a99f126 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ > A unified interface for machine learning with time series -:rocket: **Version 0.23.1 out now!** [Check out the release notes here](https://www.sktime.net/en/latest/changelog.html). +:rocket: **Version 0.24.0 out now!** [Check out the release notes here](https://www.sktime.net/en/latest/changelog.html). sktime is a library for time series analysis in Python. It provides a unified interface for multiple time series learning tasks. Currently, this includes time series classification, regression, clustering, annotation, and forecasting. It comes with [time series algorithms](https://www.sktime.net/en/stable/estimator_overview.html) and [scikit-learn] compatible tools to build, tune and validate time series models. diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 5b2374816c3..eafd41185ec 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -14,6 +14,54 @@ For upcoming changes and next releases, see our `milestones =0.29,<0.35``. +* ``numba`` (classification, regression, and transformations soft dependency) bounds have been updated to ``>=0.53,<0.59``. +* ``skpro`` (forecasting soft dependency) bounds have been updated to ``>=2.0.0,<2.2.0``. + +Deprecations and removals +~~~~~~~~~~~~~~~~~~~~~~~~~ + +* in forecasting tuners ``ForecastingGridSearchCV``, ``ForecastingRandomizedSearchCV``, + ``ForecastingSkoptSearchCV``, the default of parameter ``tune_by_variable`` + has been switched from ``True`` to ``False``. + +Contents +~~~~~~~~ + +* [MNT] Update ``numba`` requirement from ``<0.58,>=0.53`` to ``>=0.53,<0.59`` (:pr:`5299`, :pr:`5319`) :user:`dependabot[bot]`, :user:`fkiraly` +* [MNT] [Dependabot](deps-dev): Update ``skpro`` requirement from ``<2.1.0,>=2.0.0`` to ``>=2.0.0,<2.2.0`` (:pr:`5396`) :user:`dependabot[bot]` +* [MNT] [Dependabot](deps-dev): Update ``holidays`` requirement from ``<0.34,>=0.29`` to ``>=0.29,<0.35`` (:pr:`5342`) :user:`dependabot[bot]` +* [MNT] Migrate from ``pykalman`` to ``pykalman-bardo`` (:pr:`5277`) :user:`mbalatsko` +* [MNT] 0.24.0 deprecations and change actions (:pr:`5404`) :user:`fkiraly` +* πŸš€ python 3.12 πŸš€ (:pr:`5345`) :user:`fkiraly` + +Contributors +~~~~~~~~~~~~ + +:user:`fkiraly`, +:user:`mbalatsko` + + Version 0.23.1 - 2023-10-12 --------------------------- diff --git a/pyproject.toml b/pyproject.toml index fdf0f081e7a..a4c67aed180 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sktime" -version = "0.23.1" +version = "0.24.0" description = "A unified framework for machine learning with time series" authors = [ {name = "sktime developers", email = "sktime.toolbox@gmail.com"}, diff --git a/sktime/__init__.py b/sktime/__init__.py index 269ba7b7310..c30032c0c59 100644 --- a/sktime/__init__.py +++ b/sktime/__init__.py @@ -1,6 +1,6 @@ """sktime.""" -__version__ = "0.23.1" +__version__ = "0.24.0" __all__ = ["show_versions"] From 8972240f884359d49795a5971b32c84e8925a238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 21:20:57 +0200 Subject: [PATCH 22/27] [MNT] instant release action (#5419) This PR changes the release action to an instant release. Temporary until we have consolidated the release process so it is not delayed unduly by download failures or sporadic estimator fails. This might also be useful for changing the current workflow to one where all tests are run manually, or in CRON batches. Same as https://github.com/sktime/sktime/pull/5262 (was temporary) --- .github/workflows/wheels.yml | 109 +---------------------------------- 1 file changed, 1 insertion(+), 108 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index c482476a130..b53808783d2 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -27,117 +27,10 @@ jobs: name: wheels path: wheelhouse/* - test_unix_wheels: - needs: build_wheels - name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false # to not fail all combinations if just one fail - matrix: - os: [ubuntu-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - uses: actions/download-artifact@v3 - with: - name: wheels - path: wheelhouse - - - name: Get wheel filename - run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $GITHUB_ENV - - - name: Install wheel and extras - run: python -m pip install "${{ env.WHEELNAME }}[all_extras_pandas2,dev]" - - - name: Run tests - run: make test - - test_windows_wheels: - needs: build_wheels - name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} - runs-on: windows-latest - strategy: - fail-fast: false # to not fail all combinations if just one fail - matrix: - include: - # Window 64 bit - - os: windows-latest - python: 38 - python-version: '3.8' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 39 - python-version: '3.9' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 310 - python-version: '3.10' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 311 - python-version: '3.11' - bitness: 64 - platform_id: win_amd64 - - os: windows-latest - python: 312 - python-version: '3.12' - bitness: 64 - platform_id: win_amd64 - - steps: - - uses: actions/checkout@v4 - - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: test - auto-update-conda: true - python-version: ${{ matrix.python-version }} - channels: anaconda, conda-forge, - - - run: conda --version - - run: which python - - - uses: actions/download-artifact@v3 - with: - name: wheels - path: wheelhouse - - - name: Install conda libpython - run: conda install -c anaconda -n test -y libpython - - - name: Display downloaded artifacts - run: ls -l wheelhouse - - - name: Get wheel filename - run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $env:GITHUB_ENV - - - name: Activate conda env - run: conda activate test - - - name: Install wheel and extras - run: python -m pip install "${env:WHEELNAME}[all_extras_pandas2,dev]" - - - name: Show conda packages - run: conda list -n test - - - name: Run tests - run: | - mkdir -p testdir/ - cp .coveragerc testdir/ - cp setup.cfg testdir/ - python -m pytest - upload_wheels: name: Upload wheels to PyPI runs-on: ubuntu-latest - needs: [build_wheels,test_unix_wheels,test_windows_wheels] + needs: [build_wheels] steps: - uses: actions/download-artifact@v3 From 4b4407ea2bbb3d0ae42e47f1248def10cc6b1846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Fri, 13 Oct 2023 22:27:11 +0200 Subject: [PATCH 23/27] Revert "[MNT] instant release action" (#5421) This PR changes the release action back to release after testing wheels. This might be useful for changing the current workflow to one where all tests are run manually, or in CRON batches. Reverts sktime/sktime#5419. Same as removing https://github.com/sktime/sktime/pull/5262 (was temporary) --- .github/workflows/wheels.yml | 109 ++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index b53808783d2..c482476a130 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -27,10 +27,117 @@ jobs: name: wheels path: wheelhouse/* + test_unix_wheels: + needs: build_wheels + name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false # to not fail all combinations if just one fail + matrix: + os: [ubuntu-latest, macos-latest] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/download-artifact@v3 + with: + name: wheels + path: wheelhouse + + - name: Get wheel filename + run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $GITHUB_ENV + + - name: Install wheel and extras + run: python -m pip install "${{ env.WHEELNAME }}[all_extras_pandas2,dev]" + + - name: Run tests + run: make test + + test_windows_wheels: + needs: build_wheels + name: Test wheels on ${{ matrix.os }} with ${{ matrix.python-version }} + runs-on: windows-latest + strategy: + fail-fast: false # to not fail all combinations if just one fail + matrix: + include: + # Window 64 bit + - os: windows-latest + python: 38 + python-version: '3.8' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 39 + python-version: '3.9' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 310 + python-version: '3.10' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 311 + python-version: '3.11' + bitness: 64 + platform_id: win_amd64 + - os: windows-latest + python: 312 + python-version: '3.12' + bitness: 64 + platform_id: win_amd64 + + steps: + - uses: actions/checkout@v4 + - uses: conda-incubator/setup-miniconda@v2 + with: + activate-environment: test + auto-update-conda: true + python-version: ${{ matrix.python-version }} + channels: anaconda, conda-forge, + + - run: conda --version + - run: which python + + - uses: actions/download-artifact@v3 + with: + name: wheels + path: wheelhouse + + - name: Install conda libpython + run: conda install -c anaconda -n test -y libpython + + - name: Display downloaded artifacts + run: ls -l wheelhouse + + - name: Get wheel filename + run: echo "WHEELNAME=$(ls ./wheelhouse/sktime-*none-any.whl)" >> $env:GITHUB_ENV + + - name: Activate conda env + run: conda activate test + + - name: Install wheel and extras + run: python -m pip install "${env:WHEELNAME}[all_extras_pandas2,dev]" + + - name: Show conda packages + run: conda list -n test + + - name: Run tests + run: | + mkdir -p testdir/ + cp .coveragerc testdir/ + cp setup.cfg testdir/ + python -m pytest + upload_wheels: name: Upload wheels to PyPI runs-on: ubuntu-latest - needs: [build_wheels] + needs: [build_wheels,test_unix_wheels,test_windows_wheels] steps: - uses: actions/download-artifact@v3 From d69caf75bb447a488d2d21d3d2992fcc45941934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20Kir=C3=A1ly?= Date: Sat, 14 Oct 2023 11:49:43 +0200 Subject: [PATCH 24/27] [DOC] fixing docstring example for `FhPlexForecaster` (#4931) #### Reference Issues/PRs Fixes #4930 #### What does this implement/fix? Explain your changes. adds an example for `FhPlexForecaster`, removes the wrong example --- sktime/forecasting/compose/_fhplex.py | 30 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/sktime/forecasting/compose/_fhplex.py b/sktime/forecasting/compose/_fhplex.py index 0ae68410f3e..db9b266a2d1 100644 --- a/sktime/forecasting/compose/_fhplex.py +++ b/sktime/forecasting/compose/_fhplex.py @@ -55,15 +55,29 @@ class FhPlexForecaster(BaseForecaster): Examples -------- + >>> from sktime.datasets import load_airline >>> from sktime.forecasting.naive import NaiveForecaster - >>> from sktime.forecasting.compose import ForecastByLevel - >>> from sktime.utils._testing.hierarchical import _make_hierarchical - >>> y = _make_hierarchical() - >>> f = ForecastByLevel(NaiveForecaster(), groupby="local") - >>> f.fit(y) - ForecastByLevel(...) - >>> fitted_forecasters = f.forecasters_ - >>> fitted_forecasters_alt = f.get_fitted_params()["forecasters"] + >>> from sktime.forecasting.compose import FhPlexForecaster + + Simple example - same parameters per fh element + >>> y = load_airline() + >>> f = FhPlexForecaster(NaiveForecaster()) + >>> f.fit(y, fh=[1, 2, 3]) + FhPlexForecaster(...) + >>> f.forecasters_ # get individual fitted forecasters + {1: NaiveForecaster(), 2: NaiveForecaster(), 3: NaiveForecaster()} + >>> fitted_params = f.get_fitted_params() # or via get_fitted_params + >>> y_pred = f.predict() + + Simple example - different parameters per fh element + >>> y = load_airline() + >>> fh_params = [{}, {"strategy": "last"}, {"strategy": "mean"}] + >>> f = FhPlexForecaster(NaiveForecaster(), fh_params=fh_params) + >>> f.fit(y, fh=[1, 2, 3]) + FhPlexForecaster(...) + >>> f.forecasters_ # get individual fitted forecasters + {1: NaiveForecaster(), 2: NaiveForecaster(), 3: NaiveForecaster(strategy='mean')} + >>> y_pred = f.predict() """ _tags = { From aca62628148949ec1588b6b9e36dfd6fcf6b9dc1 Mon Sep 17 00:00:00 2001 From: Anirban Ray <39331844+yarnabrina@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:26:46 +0530 Subject: [PATCH 25/27] added new hook --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3bda1e00104..28437928a0b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -76,3 +76,8 @@ repos: hooks: - id: shellcheck name: shellcheck + + - repo: https://github.com/tox-dev/pyproject-fmt + rev: 1.2.0 + hooks: + - id: pyproject-fmt From c71d161462fa0c40c22ba4b1c2b7ef5198116fc6 Mon Sep 17 00:00:00 2001 From: Anirban Ray <39331844+yarnabrina@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:47:58 +0530 Subject: [PATCH 26/27] format using new hook --- pyproject.toml | 441 ++++++++++++++++++++++++------------------------- 1 file changed, 214 insertions(+), 227 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a4c67aed180..1a3f7e04fce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,274 +2,261 @@ name = "sktime" version = "0.24.0" description = "A unified framework for machine learning with time series" -authors = [ - {name = "sktime developers", email = "sktime.toolbox@gmail.com"}, +readme = "README.md" +keywords = [ + "data-mining", + "data-science", + "forecasting", + "machine-learning", + "scikit-learn", + "time-series", + "time-series-analysis", + "time-series-classification", + "time-series-regression", ] +license = { file = "LICENSE" } maintainers = [ - {name = "sktime developers", email = "sktime.toolbox@gmail.com"}, - {name = "Franz KirΓ‘ly"}, - {name = "Jonathan Bechtel"}, - {name = "Kiril Ralinovski"}, - {name = "Marc Rovira"}, - {name = "Sagar Mishra"}, - {name = "Ugochukwu Onyeka"}, + { name = "sktime developers", email = "sktime.toolbox@gmail.com" }, + { name = "Franz KirΓ‘ly" }, + { name = "Jonathan Bechtel" }, + { name = "Kiril Ralinovski" }, + { name = "Marc Rovira" }, + { name = "Sagar Mishra" }, + { name = "Ugochukwu Onyeka" }, ] -readme = "README.md" -keywords = [ - "data-science", - "machine-learning", - "data-mining", - "time-series", - "scikit-learn", - "forecasting", - "time-series-analysis", - "time-series-classification", - "time-series-regression", +authors = [ + { name = "sktime developers", email = "sktime.toolbox@gmail.com" }, ] +requires-python = ">=3.8,<3.13" classifiers = [ - "Intended Audience :: Science/Research", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Programming Language :: Python", - "Topic :: Software Development", - "Topic :: Scientific/Engineering", - "Operating System :: Microsoft :: Windows", - "Operating System :: POSIX", - "Operating System :: Unix", - "Operating System :: MacOS", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Operating System :: Unix", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering", + "Topic :: Software Development", ] -requires-python = ">=3.8,<3.13" dependencies = [ - "numpy>=1.21.0,<1.27", - "pandas>=1.1.0,<2.2.0", - "packaging", - "scikit-base<0.7.0", - "scikit-learn>=0.24.0,<1.4.0", - "scipy<2.0.0,>=1.2.0", + "numpy<1.27,>=1.21", + "packaging", + "pandas<2.2.0,>=1.1", + "scikit-base<0.7.0", + "scikit-learn<1.4.0,>=0.24", + "scipy<2.0.0,>=1.2", ] - [project.optional-dependencies] alignment = [ - "dtw-python>=1.3,<1.4", - "numba>=0.53,<0.59; python_version < '3.12'", + "dtw-python<1.4,>=1.3", + 'numba<0.59,>=0.53; python_version < "3.12"', ] -annotation = [ - "hmmlearn>=0.2.7,<0.4", - "numba>=0.53,<0.59; python_version < '3.12'", - "pyod>=0.8.0,<1.2", -] -classification = [ - "esig>=0.9.7,<0.10", - "mrsqm>=0.0.3,<0.1", - "numba>=0.53,<0.59; python_version < '3.12'", - "tensorflow>=2,<=2.14", - "tsfresh>=0.17,<0.21; python_version < '3.12'", +all_extras = [ + "arch<6.3.0,>=5.6", + "cloudpickle", + "dash!=2.9.0", + "dask", + "dtw-python", + 'esig==0.9.7; python_version < "3.10"', + 'filterpy>=1.4.5; python_version < "3.11"', + "gluonts>=0.9", + 'h5py; python_version < "3.12"', + 'hmmlearn>=0.2.7; python_version < "3.11"', + "holidays", + 'keras-self-attention; python_version < "3.11"', + "kotsu>=0.3.1", + "matplotlib>=3.3.2", + "mne", + 'numba<0.59,>=0.53; python_version < "3.12"', + 'pmdarima!=1.8.1,<3.0.0,>=1.8; python_version < "3.12"', + 'prophet>=1.1; python_version < "3.12"', + "pycatch22", + "pykalman-bardo<0.10,>=0.9.7", + 'pyod>=0.8; python_version < "3.11"', + "scikit-optimize", + "scikit_posthocs>=0.6.5", + "seaborn>=0.11", + "seasonal", + "skpro<2.2.0,>=2", + 'statsforecast<1.7.0,>=0.5.2; python_version < "3.12"', + "statsmodels>=0.12.1", + 'stumpy>=1.5.1; python_version < "3.11"', + 'tbats>=1.1; python_version < "3.12"', + 'tensorflow; python_version < "3.11"', + 'tsfresh>=0.17; python_version < "3.12"', + 'tslearn<0.6.0,>=0.5.2; python_version < "3.11"', + "xarray", ] -clustering = [ - "numba>=0.53,<0.59; python_version < '3.12'", - "tslearn>=0.5.2,<0.6.3", +all_extras_pandas2 = [ + "arch<6.3.0,>=5.6", + "cloudpickle", + "dash!=2.9.0", + "dask<2023.7.1", + "dtw-python", + 'esig==0.9.7; python_version < "3.10"', + 'filterpy>=1.4.5; python_version < "3.11"', + "gluonts>=0.9", + 'h5py; python_version < "3.12"', + 'hmmlearn>=0.2.7; python_version < "3.11"', + "holidays", + 'keras-self-attention; python_version < "3.11"', + "kotsu>=0.3.1", + "matplotlib>=3.3.2", + "mne", + 'numba<0.59,>=0.53; python_version < "3.12"', + 'pmdarima!=1.8.1,<3.0.0,>=1.8; python_version < "3.12"', + 'prophet>=1.1; python_version < "3.12"', + "pycatch22", + "pykalman-bardo<0.10,>=0.9.7", + 'pyod>=0.8; python_version < "3.11"', + "scikit_posthocs>=0.6.5", + "seaborn>=0.11", + "seasonal", + "skpro<2.2.0,>=2", + 'statsforecast<1.7.0,>=0.5.2; python_version < "3.12"', + "statsmodels>=0.12.1", + 'stumpy>=1.5.1; python_version < "3.11"', + 'tbats>=1.1; python_version < "3.12"', + 'tensorflow; python_version < "3.11"', + 'tsfresh>=0.17; python_version < "3.12"', + 'tslearn<0.6.0,>=0.5.2; python_version < "3.11"', + "xarray", ] -forecasting = [ - "arch>=5.6.0,<6.3.0", - "pmdarima>=1.8,!=1.8.1,<2.1; python_version < '3.12'", - "prophet>=1.1,<1.2; python_version < '3.12'", - "skpro>=2.0.0,<2.2.0", - "statsforecast>=0.5.2,<1.7.0; python_version < '3.12'", - "statsmodels>=0.12.1,<0.15", - "tbats>=1.1,<1.2; python_version < '3.12'", +annotation = [ + "hmmlearn<0.4,>=0.2.7", + 'numba<0.59,>=0.53; python_version < "3.12"', + "pyod<1.2,>=0.8", ] -networks = [ - "keras-self-attention>=0.51,<0.52", - "tensorflow>=2,<=2.14", +binder = [ + "jupyter", + "pandas<2.0.0", ] -param_est = [ - "seasonal>=0.3.1,<0.4", - "statsmodels>=0.12.1,<0.15", +classification = [ + "esig<0.10,>=0.9.7", + "mrsqm<0.1,>=0.0.3", + 'numba<0.59,>=0.53; python_version < "3.12"', + "tensorflow<=2.14,>=2", + 'tsfresh<0.21,>=0.17; python_version < "3.12"', ] -regression = [ - "numba>=0.53,<0.59; python_version < '3.12'", - "tensorflow>=2,<=2.14", +clustering = [ + 'numba<0.59,>=0.53; python_version < "3.12"', + "tslearn<0.6.3,>=0.5.2", ] -transformations = [ - "esig>=0.9.7,<0.10", - "filterpy>=1.4.5,<1.5", - "holidays>=0.29,<0.35", - "mne>=1.5,<1.6", - "numba>=0.53,<0.59; python_version < '3.12'", - "pycatch22>=0.4,<0.5", - "pykalman-bardo>=0.9.7,<0.10", - "statsmodels>=0.12.1,<0.15", - "stumpy>=1.5.1,<1.13; python_version < '3.12'", - "tsfresh>=0.17,<0.21; python_version < '3.12'", +cython_extras = [ + "mrseql", + 'mrsqm; python_version < "3.11"', + "numba<0.59", ] -all_extras = [ - "cloudpickle", - "dash!=2.9.0", - "dask", - "dtw-python", - "esig==0.9.7; python_version < '3.10'", - "filterpy>=1.4.5; python_version < '3.11'", - "gluonts>=0.9.0", - "h5py; python_version < '3.12'", - "hmmlearn>=0.2.7; python_version < '3.11'", - "holidays", - "keras-self-attention; python_version < '3.11'", - "kotsu>=0.3.1", - "matplotlib>=3.3.2", - "mne", - "numba>=0.53,<0.59; python_version < '3.12'", - "pmdarima>=1.8.0,!=1.8.1,<3.0.0; python_version < '3.12'", - "prophet>=1.1; python_version < '3.12'", - "pycatch22", - "pykalman-bardo>=0.9.7,<0.10", - "pyod>=0.8.0; python_version < '3.11'", - "scikit-optimize", - "scikit_posthocs>=0.6.5", - "seaborn>=0.11.0", - "seasonal", - "skpro>=2.0.0,<2.2.0", - "statsforecast>=0.5.2,<1.7.0; python_version < '3.12'", - "statsmodels>=0.12.1", - "stumpy>=1.5.1; python_version < '3.11'", - "tbats>=1.1.0; python_version < '3.12'", - "tensorflow; python_version < '3.11'", - "tsfresh>=0.17.0; python_version < '3.12'", - "tslearn>=0.5.2,<0.6.0; python_version < '3.11'", - "xarray", - "arch>=5.6.0,<6.3.0", +dev = [ + "backoff", + "httpx", + "pre-commit", + "pytest", + "pytest-cov", + "pytest-randomly", + "pytest-timeout", + "pytest-xdist", + "wheel", ] - -all_extras_pandas2 = [ - "cloudpickle", - "dash!=2.9.0", - "dask<2023.7.1", - "dtw-python", - "esig==0.9.7; python_version < '3.10'", - "filterpy>=1.4.5; python_version < '3.11'", - "gluonts>=0.9.0", - "h5py; python_version < '3.12'", - "hmmlearn>=0.2.7; python_version < '3.11'", - "holidays", - "keras-self-attention; python_version < '3.11'", - "kotsu>=0.3.1", - "matplotlib>=3.3.2", - "mne", - "numba>=0.53,<0.59; python_version < '3.12'", - "pmdarima>=1.8.0,!=1.8.1,<3.0.0; python_version < '3.12'", - "prophet>=1.1; python_version < '3.12'", - "pycatch22", - "pykalman-bardo>=0.9.7,<0.10", - "pyod>=0.8.0; python_version < '3.11'", - "scikit_posthocs>=0.6.5", - "seaborn>=0.11.0", - "seasonal", - "skpro>=2.0.0,<2.2.0", - "statsforecast>=0.5.2,<1.7.0; python_version < '3.12'", - "statsmodels>=0.12.1", - "stumpy>=1.5.1; python_version < '3.11'", - "tbats>=1.1.0; python_version < '3.12'", - "tensorflow; python_version < '3.11'", - "tsfresh>=0.17.0; python_version < '3.12'", - "tslearn>=0.5.2,<0.6.0; python_version < '3.11'", - "xarray", - "arch>=5.6.0,<6.3.0", +dl = [ + "tensorflow", ] - -cython_extras = [ - "mrseql", - "mrsqm; python_version < '3.11'", - "numba<0.59", +docs = [ + "jupyter", + "myst-parser", + "nbsphinx>=0.8.6", + "numpydoc", + "pydata-sphinx-theme", + "Sphinx!=7.2.0,<8.0.0", + "sphinx-copybutton", + "sphinx-design<0.6.0", + "sphinx-gallery<0.15.0", + "sphinx-issues<4.0.0", + "sphinx-version-warning", + "tabulate", ] - -dev = [ - "backoff", - "httpx", - "pre-commit", - "pytest", - "pytest-cov", - "pytest-randomly", - "pytest-timeout", - "pytest-xdist", - "wheel", +forecasting = [ + "arch<6.3.0,>=5.6", + 'pmdarima!=1.8.1,<2.1,>=1.8; python_version < "3.12"', + 'prophet<1.2,>=1.1; python_version < "3.12"', + "skpro<2.2.0,>=2", + 'statsforecast<1.7.0,>=0.5.2; python_version < "3.12"', + "statsmodels<0.15,>=0.12.1", + 'tbats<1.2,>=1.1; python_version < "3.12"', ] - mlflow = [ - "mlflow", + "mlflow", ] - mlflow_tests = [ - "boto3", - "botocore", - "mlflow", - "moto", + "boto3", + "botocore", + "mlflow", + "moto", ] - -binder = [ - "jupyter", - "pandas<2.0.0", +networks = [ + "keras-self-attention<0.52,>=0.51", + "tensorflow<=2.14,>=2", ] - -docs = [ - "jupyter", - "myst-parser", - "nbsphinx>=0.8.6", - "numpydoc", - "pydata-sphinx-theme", - "sphinx-copybutton", - "sphinx-issues<4.0.0", - "sphinx-gallery<0.15.0", - "sphinx-design<0.6.0", - "sphinx-version-warning", - "Sphinx<8.0.0,!=7.2.0", - "tabulate", +pandas1 = [ + "pandas<2.0.0", ] - -dl = [ - "tensorflow", +param_est = [ + "seasonal<0.4,>=0.3.1", + "statsmodels<0.15,>=0.12.1", ] - -pandas1 = [ - "pandas<2.0.0", +regression = [ + 'numba<0.59,>=0.53; python_version < "3.12"', + "tensorflow<=2.14,>=2", +] +transformations = [ + "esig<0.10,>=0.9.7", + "filterpy<1.5,>=1.4.5", + "holidays<0.35,>=0.29", + "mne<1.6,>=1.5", + 'numba<0.59,>=0.53; python_version < "3.12"', + "pycatch22<0.5,>=0.4", + "pykalman-bardo<0.10,>=0.9.7", + "statsmodels<0.15,>=0.12.1", + 'stumpy<1.13,>=1.5.1; python_version < "3.12"', + 'tsfresh<0.21,>=0.17; python_version < "3.12"', ] - [project.urls] -Homepage = "https://www.sktime.net" -Repository = "https://github.com/sktime/sktime" +"API Reference" = "https://www.sktime.net/en/stable/api_reference.html" Documentation = "https://www.sktime.net" Download = "https://pypi.org/project/sktime/#files" -"API Reference" = "https://www.sktime.net/en/stable/api_reference.html" +Homepage = "https://www.sktime.net" "Release Notes" = "https://www.sktime.net/en/stable/changelog.html" - -[project.license] -file = "LICENSE" +Repository = "https://github.com/sktime/sktime" [build-system] -requires = ["setuptools>61", "wheel", "toml", "build"] build-backend = "setuptools.build_meta" - -[tool.nbqa.exclude] -black = "^docs/source/examples/" -flake8 = "^docs/source/examples/" -isort = "^docs/source/examples/" - -[tool.setuptools] -zip-safe = true +requires = [ + "setuptools>61", +] [tool.setuptools.package-data] sktime = [ - "*.csv", - "*.csv.gz", - "*.arff", - "*.arff.gz", - "*.txt", - "*.ts", - "*.tsv", + "*.csv", + "*.csv.gz", + "*.arff", + "*.arff.gz", + "*.txt", + "*.ts", + "*.tsv", ] [tool.setuptools.packages.find] exclude = ["tests", "tests.*"] + +[tool.nbqa.exclude] +black = "^docs/source/examples/" +flake8 = "^docs/source/examples/" +isort = "^docs/source/examples/" From 4204ee610c5f88279805280a03717e8bbe6147bd Mon Sep 17 00:00:00 2001 From: Anirban Ray <39331844+yarnabrina@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:53:36 +0530 Subject: [PATCH 27/27] added new hook --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28437928a0b..9fc13897ee9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -81,3 +81,8 @@ repos: rev: 1.2.0 hooks: - id: pyproject-fmt + + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.15 + hooks: + - id: validate-pyproject