|
| 1 | +""" |
| 2 | +StatsForecastAutoMFLES |
| 3 | +----------- |
| 4 | +""" |
| 5 | + |
| 6 | +from typing import Optional |
| 7 | + |
| 8 | +from statsforecast.models import AutoMFLES as SFAutoMFLES |
| 9 | + |
| 10 | +from darts import TimeSeries |
| 11 | +from darts.logging import get_logger |
| 12 | +from darts.models.forecasting.forecasting_model import ( |
| 13 | + FutureCovariatesLocalForecastingModel, |
| 14 | +) |
| 15 | + |
| 16 | +logger = get_logger(__name__) |
| 17 | + |
| 18 | + |
| 19 | +class StatsForecastAutoMFLES(FutureCovariatesLocalForecastingModel): |
| 20 | + def __init__( |
| 21 | + self, *autoMFLES_args, add_encoders: Optional[dict] = None, **autoMFLES_kwargs |
| 22 | + ): |
| 23 | + """Auto-MFLES based on `Statsforecasts package |
| 24 | + <https://github.com/Nixtla/statsforecast>`_. |
| 25 | +
|
| 26 | + Automatically selects the best MFLES model from all feasible combinations of the parameters |
| 27 | + `seasonality_weights`, `smoother`, `ma`, and `seasonal_period`. Selection is made using the sMAPE by default. |
| 28 | +
|
| 29 | + We refer to the `statsforecast AutoMFLES documentation |
| 30 | + <https://nixtlaverse.nixtla.io/statsforecast/src/core/models.html#mfles>`_ |
| 31 | + for the exhaustive documentation of the arguments. |
| 32 | +
|
| 33 | + Parameters |
| 34 | + ---------- |
| 35 | + autoMFLES_args |
| 36 | + Positional arguments for ``statsforecasts.models.AutoMFLES``. |
| 37 | + add_encoders |
| 38 | + A large number of future covariates can be automatically generated with `add_encoders`. |
| 39 | + This can be done by adding multiple pre-defined index encoders and/or custom user-made functions that |
| 40 | + will be used as index encoders. Additionally, a transformer such as Darts' :class:`Scaler` can be added to |
| 41 | + transform the generated covariates. This happens all under one hood and only needs to be specified at |
| 42 | + model creation. |
| 43 | + Read :meth:`SequentialEncoder <darts.dataprocessing.encoders.SequentialEncoder>` to find out more about |
| 44 | + ``add_encoders``. Default: ``None``. An example showing some of ``add_encoders`` features: |
| 45 | +
|
| 46 | + .. highlight:: python |
| 47 | + .. code-block:: python |
| 48 | +
|
| 49 | + def encode_year(idx): |
| 50 | + return (idx.year - 1950) / 50 |
| 51 | +
|
| 52 | + add_encoders={ |
| 53 | + 'cyclic': {'future': ['month']}, |
| 54 | + 'datetime_attribute': {'future': ['hour', 'dayofweek']}, |
| 55 | + 'position': {'future': ['relative']}, |
| 56 | + 'custom': {'future': [encode_year]}, |
| 57 | + 'transformer': Scaler(), |
| 58 | + 'tz': 'CET' |
| 59 | + } |
| 60 | + .. |
| 61 | + autoMFLES_kwargs |
| 62 | + Keyword arguments for ``statsforecasts.models.AutoMFLES``. |
| 63 | +
|
| 64 | + Examples |
| 65 | + -------- |
| 66 | + >>> from darts.datasets import AirPassengersDataset |
| 67 | + >>> from darts.models import StatsForecastAutoMFLES |
| 68 | + >>> from darts.utils.timeseries_generation import datetime_attribute_timeseries |
| 69 | + >>> series = AirPassengersDataset().load() |
| 70 | + >>> # optionally, use some future covariates; e.g. the value of the month encoded as a sine and cosine series |
| 71 | + >>> future_cov = datetime_attribute_timeseries(series, "month", cyclic=True, add_length=6) |
| 72 | + >>> # define StatsForecastAutoMFLES parameters |
| 73 | + >>> model = StatsForecastAutoMFLES(season_length=12, test_size=12) |
| 74 | + >>> model.fit(series, future_covariates=future_cov) |
| 75 | + >>> pred = model.predict(6, future_covariates=future_cov) |
| 76 | + >>> pred.values() |
| 77 | + array([[466.03298745], |
| 78 | + [450.76192105], |
| 79 | + [517.6342497 ], |
| 80 | + [511.62988828], |
| 81 | + [520.15305998], |
| 82 | + [593.38690019]]) |
| 83 | + """ |
| 84 | + if "prediction_intervals" in autoMFLES_kwargs: |
| 85 | + logger.warning( |
| 86 | + "StatsForecastAutoMFLES does not support probabilistic forecasting. " |
| 87 | + "`prediction_intervals` will be ignored." |
| 88 | + ) |
| 89 | + |
| 90 | + super().__init__(add_encoders=add_encoders) |
| 91 | + self.model = SFAutoMFLES(*autoMFLES_args, **autoMFLES_kwargs) |
| 92 | + |
| 93 | + def _fit(self, series: TimeSeries, future_covariates: Optional[TimeSeries] = None): |
| 94 | + super()._fit(series, future_covariates) |
| 95 | + self._assert_univariate(series) |
| 96 | + series = self.training_series |
| 97 | + self.model.fit( |
| 98 | + series.values(copy=False).flatten(), |
| 99 | + X=future_covariates.values(copy=False) if future_covariates else None, |
| 100 | + ) |
| 101 | + return self |
| 102 | + |
| 103 | + def _predict( |
| 104 | + self, |
| 105 | + n: int, |
| 106 | + future_covariates: Optional[TimeSeries] = None, |
| 107 | + num_samples: int = 1, |
| 108 | + verbose: bool = False, |
| 109 | + ): |
| 110 | + super()._predict(n, future_covariates, num_samples) |
| 111 | + forecast_dict = self.model.predict( |
| 112 | + h=n, |
| 113 | + X=future_covariates.values(copy=False) if future_covariates else None, |
| 114 | + level=None, |
| 115 | + ) |
| 116 | + |
| 117 | + return self._build_forecast_series(forecast_dict["mean"]) |
| 118 | + |
| 119 | + @property |
| 120 | + def supports_multivariate(self) -> bool: |
| 121 | + return False |
| 122 | + |
| 123 | + @property |
| 124 | + def min_train_series_length(self) -> int: |
| 125 | + return 10 |
| 126 | + |
| 127 | + @property |
| 128 | + def _supports_range_index(self) -> bool: |
| 129 | + return True |
| 130 | + |
| 131 | + @property |
| 132 | + def supports_probabilistic_prediction(self) -> bool: |
| 133 | + return False |
0 commit comments