autogluon.timeseries 1.0.1b20240304__py3-none-any.whl → 1.4.1b20251210__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of autogluon.timeseries might be problematic. Click here for more details.

Files changed (108) hide show
  1. autogluon/timeseries/configs/__init__.py +3 -2
  2. autogluon/timeseries/configs/hyperparameter_presets.py +62 -0
  3. autogluon/timeseries/configs/predictor_presets.py +84 -0
  4. autogluon/timeseries/dataset/ts_dataframe.py +339 -186
  5. autogluon/timeseries/learner.py +192 -60
  6. autogluon/timeseries/metrics/__init__.py +55 -11
  7. autogluon/timeseries/metrics/abstract.py +96 -25
  8. autogluon/timeseries/metrics/point.py +186 -39
  9. autogluon/timeseries/metrics/quantile.py +47 -20
  10. autogluon/timeseries/metrics/utils.py +6 -6
  11. autogluon/timeseries/models/__init__.py +13 -7
  12. autogluon/timeseries/models/abstract/__init__.py +2 -2
  13. autogluon/timeseries/models/abstract/abstract_timeseries_model.py +533 -273
  14. autogluon/timeseries/models/abstract/model_trial.py +10 -10
  15. autogluon/timeseries/models/abstract/tunable.py +189 -0
  16. autogluon/timeseries/models/autogluon_tabular/__init__.py +2 -0
  17. autogluon/timeseries/models/autogluon_tabular/mlforecast.py +369 -215
  18. autogluon/timeseries/models/autogluon_tabular/per_step.py +513 -0
  19. autogluon/timeseries/models/autogluon_tabular/transforms.py +67 -0
  20. autogluon/timeseries/models/autogluon_tabular/utils.py +3 -51
  21. autogluon/timeseries/models/chronos/__init__.py +4 -0
  22. autogluon/timeseries/models/chronos/chronos2.py +361 -0
  23. autogluon/timeseries/models/chronos/model.py +738 -0
  24. autogluon/timeseries/models/chronos/utils.py +369 -0
  25. autogluon/timeseries/models/ensemble/__init__.py +35 -2
  26. autogluon/timeseries/models/ensemble/{abstract_timeseries_ensemble.py → abstract.py} +50 -26
  27. autogluon/timeseries/models/ensemble/array_based/__init__.py +3 -0
  28. autogluon/timeseries/models/ensemble/array_based/abstract.py +236 -0
  29. autogluon/timeseries/models/ensemble/array_based/models.py +73 -0
  30. autogluon/timeseries/models/ensemble/array_based/regressor/__init__.py +12 -0
  31. autogluon/timeseries/models/ensemble/array_based/regressor/abstract.py +88 -0
  32. autogluon/timeseries/models/ensemble/array_based/regressor/linear_stacker.py +167 -0
  33. autogluon/timeseries/models/ensemble/array_based/regressor/per_quantile_tabular.py +94 -0
  34. autogluon/timeseries/models/ensemble/array_based/regressor/tabular.py +107 -0
  35. autogluon/timeseries/models/ensemble/ensemble_selection.py +167 -0
  36. autogluon/timeseries/models/ensemble/per_item_greedy.py +162 -0
  37. autogluon/timeseries/models/ensemble/weighted/__init__.py +8 -0
  38. autogluon/timeseries/models/ensemble/weighted/abstract.py +40 -0
  39. autogluon/timeseries/models/ensemble/weighted/basic.py +78 -0
  40. autogluon/timeseries/models/ensemble/weighted/greedy.py +57 -0
  41. autogluon/timeseries/models/gluonts/__init__.py +3 -1
  42. autogluon/timeseries/models/gluonts/abstract.py +583 -0
  43. autogluon/timeseries/models/gluonts/dataset.py +109 -0
  44. autogluon/timeseries/models/gluonts/{torch/models.py → models.py} +185 -44
  45. autogluon/timeseries/models/local/__init__.py +1 -10
  46. autogluon/timeseries/models/local/abstract_local_model.py +150 -97
  47. autogluon/timeseries/models/local/naive.py +31 -23
  48. autogluon/timeseries/models/local/npts.py +6 -2
  49. autogluon/timeseries/models/local/statsforecast.py +99 -112
  50. autogluon/timeseries/models/multi_window/multi_window_model.py +99 -40
  51. autogluon/timeseries/models/registry.py +64 -0
  52. autogluon/timeseries/models/toto/__init__.py +3 -0
  53. autogluon/timeseries/models/toto/_internal/__init__.py +9 -0
  54. autogluon/timeseries/models/toto/_internal/backbone/__init__.py +3 -0
  55. autogluon/timeseries/models/toto/_internal/backbone/attention.py +196 -0
  56. autogluon/timeseries/models/toto/_internal/backbone/backbone.py +262 -0
  57. autogluon/timeseries/models/toto/_internal/backbone/distribution.py +70 -0
  58. autogluon/timeseries/models/toto/_internal/backbone/kvcache.py +136 -0
  59. autogluon/timeseries/models/toto/_internal/backbone/rope.py +89 -0
  60. autogluon/timeseries/models/toto/_internal/backbone/rotary_embedding_torch.py +342 -0
  61. autogluon/timeseries/models/toto/_internal/backbone/scaler.py +305 -0
  62. autogluon/timeseries/models/toto/_internal/backbone/transformer.py +333 -0
  63. autogluon/timeseries/models/toto/_internal/dataset.py +165 -0
  64. autogluon/timeseries/models/toto/_internal/forecaster.py +423 -0
  65. autogluon/timeseries/models/toto/dataloader.py +108 -0
  66. autogluon/timeseries/models/toto/hf_pretrained_model.py +118 -0
  67. autogluon/timeseries/models/toto/model.py +236 -0
  68. autogluon/timeseries/predictor.py +826 -305
  69. autogluon/timeseries/regressor.py +253 -0
  70. autogluon/timeseries/splitter.py +10 -31
  71. autogluon/timeseries/trainer/__init__.py +2 -3
  72. autogluon/timeseries/trainer/ensemble_composer.py +439 -0
  73. autogluon/timeseries/trainer/model_set_builder.py +256 -0
  74. autogluon/timeseries/trainer/prediction_cache.py +149 -0
  75. autogluon/timeseries/trainer/trainer.py +1298 -0
  76. autogluon/timeseries/trainer/utils.py +17 -0
  77. autogluon/timeseries/transforms/__init__.py +2 -0
  78. autogluon/timeseries/transforms/covariate_scaler.py +164 -0
  79. autogluon/timeseries/transforms/target_scaler.py +149 -0
  80. autogluon/timeseries/utils/constants.py +10 -0
  81. autogluon/timeseries/utils/datetime/base.py +38 -20
  82. autogluon/timeseries/utils/datetime/lags.py +18 -16
  83. autogluon/timeseries/utils/datetime/seasonality.py +14 -14
  84. autogluon/timeseries/utils/datetime/time_features.py +17 -14
  85. autogluon/timeseries/utils/features.py +317 -53
  86. autogluon/timeseries/utils/forecast.py +31 -17
  87. autogluon/timeseries/utils/timer.py +173 -0
  88. autogluon/timeseries/utils/warning_filters.py +44 -6
  89. autogluon/timeseries/version.py +2 -1
  90. autogluon.timeseries-1.4.1b20251210-py3.11-nspkg.pth +1 -0
  91. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info}/METADATA +71 -47
  92. autogluon_timeseries-1.4.1b20251210.dist-info/RECORD +103 -0
  93. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info}/WHEEL +1 -1
  94. autogluon/timeseries/configs/presets_configs.py +0 -11
  95. autogluon/timeseries/evaluator.py +0 -6
  96. autogluon/timeseries/models/ensemble/greedy_ensemble.py +0 -170
  97. autogluon/timeseries/models/gluonts/abstract_gluonts.py +0 -550
  98. autogluon/timeseries/models/gluonts/torch/__init__.py +0 -0
  99. autogluon/timeseries/models/presets.py +0 -325
  100. autogluon/timeseries/trainer/abstract_trainer.py +0 -1144
  101. autogluon/timeseries/trainer/auto_trainer.py +0 -74
  102. autogluon.timeseries-1.0.1b20240304-py3.8-nspkg.pth +0 -1
  103. autogluon.timeseries-1.0.1b20240304.dist-info/RECORD +0 -58
  104. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info/licenses}/LICENSE +0 -0
  105. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info/licenses}/NOTICE +0 -0
  106. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info}/namespace_packages.txt +0 -0
  107. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info}/top_level.txt +0 -0
  108. {autogluon.timeseries-1.0.1b20240304.dist-info → autogluon_timeseries-1.4.1b20251210.dist-info}/zip-safe +0 -0
@@ -5,46 +5,33 @@ import os
5
5
  import pprint
6
6
  import time
7
7
  from pathlib import Path
8
- from typing import Any, Dict, List, Optional, Tuple, Type, Union
8
+ from typing import Any, Literal, Type, cast, overload
9
9
 
10
10
  import numpy as np
11
11
  import pandas as pd
12
12
 
13
- from autogluon.common.utils.deprecated_utils import Deprecated
14
- from autogluon.common.utils.log_utils import add_log_to_file, set_logger_verbosity
13
+ from autogluon.common.utils.log_utils import (
14
+ add_log_to_file,
15
+ set_logger_verbosity,
16
+ warn_if_mlflow_autologging_is_enabled,
17
+ )
15
18
  from autogluon.common.utils.system_info import get_ag_system_info
16
19
  from autogluon.common.utils.utils import check_saved_predictor_version, setup_outputdir
17
20
  from autogluon.core.utils.decorators import apply_presets
18
21
  from autogluon.core.utils.loaders import load_pkl, load_str
19
22
  from autogluon.core.utils.savers import save_pkl, save_str
20
23
  from autogluon.timeseries import __version__ as current_ag_version
21
- from autogluon.timeseries.configs import TIMESERIES_PRESETS_CONFIGS
22
- from autogluon.timeseries.dataset.ts_dataframe import ITEMID, TimeSeriesDataFrame
23
- from autogluon.timeseries.learner import AbstractLearner, TimeSeriesLearner
24
+ from autogluon.timeseries.configs import get_predictor_presets
25
+ from autogluon.timeseries.dataset import TimeSeriesDataFrame
26
+ from autogluon.timeseries.learner import TimeSeriesLearner
24
27
  from autogluon.timeseries.metrics import TimeSeriesScorer, check_get_evaluation_metric
25
- from autogluon.timeseries.splitter import ExpandingWindowSplitter
26
- from autogluon.timeseries.trainer import AbstractTimeSeriesTrainer
28
+ from autogluon.timeseries.trainer import TimeSeriesTrainer
29
+ from autogluon.timeseries.utils.forecast import make_future_data_frame
27
30
 
28
31
  logger = logging.getLogger("autogluon.timeseries")
29
32
 
30
33
 
31
- class TimeSeriesPredictorDeprecatedMixin:
32
- """Contains deprecated methods from TimeSeriesPredictor that shouldn't show up in API documentation."""
33
-
34
- @Deprecated(min_version_to_warn="0.8.3", min_version_to_error="1.2", version_to_remove="1.2", new="evaluate")
35
- def score(self, *args, **kwargs):
36
- return self.evaluate(*args, **kwargs)
37
-
38
- @Deprecated(min_version_to_warn="0.8.3", min_version_to_error="1.2", version_to_remove="1.2", new="model_best")
39
- def get_model_best(self) -> str:
40
- return self.model_best
41
-
42
- @Deprecated(min_version_to_warn="0.8.3", min_version_to_error="1.2", version_to_remove="1.2", new="model_names")
43
- def get_model_names(self) -> str:
44
- return self.model_names()
45
-
46
-
47
- class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
34
+ class TimeSeriesPredictor:
48
35
  """AutoGluon ``TimeSeriesPredictor`` predicts future values of multiple related time series.
49
36
 
50
37
  ``TimeSeriesPredictor`` provides probabilistic (quantile) multi-step-ahead forecasts for univariate time series.
@@ -69,7 +56,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
69
56
  models that predict up to 3 days into the future from the most recent observation.
70
57
  freq : str, optional
71
58
  Frequency of the time series data (see `pandas documentation <https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases>`_
72
- for available frequencies). For example, ``"D"`` for daily data or ``"H"`` for hourly data.
59
+ for available frequencies). For example, ``"D"`` for daily data or ``"h"`` for hourly data.
73
60
 
74
61
  By default, the predictor will attempt to automatically infer the frequency from the data. This argument should
75
62
  only be set in two cases:
@@ -79,7 +66,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
79
66
 
80
67
  If ``freq`` is provided when creating the predictor, all data passed to the predictor will be automatically
81
68
  resampled at this frequency.
82
- eval_metric : Union[str, TimeSeriesScorer], default = "WQL"
69
+ eval_metric : str | TimeSeriesScorer, default = "WQL"
83
70
  Metric by which predictions will be ultimately evaluated on future test data. AutoGluon tunes hyperparameters
84
71
  in order to improve this metric on validation data, and ranks models (on validation data) according to this
85
72
  metric.
@@ -105,23 +92,29 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
105
92
  eval_metric_seasonal_period : int, optional
106
93
  Seasonal period used to compute some evaluation metrics such as mean absolute scaled error (MASE). Defaults to
107
94
  ``None``, in which case the seasonal period is computed based on the data frequency.
108
- known_covariates_names: List[str], optional
95
+ horizon_weight : list[float], optional
96
+ Weight assigned to each time step in the forecast horizon when computing the ``eval_metric``. If provided, this
97
+ must be a list with ``prediction_length`` non-negative values, where at least some values are greater than zero.
98
+ AutoGluon will automatically normalize the weights so that they sum up to ``prediction_length``. By default, all
99
+ time steps in the forecast horizon have the same weight, which is equivalent to setting ``horizon_weight = [1] * prediction_length``.
100
+
101
+ This parameter only affects model selection and ensemble construction; it has no effect on the loss function of
102
+ the individual forecasting models.
103
+ known_covariates_names: list[str], optional
109
104
  Names of the covariates that are known in advance for all time steps in the forecast horizon. These are also
110
105
  known as dynamic features, exogenous variables, additional regressors or related time series. Examples of such
111
106
  covariates include holidays, promotions or weather forecasts.
112
107
 
113
- Currently, only numeric (float of integer dtype) are supported.
114
-
115
108
  If ``known_covariates_names`` are provided, then:
116
109
 
117
- - :meth:`~autogluon.timeseries.TimeSeriesPredictor.fit`, :meth:`~autogluon.timeseries.TimeSeriesPredictor.evaluate`, and :meth:`~autogluon.timeseries.TimeSeriesPredictor.leaderboard` will expect a data frame with columns listed in ``known_covariates_names`` (in addition to the ``target`` column).
110
+ - :meth:`~autogluon.timeseries.TimeSeriesPredictor.fit`, :meth:`~autogluon.timeseries.TimeSeriesPredictor.evaluate`, and :meth:`~autogluon.timeseries.TimeSeriesPredictor.leaderboard` will expect a dataframe with columns listed in ``known_covariates_names`` (in addition to the ``target`` column).
118
111
  - :meth:`~autogluon.timeseries.TimeSeriesPredictor.predict` will expect an additional keyword argument ``known_covariates`` containing the future values of the known covariates in ``TimeSeriesDataFrame`` format.
119
112
 
120
- quantile_levels : List[float], optional
113
+ quantile_levels : list[float], optional
121
114
  List of increasing decimals that specifies which quantiles should be estimated when making distributional
122
115
  forecasts. Defaults to ``[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]``.
123
116
  path : str or pathlib.Path, optional
124
- Path to the directory where models and intermediate outputs will be saved. Defaults to a timestamped folder
117
+ Path to the local directory where models and intermediate outputs will be saved. Defaults to a timestamped folder
125
118
  ``AutogluonModels/ag-[TIMESTAMP]`` that will be created in the working directory.
126
119
  verbosity : int, default = 2
127
120
  Verbosity levels range from 0 to 4 and control how much information is printed to stdout. Higher levels
@@ -131,10 +124,10 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
131
124
  debug messages from AutoGluon and all logging in dependencies (GluonTS, PyTorch Lightning, AutoGluon-Tabular, etc.)
132
125
  log_to_file: bool, default = True
133
126
  Whether to save the logs into a file for later reference
134
- log_file_path: Union[str, Path], default = "auto"
127
+ log_file_path: str | Path, default = "auto"
135
128
  File path to save the logs.
136
- If auto, logs will be saved under `predictor_path/logs/predictor_log.txt`.
137
- Will be ignored if `log_to_file` is set to False
129
+ If auto, logs will be saved under ``predictor_path/logs/predictor_log.txt``.
130
+ Will be ignored if ``log_to_file`` is set to False
138
131
  cache_predictions : bool, default = True
139
132
  If True, the predictor will cache and reuse the predictions made by individual models whenever
140
133
  :meth:`~autogluon.timeseries.TimeSeriesPredictor.predict`, :meth:`~autogluon.timeseries.TimeSeriesPredictor.leaderboard`,
@@ -145,32 +138,37 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
145
138
  Alias for :attr:`target`.
146
139
  """
147
140
 
141
+ _learner_type = TimeSeriesLearner
148
142
  predictor_file_name = "predictor.pkl"
149
- _predictor_version_file_name = "__version__"
143
+ _predictor_version_file_name = "version.txt"
150
144
  _predictor_log_file_name = "predictor_log.txt"
151
145
 
152
146
  def __init__(
153
147
  self,
154
- target: Optional[str] = None,
155
- known_covariates_names: Optional[List[str]] = None,
148
+ target: str | None = None,
149
+ known_covariates_names: list[str] | None = None,
156
150
  prediction_length: int = 1,
157
- freq: str = None,
158
- eval_metric: Union[str, TimeSeriesScorer, None] = None,
159
- eval_metric_seasonal_period: Optional[int] = None,
160
- path: Optional[Union[str, Path]] = None,
151
+ freq: str | None = None,
152
+ eval_metric: str | TimeSeriesScorer | None = None,
153
+ eval_metric_seasonal_period: int | None = None,
154
+ horizon_weight: list[float] | None = None,
155
+ path: str | Path | None = None,
161
156
  verbosity: int = 2,
162
157
  log_to_file: bool = True,
163
- log_file_path: Union[str, Path] = "auto",
164
- quantile_levels: Optional[List[float]] = None,
158
+ log_file_path: str | Path = "auto",
159
+ quantile_levels: list[float] | None = None,
165
160
  cache_predictions: bool = True,
166
- learner_type: Optional[Type[AbstractLearner]] = None,
167
- learner_kwargs: Optional[dict] = None,
168
- label: Optional[str] = None,
161
+ label: str | None = None,
169
162
  **kwargs,
170
163
  ):
171
164
  self.verbosity = verbosity
172
165
  set_logger_verbosity(self.verbosity, logger=logger)
173
166
  self.path = setup_outputdir(path)
167
+ if self.path.lower().startswith("s3://"):
168
+ logger.warning(
169
+ "Warning: S3 paths are not supported for the `path` argument in TimeSeriesPredictor. "
170
+ "Use a local path and upload the trained predictor to S3 manually if needed"
171
+ )
174
172
  self._setup_log_to_file(log_to_file=log_to_file, log_file_path=log_file_path)
175
173
 
176
174
  self.cache_predictions = cache_predictions
@@ -190,59 +188,56 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
190
188
  raise ValueError(f"Target column {self.target} cannot be one of the known covariates.")
191
189
  self.known_covariates_names = list(known_covariates_names)
192
190
 
193
- self.prediction_length = prediction_length
191
+ self.prediction_length = int(prediction_length)
194
192
  # For each validation fold, all time series in training set must have length >= _min_train_length
195
193
  self._min_train_length = max(self.prediction_length + 1, 5)
196
194
  self.freq = freq
197
195
  if self.freq is not None:
198
- # Standardize frequency string (e.g., "min" -> "T", "Y" -> "A-DEC")
199
- std_freq = pd.tseries.frequencies.to_offset(self.freq).freqstr
196
+ # Standardize frequency string (e.g., "T" -> "min", "Y" -> "YE")
197
+ offset = pd.tseries.frequencies.to_offset(self.freq)
198
+ assert offset is not None
199
+ std_freq = offset.freqstr
200
200
  if std_freq != str(self.freq):
201
201
  logger.info(f"Frequency '{self.freq}' stored as '{std_freq}'")
202
202
  self.freq = std_freq
203
- self.eval_metric = check_get_evaluation_metric(eval_metric)
204
- self.eval_metric_seasonal_period = eval_metric_seasonal_period
203
+ self.eval_metric: TimeSeriesScorer = check_get_evaluation_metric(
204
+ eval_metric,
205
+ prediction_length=prediction_length,
206
+ seasonal_period=eval_metric_seasonal_period,
207
+ horizon_weight=horizon_weight,
208
+ )
205
209
  if quantile_levels is None:
206
210
  quantile_levels = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
207
211
  self.quantile_levels = sorted(quantile_levels)
208
-
209
- if learner_kwargs is None:
210
- learner_kwargs = {}
211
- learner_kwargs = learner_kwargs.copy()
212
- learner_kwargs.update(
213
- dict(
214
- path_context=self.path,
215
- eval_metric=eval_metric,
216
- eval_metric_seasonal_period=eval_metric_seasonal_period,
217
- target=self.target,
218
- known_covariates_names=self.known_covariates_names,
219
- prediction_length=self.prediction_length,
220
- quantile_levels=self.quantile_levels,
221
- cache_predictions=self.cache_predictions,
222
- )
212
+ self._learner: TimeSeriesLearner = self._learner_type(
213
+ path_context=self.path,
214
+ eval_metric=self.eval_metric,
215
+ target=self.target,
216
+ known_covariates_names=self.known_covariates_names,
217
+ prediction_length=self.prediction_length,
218
+ quantile_levels=self.quantile_levels,
219
+ cache_predictions=self.cache_predictions,
220
+ ensemble_model_type=kwargs.pop("ensemble_model_type", None),
223
221
  )
224
- # Using `TimeSeriesLearner` as default argument breaks doc generation with Sphnix
225
- if learner_type is None:
226
- learner_type = TimeSeriesLearner
227
- self._learner: AbstractLearner = learner_type(**learner_kwargs)
228
- self._learner_type = type(self._learner)
229
222
 
230
- if "ignore_time_index" in kwargs:
231
- raise TypeError(
232
- "`ignore_time_index` argument to TimeSeriesPredictor.__init__() has been deprecated.\n"
233
- "If your data has irregular timestamps, please either 1) specify the desired regular frequency when "
234
- "creating the predictor as `TimeSeriesPredictor(freq=...)` or 2) manually convert timestamps to "
235
- "regular frequency with `data.convert_frequency(freq=...)`."
236
- )
237
223
  if len(kwargs) > 0:
238
224
  for key in kwargs:
239
225
  raise TypeError(f"TimeSeriesPredictor.__init__() got an unexpected keyword argument '{key}'")
240
226
 
241
227
  @property
242
- def _trainer(self) -> AbstractTimeSeriesTrainer:
228
+ def _trainer(self) -> TimeSeriesTrainer:
243
229
  return self._learner.load_trainer() # noqa
244
230
 
245
- def _setup_log_to_file(self, log_to_file: bool, log_file_path: Union[str, Path]) -> None:
231
+ @property
232
+ def is_fit(self) -> bool:
233
+ return self._learner.is_fit
234
+
235
+ def _assert_is_fit(self, method_name: str) -> None:
236
+ """Check if predictor is fit and raise AssertionError with informative message if not."""
237
+ if not self.is_fit:
238
+ raise AssertionError(f"Predictor is not fit. Call `.fit` before calling `.{method_name}`. ")
239
+
240
+ def _setup_log_to_file(self, log_to_file: bool, log_file_path: str | Path) -> None:
246
241
  if log_to_file:
247
242
  if log_file_path == "auto":
248
243
  log_file_path = os.path.join(self.path, "logs", self._predictor_log_file_name)
@@ -252,14 +247,14 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
252
247
 
253
248
  def _to_data_frame(
254
249
  self,
255
- data: Union[TimeSeriesDataFrame, pd.DataFrame, str],
250
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
256
251
  name: str = "data",
257
- ) -> "TimeSeriesDataFrame":
252
+ ) -> TimeSeriesDataFrame:
258
253
  if isinstance(data, TimeSeriesDataFrame):
259
254
  return data
260
- elif isinstance(data, (pd.DataFrame, str)):
255
+ elif isinstance(data, (pd.DataFrame, Path, str)):
261
256
  try:
262
- data = TimeSeriesDataFrame(data)
257
+ data = TimeSeriesDataFrame(data) # type: ignore
263
258
  except:
264
259
  raise ValueError(
265
260
  f"Provided {name} of type {type(data)} cannot be automatically converted to a TimeSeriesDataFrame."
@@ -267,23 +262,23 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
267
262
  return data
268
263
  else:
269
264
  raise TypeError(
270
- f"{name} must be a TimeSeriesDataFrame or pandas.DataFrame or string (path to data) "
265
+ f"{name} must be a TimeSeriesDataFrame, pandas.DataFrame, pathlib.Path or string (path to data) "
271
266
  f"but received an object of type {type(data)}."
272
267
  )
273
268
 
274
269
  def _check_and_prepare_data_frame(
275
270
  self,
276
- data: Union[TimeSeriesDataFrame, pd.DataFrame, str],
271
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
277
272
  name: str = "data",
278
273
  ) -> TimeSeriesDataFrame:
279
- """Ensure that TimeSeriesDataFrame has a sorted index, valid frequency, and contains no missing values.
274
+ """Ensure that TimeSeriesDataFrame has a sorted index and a valid frequency.
280
275
 
281
276
  If self.freq is None, then self.freq of the predictor will be set to the frequency of the data.
282
277
 
283
278
  Parameters
284
279
  ----------
285
- data : Union[TimeSeriesDataFrame, pd.DataFrame, str]
286
- Data as a data frame or path to file storing the data.
280
+ data : TimeSeriesDataFrame | pd.DataFrame | Path | str
281
+ Data as a dataframe or path to file storing the data.
287
282
  name : str
288
283
  Name of the data that will be used in log messages (e.g., 'train_data', 'tuning_data', or 'data').
289
284
 
@@ -292,60 +287,77 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
292
287
  df : TimeSeriesDataFrame
293
288
  Preprocessed data in TimeSeriesDataFrame format.
294
289
  """
295
- df = self._to_data_frame(data, name=name)
296
- df = df.astype({self.target: "float32"})
290
+ df: TimeSeriesDataFrame = self._to_data_frame(data, name=name)
291
+ if not pd.api.types.is_numeric_dtype(df[self.target]):
292
+ raise ValueError(f"Target column {name}['{self.target}'] has a non-numeric dtype {df[self.target].dtype}")
293
+ # Assign makes a copy, so future operations can be performed in-place
294
+ df = df.assign(**{self.target: df[self.target].astype("float64")})
295
+ df.replace(to_replace=[float("-inf"), float("inf")], value=float("nan"), inplace=True)
296
+
297
297
  # MultiIndex.is_monotonic_increasing checks if index is sorted by ["item_id", "timestamp"]
298
298
  if not df.index.is_monotonic_increasing:
299
299
  df = df.sort_index()
300
- df._cached_freq = None # in case frequency was incorrectly cached as IRREGULAR_TIME_INDEX_FREQSTR
301
300
 
302
301
  # Ensure that data has a regular frequency that matches the predictor frequency
303
302
  if self.freq is None:
304
- if df.freq is None:
303
+ try:
304
+ # Use all items for inferring the frequency
305
+ data_freq = df.infer_frequency(num_items=None, raise_if_irregular=True)
306
+ except ValueError:
305
307
  raise ValueError(
306
308
  f"Frequency of {name} is not provided and cannot be inferred. Please set the expected data "
307
309
  f"frequency when creating the predictor with `TimeSeriesPredictor(freq=...)` or ensure that "
308
310
  f"the data has a regular time index with `{name}.convert_frequency(freq=...)`"
309
311
  )
310
312
  else:
311
- self.freq = df.freq
312
- logger.info(f"Inferred time series frequency: '{df.freq}'")
313
+ self.freq = data_freq
314
+ logger.info(f"Inferred time series frequency: '{data_freq}'")
313
315
  else:
314
- if df.freq != self.freq:
315
- logger.warning(f"{name} with frequency '{df.freq}' has been resampled to frequency '{self.freq}'.")
316
+ data_freq = df.infer_frequency(num_items=None)
317
+ if data_freq != self.freq:
318
+ logger.warning(f"{name} with frequency '{data_freq}' has been resampled to frequency '{self.freq}'.")
316
319
  df = df.convert_frequency(freq=self.freq)
317
-
318
- # Fill missing values
319
- if df.isna().values.any():
320
- # FIXME: Do not automatically fill NaNs here, handle missing values at the level of individual models.
321
- # FIXME: Current solution leads to incorrect metric computation if missing values are present
322
- logger.warning(
323
- f"{name} contains missing values represented by NaN. "
324
- f"They have been filled by carrying forward the last valid observation."
325
- )
326
- df = df.fill_missing_values()
327
- if df.isna().values.any():
328
- raise ValueError(f"Some time series in {name} consist completely of NaN values. Please remove them.")
329
320
  return df
330
321
 
331
- def _check_data_for_evaluation(self, data: TimeSeriesDataFrame, name: str = "data"):
332
- """Make sure that provided evaluation data includes both historic and future time series values."""
333
- if data.num_timesteps_per_item().min() <= self.prediction_length:
322
+ def _check_and_prepare_data_frame_for_evaluation(
323
+ self, data: TimeSeriesDataFrame, cutoff: int | None = None, name: str = "data"
324
+ ) -> TimeSeriesDataFrame:
325
+ """
326
+ Make sure that provided evaluation data includes both historical and future time series values.
327
+ Slices the dataframe based on cutoff, if needed.
328
+ """
329
+ cutoff = -1 * self.prediction_length if cutoff is None else cutoff
330
+ if not (isinstance(cutoff, int) and cutoff <= -self.prediction_length):
331
+ raise ValueError(f"`cutoff` should be a negative integer <= -prediction_length, got: {cutoff=}")
332
+
333
+ expected_length = -cutoff
334
+
335
+ if data.num_timesteps_per_item().min() <= expected_length:
336
+ var_name = "-cutoff" if expected_length > self.prediction_length else "prediction_length"
334
337
  raise ValueError(
335
- f"Cannot reserve last prediction_length={self.prediction_length} time steps for evaluation in some "
336
- f"time series in {name}. Please make sure that {name} includes both historic and future data, and that"
337
- f"all time series have length > prediction_length (at least {self.prediction_length + 1})"
338
+ f"Cannot reserve last {expected_length} time steps for evaluation in some "
339
+ f"time series in {name}. Please make sure that {name} includes both historical and future data, and that"
340
+ f"all time series have length > {var_name} (at least {expected_length + 1})"
338
341
  )
339
342
 
340
- @staticmethod
341
- def _get_dataset_stats(data: TimeSeriesDataFrame) -> str:
343
+ if cutoff < -self.prediction_length:
344
+ data = data.slice_by_timestep(None, cutoff + self.prediction_length)
345
+
346
+ return data
347
+
348
+ def _get_dataset_stats(self, data: TimeSeriesDataFrame) -> str:
342
349
  ts_lengths = data.num_timesteps_per_item()
343
- median_length = int(ts_lengths.median())
350
+ median_length = ts_lengths.median()
344
351
  min_length = ts_lengths.min()
345
352
  max_length = ts_lengths.max()
353
+ missing_value_fraction = data[self.target].isna().mean()
354
+ if missing_value_fraction > 0:
355
+ missing_value_fraction_str = f" (NaN fraction={missing_value_fraction:.1%})"
356
+ else:
357
+ missing_value_fraction_str = ""
346
358
  return (
347
- f"{len(data)} rows, {data.num_items} time series. "
348
- f"Median time series length is {median_length} (min={min_length}, max={max_length}). "
359
+ f"{len(data)} rows{missing_value_fraction_str}, {data.num_items} time series. "
360
+ f"Median time series length is {median_length:.0f} (min={min_length}, max={max_length}). "
349
361
  )
350
362
 
351
363
  def _reduce_num_val_windows_if_necessary(
@@ -374,65 +386,72 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
374
386
  )
375
387
  return new_num_val_windows
376
388
 
377
- def _filter_short_series(
389
+ def _filter_useless_train_data(
378
390
  self,
379
391
  train_data: TimeSeriesDataFrame,
380
392
  num_val_windows: int,
381
393
  val_step_size: int,
382
- ) -> Tuple[TimeSeriesDataFrame, Optional[TimeSeriesDataFrame]]:
383
- """Remove time series from train_data that are too short for chosen prediction_length and validation settings.
394
+ ) -> TimeSeriesDataFrame:
395
+ """Remove time series from train_data that either contain all NaNs or are too short for chosen settings.
384
396
 
385
- This method ensures that for each validation fold, all train series have length >= max(prediction_length + 1, 5).
397
+ This method ensures that 1) no time series consist of all NaN values and 2) for each validation fold, all train
398
+ series have length >= max(prediction_length + 1, 5).
386
399
 
387
- In other words, this method removes from train_data all time series with length less than
400
+ In other words, this method removes from train_data all time series with only NaN values or length less than
388
401
  min_train_length + prediction_length + (num_val_windows - 1) * val_step_size
389
402
  """
390
403
  min_length = self._min_train_length + self.prediction_length + (num_val_windows - 1) * val_step_size
391
-
392
404
  train_lengths = train_data.num_timesteps_per_item()
393
- train_items_to_drop = train_lengths.index[train_lengths < min_length]
394
- if len(train_items_to_drop) > 0:
405
+ too_short_items = train_lengths.index[train_lengths < min_length]
406
+
407
+ if len(too_short_items) > 0:
395
408
  logger.info(
396
- f"\tRemoving {len(train_items_to_drop)} short time series from train_data. Only series with length "
409
+ f"\tRemoving {len(too_short_items)} short time series from train_data. Only series with length "
397
410
  f">= {min_length} will be used for training."
398
411
  )
399
- filtered_train_data = train_data.query("item_id not in @train_items_to_drop")
400
- if len(filtered_train_data) == 0:
401
- raise ValueError(
402
- f"At least some time series in train_data must have length >= {min_length}. Please provide longer "
403
- f"time series as train_data or reduce prediction_length, num_val_windows, or val_step_size."
404
- )
405
- logger.info(
406
- f"\tAfter removing short series, train_data has {self._get_dataset_stats(filtered_train_data)}"
407
- )
408
- else:
409
- filtered_train_data = train_data
412
+ train_data = train_data.query("item_id not in @too_short_items")
410
413
 
411
- return filtered_train_data
414
+ all_nan_items = train_data.item_ids[
415
+ train_data[self.target].isna().groupby(TimeSeriesDataFrame.ITEMID, sort=False).all()
416
+ ]
417
+ if len(all_nan_items) > 0:
418
+ logger.info(f"\tRemoving {len(all_nan_items)} time series consisting of only NaN values from train_data.")
419
+ train_data = train_data.query("item_id not in @all_nan_items")
420
+
421
+ if len(too_short_items) or len(all_nan_items):
422
+ logger.info(f"\tAfter filtering, train_data has {self._get_dataset_stats(train_data)}")
412
423
 
413
- @apply_presets(TIMESERIES_PRESETS_CONFIGS)
424
+ if len(train_data) == 0:
425
+ raise ValueError(
426
+ f"At least some time series in train_data must have >= {min_length} observations. Please provide "
427
+ f"longer time series as train_data or reduce prediction_length, num_val_windows, or val_step_size."
428
+ )
429
+ return train_data
430
+
431
+ @apply_presets(get_predictor_presets())
414
432
  def fit(
415
433
  self,
416
- train_data: Union[TimeSeriesDataFrame, pd.DataFrame, str],
417
- tuning_data: Optional[Union[TimeSeriesDataFrame, pd.DataFrame, str]] = None,
418
- time_limit: Optional[int] = None,
419
- presets: Optional[str] = None,
420
- hyperparameters: Dict[Union[str, Type], Any] = None,
421
- hyperparameter_tune_kwargs: Optional[Union[str, Dict]] = None,
422
- excluded_model_types: Optional[List[str]] = None,
434
+ train_data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
435
+ tuning_data: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
436
+ time_limit: int | None = None,
437
+ presets: str | None = None,
438
+ hyperparameters: str | dict[str | Type, Any] | None = None,
439
+ hyperparameter_tune_kwargs: str | dict | None = None,
440
+ excluded_model_types: list[str] | None = None,
423
441
  num_val_windows: int = 1,
424
- val_step_size: Optional[int] = None,
425
- refit_every_n_windows: int = 1,
442
+ val_step_size: int | None = None,
443
+ refit_every_n_windows: int | None = 1,
426
444
  refit_full: bool = False,
427
445
  enable_ensemble: bool = True,
428
- random_seed: Optional[int] = 123,
429
- verbosity: Optional[int] = None,
446
+ skip_model_selection: bool = False,
447
+ random_seed: int | None = 123,
448
+ verbosity: int | None = None,
430
449
  ) -> "TimeSeriesPredictor":
431
450
  """Fit probabilistic forecasting models to the given time series dataset.
432
451
 
433
452
  Parameters
434
453
  ----------
435
- train_data : Union[TimeSeriesDataFrame, pd.DataFrame, str]
454
+ train_data : TimeSeriesDataFrame | pd.DataFrame | Path | str
436
455
  Training data in the :class:`~autogluon.timeseries.TimeSeriesDataFrame` format.
437
456
 
438
457
  Time series with length ``<= (num_val_windows + 1) * prediction_length`` will be ignored during training.
@@ -440,44 +459,39 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
440
459
 
441
460
  If ``known_covariates_names`` were specified when creating the predictor, ``train_data`` must include the
442
461
  columns listed in ``known_covariates_names`` with the covariates values aligned with the target time series.
443
- The known covariates must have a numeric (float or integer) dtype.
444
462
 
445
463
  Columns of ``train_data`` except ``target`` and those listed in ``known_covariates_names`` will be
446
464
  interpreted as ``past_covariates`` - covariates that are known only in the past.
447
465
 
448
- If ``train_data`` has static features (i.e., ``train_data.static_features`` is a pandas DataFrame), the
449
- predictor will interpret columns with ``int`` and ``float`` dtypes as continuous (real-valued) features,
450
- columns with ``object`` and ``str`` dtypes as categorical features, and will ignore the rest of columns.
466
+ If ``train_data`` contains covariates or static features, they will be interpreted as follows:
451
467
 
452
- For example, to ensure that column "store_id" with dtype ``int`` is interpreted as a category,
453
- we need to change its type to ``category``::
468
+ * columns with ``int``, ``bool`` and ``float`` dtypes are interpreted as continuous (real-valued) features
469
+ * columns with ``object``, ``str`` and ``category`` dtypes are as interpreted as categorical features
470
+ * columns with other dtypes are ignored
454
471
 
455
- data.static_features["store_id"] = data.static_features["store_id"].astype("category")
472
+ To ensure that the column type is interpreted correctly, please convert it to one of the above dtypes.
473
+ For example, to ensure that column "store_id" with dtype ``int`` is interpreted as a category, change
474
+ its dtype to ``category``::
456
475
 
457
- If provided data is an instance of pandas DataFrame, AutoGluon will attempt to automatically convert it
458
- to a ``TimeSeriesDataFrame``.
476
+ data.static_features["store_id"] = data.static_features["store_id"].astype("category")
459
477
 
460
- tuning_data : Union[TimeSeriesDataFrame, pd.DataFrame, str], optional
478
+ If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
479
+ If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
480
+ tuning_data : TimeSeriesDataFrame | pd.DataFrame | Path | str, optional
461
481
  Data reserved for model selection and hyperparameter tuning, rather than training individual models. Also
462
482
  used to compute the validation scores. Note that only the last ``prediction_length`` time steps of each
463
483
  time series are used for computing the validation score.
464
484
 
465
485
  If ``tuning_data`` is provided, multi-window backtesting on training data will be disabled, the
466
- :attr:`num_val_windows` will be set to ``0``, and :attr:`refit_full` will be set to ``False``.
486
+ ``num_val_windows`` will be set to ``0``, and ``refit_full`` will be set to ``False``.
467
487
 
468
488
  Leaving this argument empty and letting AutoGluon automatically generate the validation set from
469
489
  ``train_data`` is a good default.
470
490
 
471
- If ``known_covariates_names`` were specified when creating the predictor, ``tuning_data`` must also include
472
- the columns listed in ``known_covariates_names`` with the covariates values aligned with the target time
473
- series.
474
-
475
- If ``train_data`` has past covariates or static features, ``tuning_data`` must have also include them (with
476
- same columns names and dtypes).
477
-
478
- If provided data is an instance of pandas DataFrame, AutoGluon will attempt to automatically convert it
479
- to a ``TimeSeriesDataFrame``.
491
+ The names and dtypes of columns and static features in ``tuning_data`` must match the ``train_data``.
480
492
 
493
+ If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
494
+ If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
481
495
  time_limit : int, optional
482
496
  Approximately how long :meth:`~autogluon.timeseries.TimeSeriesPredictor.fit` will run (wall-clock time in
483
497
  seconds). If not specified, :meth:`~autogluon.timeseries.TimeSeriesPredictor.fit` will run until all models
@@ -496,14 +510,22 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
496
510
 
497
511
  Available presets:
498
512
 
499
- - ``"fast_training"``: fit simple statistical models (``ETS``, ``Theta``, ``Naive``, ``SeasonalNaive``) + fast tree-based models ``RecursiveTabular`` and ``DirectTabular``. These models are fast to train but may not be very accurate.
500
- - ``"medium_quality"``: all models mentioned above + deep learning model ``TemporalFusionTransformer``. Default setting that produces good forecasts with reasonable training time.
501
- - ``"high_quality"``: All ML models available in AutoGluon + additional statistical models (``NPTS``, ``AutoETS``, ``AutoARIMA``, ``CrostonSBA``, ``DynamicOptimizedTheta``). Much more accurate than ``medium_quality``, but takes longer to train.
502
- - ``"best_quality"``: Same models as in ``"high_quality"`, but performs validation with multiple backtests. Usually better than ``high_quality``, but takes even longer to train.
513
+ - ``"fast_training"``: Simple statistical and tree-based ML models. These models are fast to train but may not be very accurate.
514
+ - ``"medium_quality"``: Same models as above, plus deep learning models ``TemporalFusionTransformer`` and Chronos-Bolt (small). Produces good forecasts with reasonable training time.
515
+ - ``"high_quality"``: A mix of multiple DL, ML and statistical forecasting models available in AutoGluon that offers the best forecast accuracy. Much more accurate than ``medium_quality``, but takes longer to train.
516
+ - ``"best_quality"``: Same models as in ``"high_quality"``, but performs validation with multiple backtests. Usually better than ``high_quality``, but takes even longer to train.
517
+
518
+ Available presets with the `Chronos-Bolt <https://github.com/amazon-science/chronos-forecasting>`_ model:
519
+
520
+ - ``"bolt_{model_size}"``: where model size is one of ``tiny,mini,small,base``. Uses the Chronos-Bolt pretrained model for zero-shot forecasting.
521
+ See the documentation for ``ChronosModel`` or see `Hugging Face <https://huggingface.co/collections/amazon/chronos-models-65f1791d630a8d57cb718444>`_ for more information.
503
522
 
504
- Details for these presets can be found in ``autogluon/timeseries/configs/presets_configs.py``. If not
505
- provided, user-provided values for ``hyperparameters`` and ``hyperparameter_tune_kwargs`` will be used
506
- (defaulting to their default values specified below).
523
+ Exact definitions of these presets can be found in the source code
524
+ [`1 <https://github.com/autogluon/autogluon/blob/stable/timeseries/src/autogluon/timeseries/configs/presets_configs.py>`_,
525
+ `2 <https://github.com/autogluon/autogluon/blob/stable/timeseries/src/autogluon/timeseries/models/presets.py>`_].
526
+
527
+ If no ``presets`` are selected, user-provided values for ``hyperparameters`` will be used (defaulting to their
528
+ default values specified below).
507
529
  hyperparameters : str or dict, optional
508
530
  Determines what models are trained and what hyperparameters are used by each model.
509
531
 
@@ -561,7 +583,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
561
583
  Valid preset values:
562
584
 
563
585
  * "auto": Performs HPO via bayesian optimization search on GluonTS-backed neural forecasting models and
564
- random search on other models using local scheduler.
586
+ random search on other models using local scheduler.
565
587
  * "random": Performs HPO via random search.
566
588
 
567
589
  You can also provide a dict to specify searchers and schedulers
@@ -569,7 +591,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
569
591
 
570
592
  * "num_trials": How many HPO trials to run
571
593
  * "scheduler": Which scheduler to use. Valid values:
572
- * "local": Local shceduler that schedules trials FIFO
594
+ * "local": Local scheduler that schedules trials FIFO
573
595
  * "searcher": Which searching algorithm to use. Valid values:
574
596
  * "local_random": Uses the "random" searcher
575
597
  * "random": Perform random search
@@ -588,7 +610,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
588
610
  "scheduler": "local",
589
611
  },
590
612
  )
591
- excluded_model_types: List[str], optional
613
+ excluded_model_types: list[str], optional
592
614
  Banned subset of model types to avoid training during ``fit()``, even if present in ``hyperparameters``.
593
615
  For example, the following code will train all models included in the ``high_quality`` presets except ``DeepAR``::
594
616
 
@@ -603,7 +625,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
603
625
  of time series in ``train_data`` are long enough for the chosen number of backtests.
604
626
 
605
627
  Increasing this parameter increases the training time roughly by a factor of ``num_val_windows // refit_every_n_windows``.
606
- See :attr:`refit_every_n_windows` and :attr:`val_step_size`: for details.
628
+ See ``refit_every_n_windows`` and ``val_step_size`` for details.
607
629
 
608
630
  For example, for ``prediction_length=2``, ``num_val_windows=3`` and ``val_step_size=1`` the folds are::
609
631
 
@@ -622,7 +644,11 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
622
644
  This argument has no effect if ``tuning_data`` is provided.
623
645
  refit_every_n_windows: int or None, default = 1
624
646
  When performing cross validation, each model will be retrained every ``refit_every_n_windows`` validation
625
- windows. If set to ``None``, model will only be fit once for the first validation window.
647
+ windows, where the number of validation windows is specified by ``num_val_windows``. Note that in the
648
+ default setting where ``num_val_windows=1``, this argument has no effect.
649
+
650
+ If set to ``None``, models will only be fit once for the first (oldest) validation window. By default,
651
+ ``refit_every_n_windows=1``, i.e., all models will be refit for each validation window.
626
652
  refit_full : bool, default = False
627
653
  If True, after training is complete, AutoGluon will attempt to re-train all models using all of training
628
654
  data (including the data initially reserved for validation). This argument has no effect if ``tuning_data``
@@ -630,6 +656,10 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
630
656
  enable_ensemble : bool, default = True
631
657
  If True, the ``TimeSeriesPredictor`` will fit a simple weighted ensemble on top of the models specified via
632
658
  ``hyperparameters``.
659
+ skip_model_selection : bool, default = False
660
+ If True, predictor will not compute the validation score. For example, this argument is useful if we want
661
+ to use the predictor as a wrapper for a single pre-trained model. If set to True, then the ``hyperparameters``
662
+ dict must contain exactly one model without hyperparameter search spaces or an exception will be raised.
633
663
  random_seed : int or None, default = 123
634
664
  If provided, fixes the seed of the random number generator for all models. This guarantees reproducible
635
665
  results for most models (except those trained on GPU because of the non-determinism of GPU operations).
@@ -639,12 +669,15 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
639
669
 
640
670
  """
641
671
  time_start = time.time()
642
- if self._learner.is_fit:
643
- raise AssertionError("Predictor is already fit! To fit additional models create a new `Predictor`.")
672
+ if self.is_fit:
673
+ raise AssertionError(
674
+ "Predictor is already fit! To fit additional models create a new `TimeSeriesPredictor`."
675
+ )
644
676
 
645
677
  if verbosity is None:
646
678
  verbosity = self.verbosity
647
679
  set_logger_verbosity(verbosity, logger=logger)
680
+ warn_if_mlflow_autologging_is_enabled(logger=logger)
648
681
 
649
682
  logger.info("Beginning AutoGluon training..." + (f" Time limit = {time_limit}s" if time_limit else ""))
650
683
  logger.info(f"AutoGluon will save models to '{self.path}'")
@@ -658,7 +691,8 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
658
691
  target=self.target,
659
692
  known_covariates_names=self.known_covariates_names,
660
693
  eval_metric=self.eval_metric,
661
- eval_metric_seasonal_period=self.eval_metric_seasonal_period,
694
+ eval_metric_seasonal_period=self.eval_metric.seasonal_period,
695
+ horizon_weight=self.eval_metric.horizon_weight,
662
696
  quantile_levels=self.quantile_levels,
663
697
  freq=self.freq,
664
698
  time_limit=time_limit,
@@ -669,6 +703,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
669
703
  val_step_size=val_step_size,
670
704
  refit_every_n_windows=refit_every_n_windows,
671
705
  refit_full=refit_full,
706
+ skip_model_selection=skip_model_selection,
672
707
  enable_ensemble=enable_ensemble,
673
708
  random_seed=random_seed,
674
709
  verbosity=verbosity,
@@ -691,37 +726,44 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
691
726
 
692
727
  if tuning_data is not None:
693
728
  tuning_data = self._check_and_prepare_data_frame(tuning_data, name="tuning_data")
694
- self._check_data_for_evaluation(tuning_data, name="tuning_data")
695
- logger.info(f"Provided tuning_data has {self._get_dataset_stats(train_data)}")
729
+ tuning_data = self._check_and_prepare_data_frame_for_evaluation(tuning_data, name="tuning_data")
730
+ logger.info(f"Provided tuning_data has {self._get_dataset_stats(tuning_data)}")
696
731
  # TODO: Use num_val_windows to perform multi-window backtests on tuning_data
697
- if num_val_windows > 0:
732
+ if num_val_windows > 1:
698
733
  logger.warning(
699
734
  "\tSetting num_val_windows = 0 (disabling backtesting on train_data) because tuning_data is provided."
700
735
  )
701
- num_val_windows = 0
736
+ num_val_windows = 1
702
737
 
703
738
  if num_val_windows == 0 and tuning_data is None:
704
739
  raise ValueError("Please set num_val_windows >= 1 or provide custom tuning_data")
705
740
 
706
- train_data = self._filter_short_series(
707
- train_data, num_val_windows=num_val_windows, val_step_size=val_step_size
708
- )
741
+ if num_val_windows <= 1 and refit_every_n_windows is not None and refit_every_n_windows > 1:
742
+ logger.warning(
743
+ f"\trefit_every_n_windows provided as {refit_every_n_windows} but num_val_windows is set to {num_val_windows}."
744
+ " Refit_every_n_windows will have no effect."
745
+ )
709
746
 
710
- val_splitter = ExpandingWindowSplitter(
711
- prediction_length=self.prediction_length, num_val_windows=num_val_windows, val_step_size=val_step_size
712
- )
747
+ if not skip_model_selection:
748
+ train_data = self._filter_useless_train_data(
749
+ train_data,
750
+ num_val_windows=0 if tuning_data is not None else num_val_windows,
751
+ val_step_size=val_step_size,
752
+ )
713
753
 
714
754
  time_left = None if time_limit is None else time_limit - (time.time() - time_start)
715
755
  self._learner.fit(
716
756
  train_data=train_data,
717
- val_data=tuning_data,
718
757
  hyperparameters=hyperparameters,
758
+ val_data=tuning_data,
719
759
  hyperparameter_tune_kwargs=hyperparameter_tune_kwargs,
720
760
  excluded_model_types=excluded_model_types,
721
761
  time_limit=time_left,
722
762
  verbosity=verbosity,
723
- val_splitter=val_splitter,
763
+ num_val_windows=(num_val_windows,) if isinstance(num_val_windows, int) else num_val_windows,
764
+ val_step_size=val_step_size,
724
765
  refit_every_n_windows=refit_every_n_windows,
766
+ skip_model_selection=skip_model_selection,
725
767
  enable_ensemble=enable_ensemble,
726
768
  random_seed=random_seed,
727
769
  )
@@ -734,40 +776,41 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
734
776
  self.save()
735
777
  return self
736
778
 
737
- def model_names(self) -> List[str]:
779
+ def model_names(self) -> list[str]:
738
780
  """Returns the list of model names trained by this predictor object."""
781
+ self._assert_is_fit("model_names")
739
782
  return self._trainer.get_model_names()
740
783
 
741
784
  def predict(
742
785
  self,
743
- data: Union[TimeSeriesDataFrame, pd.DataFrame, str],
744
- known_covariates: Optional[Union[TimeSeriesDataFrame, pd.DataFrame, str]] = None,
745
- model: Optional[str] = None,
786
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
787
+ known_covariates: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
788
+ model: str | None = None,
746
789
  use_cache: bool = True,
747
- random_seed: Optional[int] = 123,
790
+ random_seed: int | None = 123,
748
791
  ) -> TimeSeriesDataFrame:
749
792
  """Return quantile and mean forecasts for the given dataset, starting from the end of each time series.
750
793
 
751
794
  Parameters
752
795
  ----------
753
- data : Union[TimeSeriesDataFrame, pd.DataFrame, str]
754
- Time series data to forecast with.
796
+ data : TimeSeriesDataFrame | pd.DataFrame | Path | str
797
+ Historical time series data for which the forecast needs to be made.
755
798
 
756
- If ``known_covariates_names`` were specified when creating the predictor, ``data`` must include the columns
757
- listed in ``known_covariates_names`` with the covariates values aligned with the target time series.
799
+ The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
800
+ the predictor.
758
801
 
759
- If ``train_data`` used to train the predictor contained past covariates or static features, then ``data``
760
- must also include them (with same column names and dtypes).
761
-
762
- If provided data is an instance of pandas DataFrame, AutoGluon will attempt to automatically convert it
763
- to a ``TimeSeriesDataFrame``.
764
- known_covariates : Union[TimeSeriesDataFrame, pd.DataFrame, str], optional
802
+ If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
803
+ If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
804
+ known_covariates : TimeSeriesDataFrame | pd.DataFrame | Path | str, optional
765
805
  If ``known_covariates_names`` were specified when creating the predictor, it is necessary to provide the
766
- values of the known covariates for each time series during the forecast horizon. That is:
806
+ values of the known covariates for each time series during the forecast horizon. Specifically:
807
+
808
+ - Must contain all columns listed in ``known_covariates_names``.
809
+ - Must include all ``item_id`` values present in the input ``data``.
810
+ - Must include ``timestamp`` values for the full forecast horizon (i.e., ``prediction_length`` time steps) following the end of each series in the input ``data``.
767
811
 
768
- - The columns must include all columns listed in ``known_covariates_names``
769
- - The ``item_id`` index must include all item ids present in ``data``
770
- - The ``timestamp`` index must include the values for ``prediction_length`` many time steps into the future from the end of each time series in ``data``
812
+ You can use :meth:`autogluon.timeseries.TimeSeriesPredictor.make_future_data_frame` to generate a template
813
+ containing the required ``item_id`` and ``timestamp`` combinations for the ``known_covariates`` dataframe.
771
814
 
772
815
  See example below.
773
816
  model : str, optional
@@ -808,8 +851,10 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
808
851
  B 2020-03-04 17.1
809
852
  2020-03-05 8.3
810
853
  """
811
- # Don't use data.item_ids in case data is not a TimeSeriesDataFrame
812
- original_item_id_order = data.reset_index()[ITEMID].unique()
854
+ self._assert_is_fit("predict")
855
+ # Save original item_id order to return predictions in the same order as input data
856
+ data = self._to_data_frame(data)
857
+ original_item_id_order = data.item_ids
813
858
  data = self._check_and_prepare_data_frame(data)
814
859
  if known_covariates is not None:
815
860
  known_covariates = self._to_data_frame(known_covariates)
@@ -820,41 +865,250 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
820
865
  use_cache=use_cache,
821
866
  random_seed=random_seed,
822
867
  )
823
- return predictions.reindex(original_item_id_order, level=ITEMID)
868
+ return cast(TimeSeriesDataFrame, predictions.reindex(original_item_id_order, level=TimeSeriesDataFrame.ITEMID))
869
+
870
+ @overload
871
+ def backtest_predictions(
872
+ self,
873
+ data: TimeSeriesDataFrame | None = None,
874
+ *,
875
+ model: str | None = None,
876
+ num_val_windows: int | None = None,
877
+ val_step_size: int | None = None,
878
+ use_cache: bool = True,
879
+ ) -> list[TimeSeriesDataFrame]: ...
880
+
881
+ @overload
882
+ def backtest_predictions(
883
+ self,
884
+ data: TimeSeriesDataFrame | None = None,
885
+ *,
886
+ model: list[str],
887
+ num_val_windows: int | None = None,
888
+ val_step_size: int | None = None,
889
+ use_cache: bool = True,
890
+ ) -> dict[str, list[TimeSeriesDataFrame]]: ...
891
+
892
+ def backtest_predictions(
893
+ self,
894
+ data: TimeSeriesDataFrame | None = None,
895
+ *,
896
+ model: str | list[str] | None = None,
897
+ num_val_windows: int | None = None,
898
+ val_step_size: int | None = None,
899
+ use_cache: bool = True,
900
+ ) -> list[TimeSeriesDataFrame] | dict[str, list[TimeSeriesDataFrame]]:
901
+ """Return predictions for multiple validation windows.
902
+
903
+ When ``data=None``, returns the predictions that were saved during training. Otherwise, generates new
904
+ predictions by splitting ``data`` into multiple windows using an expanding window strategy.
905
+
906
+ The corresponding target values for each window can be obtained using
907
+ :meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_targets`.
908
+
909
+ Parameters
910
+ ----------
911
+ data : TimeSeriesDataFrame, optional
912
+ Time series data to generate predictions for. If ``None``, returns the predictions that were saved
913
+ during training on ``train_data``.
914
+
915
+ If provided, all time series in ``data`` must have length at least
916
+ ``prediction_length + (num_val_windows - 1) * val_step_size + 1``.
917
+
918
+ The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
919
+ the predictor.
920
+ model : str, list[str], or None, default = None
921
+ Name of the model(s) to generate predictions with. By default, the best model during training
922
+ (with highest validation score) will be used.
923
+
924
+ - If ``str``: Returns predictions for a single model as a list.
925
+ - If ``list[str]``: Returns predictions for multiple models as a dict mapping model names to lists.
926
+ - If ``None``: Uses the best model.
927
+ num_val_windows : int, optional
928
+ Number of validation windows to generate. If ``None``, uses the ``num_val_windows`` value from training
929
+ configuration when ``data=None``, otherwise defaults to 1.
930
+
931
+ For example, with ``prediction_length=2``, ``num_val_windows=3``, and ``val_step_size=1``, the validation
932
+ windows are::
933
+
934
+ |-------------------|
935
+ | x x x x x y y - - |
936
+ | x x x x x x y y - |
937
+ | x x x x x x x y y |
938
+
939
+ where ``x`` denotes training time steps and ``y`` denotes validation time steps for each window.
940
+ val_step_size : int, optional
941
+ Number of time steps between the start of consecutive validation windows. If ``None``, defaults to
942
+ ``prediction_length``.
943
+ use_cache : bool, default = True
944
+ If True, will attempt to use cached predictions. If False, cached predictions will be ignored.
945
+ This argument is ignored if ``cache_predictions`` was set to False when creating the ``TimeSeriesPredictor``.
946
+
947
+ Returns
948
+ -------
949
+ list[TimeSeriesDataFrame] or dict[str, list[TimeSeriesDataFrame]]
950
+ Predictions for each validation window.
951
+
952
+ - If ``model`` is a ``str`` or ``None``: Returns a list of length ``num_val_windows``, where each element
953
+ contains the predictions for one validation window.
954
+ - If ``model`` is a ``list[str]``: Returns a dict mapping each model name to a list of predictions for
955
+ each validation window.
956
+
957
+ Examples
958
+ --------
959
+ Make predictions on new data with the best model
960
+
961
+ >>> predictor.backtest_predictions(test_data, num_val_windows=2)
962
+
963
+ Load validation predictions for all models that were saved during training
964
+
965
+ >>> predictor.backtest_predictions(model=predictor.model_names())
966
+
967
+ See Also
968
+ --------
969
+ backtest_targets
970
+ Return target values aligned with predictions.
971
+ evaluate
972
+ Evaluate forecast accuracy on a hold-out set.
973
+ predict
974
+ Generate forecasts for future time steps.
975
+ """
976
+ self._assert_is_fit("backtest_predictions")
977
+ if data is not None:
978
+ data = self._check_and_prepare_data_frame(data)
979
+
980
+ if model is None:
981
+ model_names = [self.model_best]
982
+ elif isinstance(model, str):
983
+ model_names = [model]
984
+ else:
985
+ model_names = model
986
+
987
+ result = self._learner.backtest_predictions(
988
+ data=data,
989
+ model_names=model_names,
990
+ num_val_windows=num_val_windows,
991
+ val_step_size=val_step_size,
992
+ use_cache=use_cache,
993
+ )
994
+
995
+ if isinstance(model, list):
996
+ return result
997
+ else:
998
+ return result[model_names[0]]
999
+
1000
+ def backtest_targets(
1001
+ self,
1002
+ data: TimeSeriesDataFrame | None = None,
1003
+ *,
1004
+ num_val_windows: int | None = None,
1005
+ val_step_size: int | None = None,
1006
+ ) -> list[TimeSeriesDataFrame]:
1007
+ """Return target values for each validation window.
1008
+
1009
+ Returns the actual target values corresponding to each validation window used in
1010
+ :meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`. The returned targets are aligned
1011
+ with the predictions, making it easy to compute custom evaluation metrics or analyze forecast errors.
1012
+
1013
+ Parameters
1014
+ ----------
1015
+ data : TimeSeriesDataFrame, optional
1016
+ Time series data to extract targets from. If ``None``, returns the targets from the validation windows
1017
+ used during training.
1018
+
1019
+ If provided, all time series in ``data`` must have length at least
1020
+ ``prediction_length + (num_val_windows - 1) * val_step_size + 1``.
1021
+
1022
+ The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
1023
+ the predictor.
1024
+ num_val_windows : int, optional
1025
+ Number of validation windows to extract targets for. If ``None``, uses the ``num_val_windows`` value from
1026
+ training configuration when ``data=None``, otherwise defaults to 1.
1027
+
1028
+ This should match the ``num_val_windows`` argument passed to
1029
+ :meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`.
1030
+ val_step_size : int, optional
1031
+ Number of time steps between the start of consecutive validation windows. If ``None``, defaults to
1032
+ ``prediction_length``.
1033
+
1034
+ This should match the ``val_step_size`` argument passed to
1035
+ :meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`.
1036
+
1037
+ Returns
1038
+ -------
1039
+ list[TimeSeriesDataFrame]
1040
+ Target values for each validation window. Returns a list of length ``num_val_windows``,
1041
+ where each element contains the full time series data for one validation window.
1042
+ Each dataframe includes both historical context and the last ``prediction_length`` time steps
1043
+ that represent the target values to compare against predictions.
1044
+
1045
+ The returned targets are aligned with the output of
1046
+ :meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`, so ``targets[i]`` corresponds
1047
+ to ``predictions[i]`` for the i-th validation window.
1048
+
1049
+ See Also
1050
+ --------
1051
+ backtest_predictions
1052
+ Return predictions for multiple validation windows.
1053
+ evaluate
1054
+ Evaluate forecast accuracy on a hold-out set.
1055
+ """
1056
+ self._assert_is_fit("backtest_targets")
1057
+ if data is not None:
1058
+ data = self._check_and_prepare_data_frame(data)
1059
+ return self._learner.backtest_targets(
1060
+ data=data,
1061
+ num_val_windows=num_val_windows,
1062
+ val_step_size=val_step_size,
1063
+ )
824
1064
 
825
1065
  def evaluate(
826
1066
  self,
827
- data: Union[TimeSeriesDataFrame, pd.DataFrame, str],
828
- model: Optional[str] = None,
829
- metrics: Optional[Union[str, TimeSeriesScorer, List[Union[str, TimeSeriesScorer]]]] = None,
1067
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
1068
+ model: str | None = None,
1069
+ metrics: str | TimeSeriesScorer | list[str | TimeSeriesScorer] | None = None,
1070
+ cutoff: int | None = None,
830
1071
  display: bool = False,
831
1072
  use_cache: bool = True,
832
- ) -> Dict[str, float]:
1073
+ ) -> dict[str, float]:
833
1074
  """Evaluate the forecast accuracy for given dataset.
834
1075
 
835
1076
  This method measures the forecast accuracy using the last ``self.prediction_length`` time steps of each time
836
1077
  series in ``data`` as a hold-out set.
837
1078
 
1079
+ .. note::
1080
+ Metrics are always reported in 'higher is better' format.
1081
+ This means that metrics such as MASE or MAPE will be multiplied by -1, so their values will be negative.
1082
+ This is necessary to avoid the user needing to know the metric to understand if higher is better when
1083
+ looking at the evaluation results.
1084
+
838
1085
  Parameters
839
1086
  ----------
840
- data : Union[TimeSeriesDataFrame, pd.DataFrame, str]
841
- The data to evaluate the best model on. The last ``prediction_length`` time steps of the data set, for each
842
- item, will be held out for prediction and forecast accuracy will be calculated on these time steps.
1087
+ data : TimeSeriesDataFrame | pd.DataFrame | Path | str
1088
+ The data to evaluate the best model on. If a ``cutoff`` is not provided, the last ``prediction_length``
1089
+ time steps of each time series in ``data`` will be held out for prediction and forecast accuracy will
1090
+ be calculated on these time steps. When a ``cutoff`` is provided, the ``-cutoff``-th to the
1091
+ ``-cutoff + prediction_length``-th time steps of each time series are used for evaluation.
843
1092
 
844
- If ``known_covariates_names`` were specified when creating the predictor, ``data`` must include the columns
845
- listed in ``known_covariates_names`` with the covariates values aligned with the target time series.
1093
+ Must include both historical and future data (i.e., length of all time series in ``data`` must be at least
1094
+ ``prediction_length + 1``, if ``cutoff`` is not provided, ``-cutoff + 1`` otherwise).
846
1095
 
847
- If ``train_data`` used to train the predictor contained past covariates or static features, then ``data``
848
- must also include them (with same column names and dtypes).
1096
+ The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
1097
+ the predictor.
849
1098
 
850
- If provided data is an instance of pandas DataFrame, AutoGluon will attempt to automatically convert it
851
- to a ``TimeSeriesDataFrame``.
1099
+ If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
1100
+ If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
852
1101
  model : str, optional
853
1102
  Name of the model that you would like to evaluate. By default, the best model during training
854
1103
  (with highest validation score) will be used.
855
- metrics : str, TimeSeriesScorer or List[Union[str, TimeSeriesScorer]], optional
1104
+ metrics : str, TimeSeriesScorer or list[str | TimeSeriesScorer], optional
856
1105
  Metric or a list of metrics to compute scores with. Defaults to ``self.eval_metric``. Supports both
857
1106
  metric names as strings and custom metrics based on TimeSeriesScorer.
1107
+ cutoff : int, optional
1108
+ A *negative* integer less than or equal to ``-1 * prediction_length`` denoting the time step in ``data``
1109
+ where the forecast evaluation starts, i.e., time series are evaluated from the ``-cutoff``-th to the
1110
+ ``-cutoff + prediction_length``-th time step. Defaults to ``-1 * prediction_length``, using the last
1111
+ ``prediction_length`` time steps of each time series for evaluation.
858
1112
  display : bool, default = False
859
1113
  If True, the scores will be printed.
860
1114
  use_cache : bool, default = True
@@ -863,29 +1117,185 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
863
1117
 
864
1118
  Returns
865
1119
  -------
866
- scores_dict : Dict[str, float]
1120
+ scores_dict : dict[str, float]
867
1121
  Dictionary where keys = metrics, values = performance along each metric. For consistency, error metrics
868
1122
  will have their signs flipped to obey this convention. For example, negative MAPE values will be reported.
869
1123
  To get the ``eval_metric`` score, do ``output[predictor.eval_metric.name]``.
870
1124
  """
1125
+ self._assert_is_fit("evaluate")
871
1126
  data = self._check_and_prepare_data_frame(data)
872
- self._check_data_for_evaluation(data)
1127
+ data = self._check_and_prepare_data_frame_for_evaluation(data, cutoff=cutoff)
1128
+
873
1129
  scores_dict = self._learner.evaluate(data, model=model, metrics=metrics, use_cache=use_cache)
874
1130
  if display:
875
1131
  logger.info("Evaluations on test data:")
876
1132
  logger.info(json.dumps(scores_dict, indent=4))
877
1133
  return scores_dict
878
1134
 
1135
+ def feature_importance(
1136
+ self,
1137
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
1138
+ model: str | None = None,
1139
+ metric: str | TimeSeriesScorer | None = None,
1140
+ features: list[str] | None = None,
1141
+ time_limit: float | None = None,
1142
+ method: Literal["naive", "permutation"] = "permutation",
1143
+ subsample_size: int = 50,
1144
+ num_iterations: int | None = None,
1145
+ random_seed: int | None = 123,
1146
+ relative_scores: bool = False,
1147
+ include_confidence_band: bool = True,
1148
+ confidence_level: float = 0.99,
1149
+ ) -> pd.DataFrame:
1150
+ """
1151
+ Calculates feature importance scores for the given model via replacing each feature by a shuffled version of the same feature
1152
+ (also known as permutation feature importance) or by assigning a constant value representing the median or mode of the feature,
1153
+ and computing the relative decrease in the model's predictive performance.
1154
+
1155
+ A feature's importance score represents the performance drop that results when the model makes predictions on a perturbed copy
1156
+ of the data where this feature's values have been randomly shuffled across rows. A feature score of 0.01 would indicate that the
1157
+ predictive performance dropped by 0.01 when the feature was randomly shuffled or replaced. The higher the score a feature has,
1158
+ the more important it is to the model's performance.
1159
+
1160
+ If a feature has a negative score, this means that the feature is likely harmful to the final model, and a model trained with
1161
+ the feature removed would be expected to achieve a better predictive performance. Note that calculating feature importance can
1162
+ be a computationally expensive process, particularly if the model uses many features. In many cases, this can take longer than
1163
+ the original model training. Roughly, this will equal to the number of features in the data multiplied by ``num_iterations``
1164
+ (or, 1 when ``method="naive"``) and time taken when ``evaluate()`` is called on a dataset with ``subsample_size``.
1165
+
1166
+ Parameters
1167
+ ----------
1168
+ data : TimeSeriesDataFrame, pd.DataFrame, Path or str, optional
1169
+ The data to evaluate feature importances on. The last ``prediction_length`` time steps of the data set, for each
1170
+ item, will be held out for prediction and forecast accuracy will be calculated on these time steps.
1171
+ More accurate feature importances will be obtained from new data that was held-out during ``fit()``.
1172
+
1173
+ The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
1174
+ the predictor.
1175
+
1176
+ If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
1177
+ If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
1178
+
1179
+ If ``data`` is not provided, then validation (tuning) data provided during training (or the held out data used for
1180
+ validation if ``tuning_data`` was not explicitly provided ``fit()``) will be used.
1181
+ model : str, optional
1182
+ Name of the model that you would like to evaluate. By default, the best model during training
1183
+ (with highest validation score) will be used.
1184
+ metric : str or TimeSeriesScorer, optional
1185
+ Metric to be used for computing feature importance. If None, the ``eval_metric`` specified during initialization of
1186
+ the ``TimeSeriesPredictor`` will be used.
1187
+ features : list[str], optional
1188
+ List of feature names that feature importances are calculated for and returned. By default, all feature importances
1189
+ will be returned.
1190
+ method : {"permutation", "naive"}, default = "permutation"
1191
+ Method to be used for computing feature importance.
1192
+
1193
+ * ``naive``: computes feature importance by replacing the values of each feature by a constant value and computing
1194
+ feature importances as the relative improvement in the evaluation metric. The constant value is the median for
1195
+ real-valued features and the mode for categorical features, for both covariates and static features, obtained from the
1196
+ feature values in ``data`` provided.
1197
+ * ``permutation``: computes feature importance by naively shuffling the values of the feature across different items
1198
+ and time steps. Each feature is shuffled for ``num_iterations`` times and feature importances are computed as the
1199
+ relative improvement in the evaluation metric. Refer to https://explained.ai/rf-importance/ for an explanation of
1200
+ permutation importance.
1201
+
1202
+ subsample_size : int, default = 50
1203
+ The number of items to sample from ``data`` when computing feature importance. Larger values increase the accuracy of
1204
+ the feature importance scores. Runtime linearly scales with ``subsample_size``.
1205
+ time_limit : float, optional
1206
+ Time in seconds to limit the calculation of feature importance. If None, feature importance will calculate without early stopping.
1207
+ If ``method="permutation"``, a minimum of 1 full shuffle set will always be evaluated. If a shuffle set evaluation takes longer than
1208
+ ``time_limit``, the method will take the length of a shuffle set evaluation to return regardless of the ``time_limit``.
1209
+ num_iterations : int, optional
1210
+ The number of different iterations of the data that are evaluated. If ``method="permutation"``, this will be interpreted
1211
+ as the number of shuffle sets (equivalent to ``num_shuffle_sets`` in :meth:`TabularPredictor.feature_importance`). If ``method="naive"``, the
1212
+ constant replacement approach is repeated for ``num_iterations`` times, and a different subsample of data (of size ``subsample_size``) will
1213
+ be taken in each iteration.
1214
+ Default is 1 for ``method="naive"`` and 5 for ``method="permutation"``. The value will be ignored if ``method="naive"`` and the subsample
1215
+ size is greater than the number of items in ``data`` as additional iterations will be redundant.
1216
+ Larger values will increase the quality of the importance evaluation.
1217
+ It is generally recommended to increase ``subsample_size`` before increasing ``num_iterations``.
1218
+ Runtime scales linearly with ``num_iterations``.
1219
+ random_seed : int or None, default = 123
1220
+ If provided, fixes the seed of the random number generator for all models. This guarantees reproducible
1221
+ results for feature importance.
1222
+ relative_scores : bool, default = False
1223
+ By default, this method will return expected average *absolute* improvement in the eval metric due to the feature. If True, then
1224
+ the statistics will be computed over the *relative* (percentage) improvements.
1225
+ include_confidence_band: bool, default = True
1226
+ If True, returned DataFrame will include two additional columns specifying confidence interval for the true underlying importance value of
1227
+ each feature. Increasing ``subsample_size`` and ``num_iterations`` will tighten the confidence interval.
1228
+ confidence_level: float, default = 0.99
1229
+ This argument is only considered when ``include_confidence_band=True``, and can be used to specify the confidence level used
1230
+ for constructing confidence intervals. For example, if ``confidence_level`` is set to 0.99, then the returned DataFrame will include
1231
+ columns ``p99_high`` and ``p99_low`` which indicates that the true feature importance will be between ``p99_high`` and ``p99_low`` 99% of
1232
+ the time (99% confidence interval). More generally, if ``confidence_level`` = 0.XX, then the columns containing the XX% confidence interval
1233
+ will be named ``pXX_high`` and ``pXX_low``.
1234
+
1235
+ Returns
1236
+ -------
1237
+ :class:`pd.DataFrame` of feature importance scores with 2 columns:
1238
+ index: The feature name.
1239
+ 'importance': The estimated feature importance score.
1240
+ 'stddev': The standard deviation of the feature importance score. If NaN, then not enough ``num_iterations`` were used.
1241
+ """
1242
+ self._assert_is_fit("feature_importance")
1243
+ if data is not None:
1244
+ data = self._check_and_prepare_data_frame(data)
1245
+ data = self._check_and_prepare_data_frame_for_evaluation(data)
1246
+
1247
+ fi_df = self._learner.get_feature_importance(
1248
+ data=data,
1249
+ model=model,
1250
+ metric=metric,
1251
+ features=features,
1252
+ time_limit=time_limit,
1253
+ method=method,
1254
+ subsample_size=subsample_size,
1255
+ num_iterations=num_iterations,
1256
+ random_seed=random_seed,
1257
+ relative_scores=relative_scores,
1258
+ include_confidence_band=include_confidence_band,
1259
+ confidence_level=confidence_level,
1260
+ )
1261
+ return fi_df
1262
+
879
1263
  @classmethod
880
1264
  def _load_version_file(cls, path: str) -> str:
1265
+ """
1266
+ Loads the version file that is part of the saved predictor artifact.
1267
+
1268
+ Parameters
1269
+ ----------
1270
+ path: str
1271
+ The path that would be used to load the predictor via `predictor.load(path)`
1272
+
1273
+ Returns
1274
+ -------
1275
+ The version of AutoGluon used to fit the predictor, as a string.
1276
+
1277
+ """
881
1278
  version_file_path = os.path.join(path, cls._predictor_version_file_name)
882
- version = load_str.load(path=version_file_path)
1279
+ try:
1280
+ version = load_str.load(path=version_file_path)
1281
+ except:
1282
+ # Loads the old version file used in `autogluon.timeseries<=1.1.0`, named `__version__`.
1283
+ # This file name was changed because Kaggle does not allow uploading files named `__version__`.
1284
+ version_file_path = os.path.join(path, "__version__")
1285
+ version = load_str.load(path=version_file_path)
883
1286
  return version
884
1287
 
885
1288
  @classmethod
886
- def load(cls, path: Union[str, Path], require_version_match: bool = True) -> "TimeSeriesPredictor":
1289
+ def load(cls, path: str | Path, require_version_match: bool = True) -> "TimeSeriesPredictor":
887
1290
  """Load an existing ``TimeSeriesPredictor`` from given ``path``.
888
1291
 
1292
+ .. warning::
1293
+
1294
+ :meth:`autogluon.timeseries.TimeSeriesPredictor.load` uses ``pickle`` module implicitly, which is known to
1295
+ be insecure. It is possible to construct malicious pickle data which will execute arbitrary code during
1296
+ unpickling. Never load data that could have come from an untrusted source, or that could have been tampered
1297
+ with. **Only load data you trust.**
1298
+
889
1299
  Parameters
890
1300
  ----------
891
1301
  path : str or pathlib.Path
@@ -907,14 +1317,18 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
907
1317
  """
908
1318
  if not path:
909
1319
  raise ValueError("`path` cannot be None or empty in load().")
910
- path: str = setup_outputdir(path, warn_if_exist=False)
1320
+ path = setup_outputdir(path, warn_if_exist=False)
1321
+
1322
+ predictor_path = Path(path) / cls.predictor_file_name
1323
+ if not predictor_path.exists():
1324
+ raise FileNotFoundError(f"No such file '{predictor_path}'")
911
1325
 
912
1326
  try:
913
1327
  version_saved = cls._load_version_file(path=path)
914
1328
  except:
915
1329
  logger.warning(
916
1330
  f'WARNING: Could not find version file at "{os.path.join(path, cls._predictor_version_file_name)}".\n'
917
- f"This means that the predictor was fit in a version `<=0.7.0`."
1331
+ f"This means that the predictor was fit in an AutoGluon version `<=0.7.0`."
918
1332
  )
919
1333
  version_saved = "Unknown (Likely <=0.7.0)"
920
1334
 
@@ -926,13 +1340,13 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
926
1340
  )
927
1341
 
928
1342
  logger.info(f"Loading predictor from path {path}")
929
- learner = AbstractLearner.load(path)
930
- predictor = load_pkl.load(path=os.path.join(learner.path, cls.predictor_file_name))
1343
+ learner = cls._learner_type.load(path)
1344
+ predictor = load_pkl.load(path=str(predictor_path))
931
1345
  predictor._learner = learner
932
1346
  predictor.path = learner.path
933
1347
  return predictor
934
1348
 
935
- def _save_version_file(self):
1349
+ def _save_version_file(self) -> None:
936
1350
  version_file_contents = current_ag_version
937
1351
  version_file_path = os.path.join(self.path, self._predictor_version_file_name)
938
1352
  save_str.save(path=version_file_path, data=version_file_contents, verbose=False)
@@ -944,43 +1358,87 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
944
1358
  (we do not recommend modifying the Predictor object yourself as it tracks many trained models).
945
1359
  """
946
1360
  tmp_learner = self._learner
947
- self._learner = None
1361
+ self._learner = None # type: ignore
948
1362
  save_pkl.save(path=os.path.join(tmp_learner.path, self.predictor_file_name), object=self)
949
1363
  self._learner = tmp_learner
950
1364
  self._save_version_file()
951
1365
 
952
- def info(self) -> Dict[str, Any]:
1366
+ def info(self) -> dict[str, Any]:
953
1367
  """Returns a dictionary of objects each describing an attribute of the training process and trained models."""
954
1368
  return self._learner.get_info(include_model_info=True)
955
1369
 
956
1370
  @property
957
1371
  def model_best(self) -> str:
958
1372
  """Returns the name of the best model from trainer."""
1373
+ self._assert_is_fit("model_best")
959
1374
  if self._trainer.model_best is not None:
960
1375
  models = self._trainer.get_model_names()
961
1376
  if self._trainer.model_best in models:
962
1377
  return self._trainer.model_best
963
1378
  return self._trainer.get_model_best()
964
1379
 
1380
+ def persist(self, models: Literal["all", "best"] | list[str] = "best", with_ancestors: bool = True) -> list[str]:
1381
+ """Persist models in memory for reduced inference latency. This is particularly important if the models are being used for online
1382
+ inference where low latency is critical. If models are not persisted in memory, they are loaded from disk every time they are
1383
+ asked to make predictions. This is especially cumbersome for large deep learning based models which have to be loaded into
1384
+ accelerator (e.g., GPU) memory each time.
1385
+
1386
+ Parameters
1387
+ ----------
1388
+ models : list of str or str, default = 'best'
1389
+ Model names of models to persist.
1390
+ If 'best' then the model with the highest validation score is persisted (this is the model used for prediction by default).
1391
+ If 'all' then all models are persisted. Valid models are listed in this ``predictor`` by calling ``predictor.model_names()``.
1392
+ with_ancestors : bool, default = True
1393
+ If True, all ancestor models of the provided models will also be persisted.
1394
+ If False, ensemble models will not have the models they depend on persisted unless those models were specified in ``models``.
1395
+ This will slow down inference as the ancestor models will still need to be loaded from disk for each predict call.
1396
+ Only relevant for ensemble models.
1397
+
1398
+ Returns
1399
+ -------
1400
+ list_of_models : list[str]
1401
+ List of persisted model names.
1402
+ """
1403
+ self._assert_is_fit("persist")
1404
+ return self._learner.persist_trainer(models=models, with_ancestors=with_ancestors)
1405
+
1406
+ def unpersist(self) -> list[str]:
1407
+ """Unpersist models in memory for reduced memory usage. If models are not persisted in memory, they are loaded from
1408
+ disk every time they are asked to make predictions.
1409
+
1410
+ Note: Another way to reset the predictor and unpersist models is to reload the predictor from disk
1411
+ via ``predictor = TimeSeriesPredictor.load(predictor.path)``.
1412
+
1413
+ Returns
1414
+ -------
1415
+ list_of_models : list[str]
1416
+ List of unpersisted model names.
1417
+ """
1418
+ return self._learner.unpersist_trainer()
1419
+
965
1420
  def leaderboard(
966
1421
  self,
967
- data: Optional[Union[TimeSeriesDataFrame, pd.DataFrame, str]] = None,
1422
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
1423
+ cutoff: int | None = None,
1424
+ extra_info: bool = False,
1425
+ extra_metrics: list[str | TimeSeriesScorer] | None = None,
968
1426
  display: bool = False,
969
1427
  use_cache: bool = True,
970
1428
  **kwargs,
971
1429
  ) -> pd.DataFrame:
972
1430
  """Return a leaderboard showing the performance of every trained model, the output is a
973
- pandas data frame with columns:
1431
+ pandas dataframe with columns:
974
1432
 
975
1433
  * ``model``: The name of the model.
976
1434
  * ``score_test``: The test score of the model on ``data``, if provided. Computed according to ``eval_metric``.
977
1435
  * ``score_val``: The validation score of the model using the internal validation data. Computed according to ``eval_metric``.
978
1436
 
979
1437
  .. note::
980
- Metrics scores are always shown in 'higher is better' format.
1438
+ Metrics are always reported in 'higher is better' format.
981
1439
  This means that metrics such as MASE or MAPE will be multiplied by -1, so their values will be negative.
982
1440
  This is necessary to avoid the user needing to know the metric to understand if higher is better when
983
- looking at leaderboard.
1441
+ looking at the leaderboard.
984
1442
 
985
1443
  * ``pred_time_val``: Time taken by the model to predict on the validation data set
986
1444
  * ``fit_time_marginal``: The fit time required to train the model (ignoring base models for ensembles).
@@ -989,19 +1447,35 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
989
1447
 
990
1448
  Parameters
991
1449
  ----------
992
- data : Union[TimeSeriesDataFrame, pd.DataFrame, str], optional
993
- dataset used for additional evaluation. If not provided, the validation set used during training will be
994
- used.
995
-
996
- If ``known_covariates_names`` were specified when creating the predictor, ``data`` must include the columns
997
- listed in ``known_covariates_names`` with the covariates values aligned with the target time series.
998
-
999
- If ``train_data`` used to train the predictor contained past covariates or static features, then ``data``
1000
- must also include them (with same column names and dtypes).
1001
-
1002
- If provided data is an instance of pandas DataFrame, AutoGluon will attempt to automatically convert it
1003
- to a ``TimeSeriesDataFrame``.
1004
-
1450
+ data : TimeSeriesDataFrame | pd.DataFrame | Path | str, optional
1451
+ dataset used for additional evaluation. Must include both historical and future data (i.e., length of all
1452
+ time series in ``data`` must be at least ``prediction_length + 1``, if ``cutoff`` is not provided,
1453
+ ``-cutoff + 1`` otherwise).
1454
+
1455
+ The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
1456
+ the predictor.
1457
+
1458
+ If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
1459
+ If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
1460
+ cutoff : int, optional
1461
+ A *negative* integer less than or equal to ``-1 * prediction_length`` denoting the time step in ``data``
1462
+ where the forecast evaluation starts, i.e., time series are evaluated from the ``-cutoff``-th to the
1463
+ ``-cutoff + prediction_length``-th time step. Defaults to ``-1 * prediction_length``, using the last
1464
+ ``prediction_length`` time steps of each time series for evaluation.
1465
+ extra_info : bool, default = False
1466
+ If True, the leaderboard will contain an additional column ``hyperparameters`` with the hyperparameters used
1467
+ by each model during training. An empty dictionary ``{}`` means that the model was trained with default
1468
+ hyperparameters.
1469
+ extra_metrics : list[str | TimeSeriesScorer], optional
1470
+ A list of metrics to calculate scores for and include in the output DataFrame.
1471
+
1472
+ Only valid when ``data`` is specified. The scores refer to the scores on ``data`` (same data as used to
1473
+ calculate the ``score_test`` column).
1474
+
1475
+ This list can contain any values which would also be valid for ``eval_metric`` when creating a :class:`~autogluon.timeseries.TimeSeriesPredictor`.
1476
+
1477
+ For each provided ``metric``, a column with name ``str(metric)`` will be added to the leaderboard, containing
1478
+ the value of the metric computed on ``data``.
1005
1479
  display : bool, default = False
1006
1480
  If True, the leaderboard DataFrame will be printed.
1007
1481
  use_cache : bool, default = True
@@ -1014,6 +1488,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1014
1488
  The leaderboard containing information on all models and in order of best model to worst in terms of
1015
1489
  test performance.
1016
1490
  """
1491
+ self._assert_is_fit("leaderboard")
1017
1492
  if "silent" in kwargs:
1018
1493
  # keep `silent` logic for backwards compatibility
1019
1494
  assert isinstance(kwargs["silent"], bool)
@@ -1021,17 +1496,62 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1021
1496
  if len(kwargs) > 0:
1022
1497
  for key in kwargs:
1023
1498
  raise TypeError(f"TimeSeriesPredictor.leaderboard() got an unexpected keyword argument '{key}'")
1499
+ if data is None and extra_metrics is not None:
1500
+ raise ValueError("`extra_metrics` is only valid when `data` is specified.")
1501
+ if data is None and cutoff is not None:
1502
+ raise ValueError("`cutoff` is only valid when `data` is specified.")
1024
1503
 
1025
1504
  if data is not None:
1026
1505
  data = self._check_and_prepare_data_frame(data)
1027
- self._check_data_for_evaluation(data)
1028
- leaderboard = self._learner.leaderboard(data, use_cache=use_cache)
1506
+ data = self._check_and_prepare_data_frame_for_evaluation(data, cutoff=cutoff)
1507
+
1508
+ leaderboard = self._learner.leaderboard(
1509
+ data, extra_info=extra_info, extra_metrics=extra_metrics, use_cache=use_cache
1510
+ )
1029
1511
  if display:
1030
1512
  with pd.option_context("display.max_rows", None, "display.max_columns", None, "display.width", 1000):
1031
1513
  print(leaderboard)
1032
1514
  return leaderboard
1033
1515
 
1034
- def fit_summary(self, verbosity: int = 1) -> Dict[str, Any]:
1516
+ def make_future_data_frame(self, data: TimeSeriesDataFrame | pd.DataFrame | Path | str) -> pd.DataFrame:
1517
+ """Generate a dataframe with the ``item_id`` and ``timestamp`` values corresponding to the forecast horizon.
1518
+
1519
+ Parameters
1520
+ ----------
1521
+ data : TimeSeriesDataFrame | pd.DataFrame | Path | str
1522
+ Historical time series data.
1523
+
1524
+ Returns
1525
+ -------
1526
+ forecast_horizon : pd.DataFrame
1527
+ Data frame with columns ``item_id`` and ``timestamp`` corresponding to the forecast horizon. For each item ID
1528
+ in ``data``, ``forecast_horizon`` will contain the timestamps for the next ``prediction_length`` time steps,
1529
+ following the end of each series in the input data.
1530
+
1531
+ Examples
1532
+ --------
1533
+ >>> print(data)
1534
+ target
1535
+ item_id timestamp
1536
+ A 2024-01-01 0
1537
+ 2024-01-02 1
1538
+ 2024-01-03 2
1539
+ B 2024-04-07 3
1540
+ 2024-04-08 4
1541
+ >>> predictor = TimeSeriesPredictor(prediction_length=2, freq="D")
1542
+ >>> print(predictor.make_future_data_frame(data))
1543
+ item_id timestamp
1544
+ 0 A 2024-01-04
1545
+ 0 A 2024-01-05
1546
+ 1 B 2024-04-09
1547
+ 1 B 2024-04-10
1548
+ """
1549
+ if self.freq is None:
1550
+ raise ValueError("Please fit the predictor before calling `make_future_data_frame`")
1551
+ data = self._check_and_prepare_data_frame(data)
1552
+ return make_future_data_frame(data, prediction_length=self.prediction_length, freq=self.freq)
1553
+
1554
+ def fit_summary(self, verbosity: int = 1) -> dict[str, Any]:
1035
1555
  """Output summary of information about models produced during
1036
1556
  :meth:`~autogluon.timeseries.TimeSeriesPredictor.fit`.
1037
1557
 
@@ -1042,10 +1562,11 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1042
1562
 
1043
1563
  Returns
1044
1564
  -------
1045
- summary_dict : Dict[str, Any]
1565
+ summary_dict : dict[str, Any]
1046
1566
  Dict containing various detailed information. We do not recommend directly printing this dict as it may
1047
1567
  be very large.
1048
1568
  """
1569
+ self._assert_is_fit("fit_summary")
1049
1570
  # TODO: HPO-specific information currently not reported in fit_summary
1050
1571
  # TODO: Revisit after ray tune integration
1051
1572
 
@@ -1066,7 +1587,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1066
1587
  model_hyperparams = {}
1067
1588
  for model_name in self.model_names():
1068
1589
  model_obj = self._trainer.load_model(model_name)
1069
- model_hyperparams[model_name] = model_obj.params
1590
+ model_hyperparams[model_name] = model_obj.get_hyperparameters()
1070
1591
 
1071
1592
  results["model_hyperparams"] = model_hyperparams
1072
1593
  results["leaderboard"] = self._learner.leaderboard()
@@ -1081,7 +1602,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1081
1602
  print("****************** End of fit() summary ******************")
1082
1603
  return results
1083
1604
 
1084
- def refit_full(self, model: str = "all", set_best_to_refit_full: bool = True) -> Dict[str, str]:
1605
+ def refit_full(self, model: str = "all", set_best_to_refit_full: bool = True) -> dict[str, str]:
1085
1606
  """Retrain model on all of the data (training + validation).
1086
1607
 
1087
1608
  This method can only be used if no ``tuning_data`` was passed to :meth:`~autogluon.timeseries.TimeSeriesPredictor.fit`.
@@ -1106,6 +1627,7 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1106
1627
  ``predictor.predict(data)`` is called will be the refit_full version instead of the original version of the
1107
1628
  model. Has no effect if ``model`` is not the best model.
1108
1629
  """
1630
+ self._assert_is_fit("refit_full")
1109
1631
  logger.warning(
1110
1632
  "\tWARNING: refit_full functionality for TimeSeriesPredictor is experimental "
1111
1633
  "and is not yet supported by all models."
@@ -1143,40 +1665,38 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1143
1665
  )
1144
1666
  return refit_full_dict
1145
1667
 
1146
- def __dir__(self) -> List[str]:
1147
- # This hides method from IPython autocomplete, but not VSCode autocomplete
1148
- deprecated = ["score", "get_model_best", "get_model_names"]
1149
- return [d for d in super().__dir__() if d not in deprecated]
1150
-
1151
1668
  def _simulation_artifact(self, test_data: TimeSeriesDataFrame) -> dict:
1152
1669
  """[Advanced] Computes and returns the necessary information to perform offline ensemble simulation."""
1153
1670
 
1154
1671
  def select_target(ts_df: TimeSeriesDataFrame) -> TimeSeriesDataFrame:
1155
1672
  ts_df = ts_df.copy()
1156
1673
  ts_df.static_features = None
1157
- return ts_df[[self.target]]
1674
+ return cast(TimeSeriesDataFrame, ts_df[[self.target]])
1158
1675
 
1159
1676
  test_data = self._check_and_prepare_data_frame(test_data)
1160
- self._check_data_for_evaluation(test_data, name="test_data")
1677
+ test_data = self._check_and_prepare_data_frame_for_evaluation(test_data, name="test_data")
1161
1678
  test_data = self._learner.feature_generator.transform(test_data)
1162
1679
 
1163
1680
  trainer = self._trainer
1164
1681
  train_data = trainer.load_train_data()
1165
1682
  val_data = trainer.load_val_data()
1166
- base_models = trainer.get_model_names(level=0)
1167
- pred_proba_dict_val: Dict[str, List[TimeSeriesDataFrame]] = {
1168
- model: trainer._get_model_oof_predictions(model) for model in base_models
1683
+ base_model_names = trainer.get_model_names(layer=0)
1684
+ pred_proba_dict_val: dict[str, list[TimeSeriesDataFrame]] = {
1685
+ model_name: trainer._get_model_oof_predictions(model_name)
1686
+ for model_name in base_model_names
1687
+ if "_FULL" not in model_name
1169
1688
  }
1170
1689
 
1171
1690
  past_data, known_covariates = test_data.get_model_inputs_for_scoring(
1172
- prediction_length=self.prediction_length, known_covariates_names=trainer.metadata.known_covariates_real
1691
+ prediction_length=self.prediction_length,
1692
+ known_covariates_names=trainer.covariate_metadata.known_covariates,
1173
1693
  )
1174
- pred_proba_dict_test: Dict[str, TimeSeriesDataFrame] = trainer.get_model_pred_dict(
1175
- base_models, data=past_data, known_covariates=known_covariates
1694
+ pred_proba_dict_test, _ = trainer.get_model_pred_dict(
1695
+ base_model_names, data=past_data, known_covariates=known_covariates
1176
1696
  )
1177
1697
 
1178
- y_val: List[TimeSeriesDataFrame] = [
1179
- select_target(df) for df in trainer._get_ensemble_oof_data(train_data=train_data, val_data=val_data)
1698
+ y_val: list[TimeSeriesDataFrame] = [
1699
+ select_target(df) for df in trainer._get_validation_windows(train_data=train_data, val_data=val_data)
1180
1700
  ]
1181
1701
  y_test: TimeSeriesDataFrame = select_target(test_data)
1182
1702
 
@@ -1188,34 +1708,35 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1188
1708
  target=self.target,
1189
1709
  prediction_length=self.prediction_length,
1190
1710
  eval_metric=self.eval_metric.name,
1191
- eval_metric_seasonal_period=self.eval_metric_seasonal_period,
1711
+ eval_metric_seasonal_period=self.eval_metric.seasonal_period,
1712
+ horizon_weight=self.eval_metric.horizon_weight,
1192
1713
  quantile_levels=self.quantile_levels,
1193
1714
  )
1194
1715
  return simulation_dict
1195
1716
 
1196
1717
  def plot(
1197
1718
  self,
1198
- data: Union[TimeSeriesDataFrame, pd.DataFrame, str],
1199
- predictions: Optional[Union[TimeSeriesDataFrame, pd.DataFrame, str]] = None,
1200
- quantile_levels: Optional[List[float]] = None,
1201
- item_ids: Optional[List[Union[str, int]]] = None,
1719
+ data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
1720
+ predictions: TimeSeriesDataFrame | None = None,
1721
+ quantile_levels: list[float] | None = None,
1722
+ item_ids: list[str | int] | None = None,
1202
1723
  max_num_item_ids: int = 8,
1203
- max_history_length: Optional[int] = None,
1204
- point_forecast_column: Optional[str] = None,
1205
- matplotlib_rc_params: Optional[dict] = None,
1724
+ max_history_length: int | None = None,
1725
+ point_forecast_column: str | None = None,
1726
+ matplotlib_rc_params: dict | None = None,
1206
1727
  ):
1207
- """Plot historic time series values and the forecasts.
1728
+ """Plot historical time series values and the forecasts.
1208
1729
 
1209
1730
  Parameters
1210
1731
  ----------
1211
- data : Union[TimeSeriesDataFrame, pd.DataFrame, str]
1732
+ data : TimeSeriesDataFrame | pd.DataFrame | Path | str
1212
1733
  Observed time series data.
1213
1734
  predictions : TimeSeriesDataFrame, optional
1214
1735
  Predictions generated by calling :meth:`~autogluon.timeseries.TimeSeriesPredictor.predict`.
1215
- quantile_levels : List[float], optional
1736
+ quantile_levels : list[float], optional
1216
1737
  Quantile levels for which to plot the prediction intervals. Defaults to lowest & highest quantile levels
1217
1738
  available in ``predictions``.
1218
- item_ids : List[Union[str, int]], optional
1739
+ item_ids : list[str | int], optional
1219
1740
  If provided, plots will only be generated for time series with these item IDs. By default (if set to
1220
1741
  ``None``), item IDs are selected randomly. In either case, plots are generated for at most
1221
1742
  ``max_num_item_ids`` time series.
@@ -1227,8 +1748,8 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1227
1748
  Name of the column in ``predictions`` that will be plotted as the point forecast. Defaults to ``"0.5"``,
1228
1749
  if this column is present in ``predictions``, otherwise ``"mean"``.
1229
1750
  matplotlib_rc_params : dict, optional
1230
- Dictionary describing the plot style that will be passed to [`matplotlib.pyplot.rc_context`](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.rc_context.html).
1231
- See [matplotlib documentation](https://matplotlib.org/stable/users/explain/customizing.html#the-default-matplotlibrc-file) for the list of available options.
1751
+ Dictionary describing the plot style that will be passed to `matplotlib.pyplot.rc_context <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.rc_context.html>`_.
1752
+ See `matplotlib documentation <https://matplotlib.org/stable/users/explain/customizing.html#the-default-matplotlibrc-file>`_ for the list of available options.
1232
1753
  """
1233
1754
  import matplotlib.pyplot as plt
1234
1755
 
@@ -1291,14 +1812,14 @@ class TimeSeriesPredictor(TimeSeriesPredictorDeprecatedMixin):
1291
1812
  ax.plot(ts, label="Observed", color="C0")
1292
1813
 
1293
1814
  if predictions is not None:
1294
- forecast = predictions.loc[item_id]
1815
+ forecast: pd.DataFrame = predictions.loc[item_id] # type: ignore
1295
1816
  point_forecast = forecast[point_forecast_column]
1296
1817
  ax.plot(point_forecast, color="C1", label="Forecast")
1297
1818
  if quantile_levels is not None:
1298
1819
  for q in quantile_levels:
1299
1820
  ax.fill_between(forecast.index, point_forecast, forecast[str(q)], color="C1", alpha=0.2)
1300
1821
  if len(axes) > len(item_ids):
1301
- axes[len(item_ids)].set_axis_off()
1302
- handles, labels = axes[0].get_legend_handles_labels()
1822
+ axes[len(item_ids)].set_axis_off() # type: ignore
1823
+ handles, labels = axes[0].get_legend_handles_labels() # type: ignore
1303
1824
  fig.legend(handles, labels, bbox_to_anchor=(0.5, 0.0), ncols=len(handles))
1304
1825
  return fig