autogluon.timeseries 1.4.1b20251115__py3-none-any.whl → 1.5.0b20251221__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.
- autogluon/timeseries/configs/hyperparameter_presets.py +13 -28
- autogluon/timeseries/configs/predictor_presets.py +23 -39
- autogluon/timeseries/dataset/ts_dataframe.py +32 -34
- autogluon/timeseries/learner.py +67 -33
- autogluon/timeseries/metrics/__init__.py +4 -4
- autogluon/timeseries/metrics/abstract.py +8 -8
- autogluon/timeseries/metrics/point.py +9 -9
- autogluon/timeseries/metrics/quantile.py +4 -4
- autogluon/timeseries/models/__init__.py +2 -1
- autogluon/timeseries/models/abstract/abstract_timeseries_model.py +52 -50
- autogluon/timeseries/models/abstract/model_trial.py +2 -1
- autogluon/timeseries/models/abstract/tunable.py +8 -8
- autogluon/timeseries/models/autogluon_tabular/mlforecast.py +30 -26
- autogluon/timeseries/models/autogluon_tabular/per_step.py +13 -11
- autogluon/timeseries/models/autogluon_tabular/transforms.py +2 -2
- autogluon/timeseries/models/chronos/__init__.py +2 -1
- autogluon/timeseries/models/chronos/chronos2.py +395 -0
- autogluon/timeseries/models/chronos/model.py +30 -25
- autogluon/timeseries/models/chronos/utils.py +5 -5
- autogluon/timeseries/models/ensemble/__init__.py +17 -10
- autogluon/timeseries/models/ensemble/abstract.py +13 -9
- autogluon/timeseries/models/ensemble/array_based/__init__.py +2 -2
- autogluon/timeseries/models/ensemble/array_based/abstract.py +24 -31
- autogluon/timeseries/models/ensemble/array_based/models.py +146 -11
- autogluon/timeseries/models/ensemble/array_based/regressor/__init__.py +2 -0
- autogluon/timeseries/models/ensemble/array_based/regressor/abstract.py +6 -5
- autogluon/timeseries/models/ensemble/array_based/regressor/linear_stacker.py +186 -0
- autogluon/timeseries/models/ensemble/array_based/regressor/per_quantile_tabular.py +44 -83
- autogluon/timeseries/models/ensemble/array_based/regressor/tabular.py +21 -55
- autogluon/timeseries/models/ensemble/ensemble_selection.py +167 -0
- autogluon/timeseries/models/ensemble/per_item_greedy.py +172 -0
- autogluon/timeseries/models/ensemble/weighted/abstract.py +7 -3
- autogluon/timeseries/models/ensemble/weighted/basic.py +26 -13
- autogluon/timeseries/models/ensemble/weighted/greedy.py +21 -144
- autogluon/timeseries/models/gluonts/abstract.py +30 -29
- autogluon/timeseries/models/gluonts/dataset.py +9 -9
- autogluon/timeseries/models/gluonts/models.py +0 -7
- autogluon/timeseries/models/local/__init__.py +0 -7
- autogluon/timeseries/models/local/abstract_local_model.py +13 -16
- autogluon/timeseries/models/local/naive.py +2 -2
- autogluon/timeseries/models/local/npts.py +7 -1
- autogluon/timeseries/models/local/statsforecast.py +13 -13
- autogluon/timeseries/models/multi_window/multi_window_model.py +38 -23
- autogluon/timeseries/models/registry.py +3 -4
- autogluon/timeseries/models/toto/_internal/backbone/attention.py +3 -4
- autogluon/timeseries/models/toto/_internal/backbone/backbone.py +6 -6
- autogluon/timeseries/models/toto/_internal/backbone/rope.py +4 -9
- autogluon/timeseries/models/toto/_internal/backbone/rotary_embedding_torch.py +342 -0
- autogluon/timeseries/models/toto/_internal/backbone/scaler.py +2 -3
- autogluon/timeseries/models/toto/_internal/backbone/transformer.py +10 -10
- autogluon/timeseries/models/toto/_internal/dataset.py +2 -2
- autogluon/timeseries/models/toto/_internal/forecaster.py +8 -8
- autogluon/timeseries/models/toto/dataloader.py +4 -4
- autogluon/timeseries/models/toto/hf_pretrained_model.py +97 -16
- autogluon/timeseries/models/toto/model.py +30 -17
- autogluon/timeseries/predictor.py +531 -136
- autogluon/timeseries/regressor.py +18 -23
- autogluon/timeseries/splitter.py +2 -2
- autogluon/timeseries/trainer/ensemble_composer.py +323 -129
- autogluon/timeseries/trainer/model_set_builder.py +9 -9
- autogluon/timeseries/trainer/prediction_cache.py +16 -16
- autogluon/timeseries/trainer/trainer.py +235 -145
- autogluon/timeseries/trainer/utils.py +3 -4
- autogluon/timeseries/transforms/covariate_scaler.py +7 -7
- autogluon/timeseries/transforms/target_scaler.py +8 -8
- autogluon/timeseries/utils/constants.py +10 -0
- autogluon/timeseries/utils/datetime/lags.py +1 -3
- autogluon/timeseries/utils/datetime/seasonality.py +1 -3
- autogluon/timeseries/utils/features.py +22 -9
- autogluon/timeseries/utils/forecast.py +1 -2
- autogluon/timeseries/utils/timer.py +173 -0
- autogluon/timeseries/version.py +1 -1
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/METADATA +23 -21
- autogluon_timeseries-1.5.0b20251221.dist-info/RECORD +103 -0
- autogluon_timeseries-1.4.1b20251115.dist-info/RECORD +0 -96
- /autogluon.timeseries-1.4.1b20251115-py3.9-nspkg.pth → /autogluon.timeseries-1.5.0b20251221-py3.11-nspkg.pth +0 -0
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/WHEEL +0 -0
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/licenses/LICENSE +0 -0
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/licenses/NOTICE +0 -0
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/namespace_packages.txt +0 -0
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/top_level.txt +0 -0
- {autogluon_timeseries-1.4.1b20251115.dist-info → autogluon_timeseries-1.5.0b20251221.dist-info}/zip-safe +0 -0
|
@@ -5,11 +5,12 @@ import os
|
|
|
5
5
|
import pprint
|
|
6
6
|
import time
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Literal,
|
|
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.decorators import apply_presets
|
|
13
14
|
from autogluon.common.utils.log_utils import (
|
|
14
15
|
add_log_to_file,
|
|
15
16
|
set_logger_verbosity,
|
|
@@ -17,7 +18,6 @@ from autogluon.common.utils.log_utils import (
|
|
|
17
18
|
)
|
|
18
19
|
from autogluon.common.utils.system_info import get_ag_system_info
|
|
19
20
|
from autogluon.common.utils.utils import check_saved_predictor_version, setup_outputdir
|
|
20
|
-
from autogluon.core.utils.decorators import apply_presets
|
|
21
21
|
from autogluon.core.utils.loaders import load_pkl, load_str
|
|
22
22
|
from autogluon.core.utils.savers import save_pkl, save_str
|
|
23
23
|
from autogluon.timeseries import __version__ as current_ag_version
|
|
@@ -66,7 +66,7 @@ class TimeSeriesPredictor:
|
|
|
66
66
|
|
|
67
67
|
If ``freq`` is provided when creating the predictor, all data passed to the predictor will be automatically
|
|
68
68
|
resampled at this frequency.
|
|
69
|
-
eval_metric :
|
|
69
|
+
eval_metric : str | TimeSeriesScorer, default = "WQL"
|
|
70
70
|
Metric by which predictions will be ultimately evaluated on future test data. AutoGluon tunes hyperparameters
|
|
71
71
|
in order to improve this metric on validation data, and ranks models (on validation data) according to this
|
|
72
72
|
metric.
|
|
@@ -124,7 +124,7 @@ class TimeSeriesPredictor:
|
|
|
124
124
|
debug messages from AutoGluon and all logging in dependencies (GluonTS, PyTorch Lightning, AutoGluon-Tabular, etc.)
|
|
125
125
|
log_to_file: bool, default = True
|
|
126
126
|
Whether to save the logs into a file for later reference
|
|
127
|
-
log_file_path:
|
|
127
|
+
log_file_path: str | Path, default = "auto"
|
|
128
128
|
File path to save the logs.
|
|
129
129
|
If auto, logs will be saved under ``predictor_path/logs/predictor_log.txt``.
|
|
130
130
|
Will be ignored if ``log_to_file`` is set to False
|
|
@@ -145,20 +145,20 @@ class TimeSeriesPredictor:
|
|
|
145
145
|
|
|
146
146
|
def __init__(
|
|
147
147
|
self,
|
|
148
|
-
target:
|
|
149
|
-
known_covariates_names:
|
|
148
|
+
target: str | None = None,
|
|
149
|
+
known_covariates_names: list[str] | None = None,
|
|
150
150
|
prediction_length: int = 1,
|
|
151
|
-
freq:
|
|
152
|
-
eval_metric:
|
|
153
|
-
eval_metric_seasonal_period:
|
|
154
|
-
horizon_weight:
|
|
155
|
-
path:
|
|
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,
|
|
156
156
|
verbosity: int = 2,
|
|
157
157
|
log_to_file: bool = True,
|
|
158
|
-
log_file_path:
|
|
159
|
-
quantile_levels:
|
|
158
|
+
log_file_path: str | Path = "auto",
|
|
159
|
+
quantile_levels: list[float] | None = None,
|
|
160
160
|
cache_predictions: bool = True,
|
|
161
|
-
label:
|
|
161
|
+
label: str | None = None,
|
|
162
162
|
**kwargs,
|
|
163
163
|
):
|
|
164
164
|
self.verbosity = verbosity
|
|
@@ -228,7 +228,16 @@ class TimeSeriesPredictor:
|
|
|
228
228
|
def _trainer(self) -> TimeSeriesTrainer:
|
|
229
229
|
return self._learner.load_trainer() # noqa
|
|
230
230
|
|
|
231
|
-
|
|
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:
|
|
232
241
|
if log_to_file:
|
|
233
242
|
if log_file_path == "auto":
|
|
234
243
|
log_file_path = os.path.join(self.path, "logs", self._predictor_log_file_name)
|
|
@@ -238,7 +247,7 @@ class TimeSeriesPredictor:
|
|
|
238
247
|
|
|
239
248
|
def _to_data_frame(
|
|
240
249
|
self,
|
|
241
|
-
data:
|
|
250
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
|
|
242
251
|
name: str = "data",
|
|
243
252
|
) -> TimeSeriesDataFrame:
|
|
244
253
|
if isinstance(data, TimeSeriesDataFrame):
|
|
@@ -259,7 +268,7 @@ class TimeSeriesPredictor:
|
|
|
259
268
|
|
|
260
269
|
def _check_and_prepare_data_frame(
|
|
261
270
|
self,
|
|
262
|
-
data:
|
|
271
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
|
|
263
272
|
name: str = "data",
|
|
264
273
|
) -> TimeSeriesDataFrame:
|
|
265
274
|
"""Ensure that TimeSeriesDataFrame has a sorted index and a valid frequency.
|
|
@@ -268,7 +277,7 @@ class TimeSeriesPredictor:
|
|
|
268
277
|
|
|
269
278
|
Parameters
|
|
270
279
|
----------
|
|
271
|
-
data :
|
|
280
|
+
data : TimeSeriesDataFrame | pd.DataFrame | Path | str
|
|
272
281
|
Data as a dataframe or path to file storing the data.
|
|
273
282
|
name : str
|
|
274
283
|
Name of the data that will be used in log messages (e.g., 'train_data', 'tuning_data', or 'data').
|
|
@@ -311,7 +320,7 @@ class TimeSeriesPredictor:
|
|
|
311
320
|
return df
|
|
312
321
|
|
|
313
322
|
def _check_and_prepare_data_frame_for_evaluation(
|
|
314
|
-
self, data: TimeSeriesDataFrame, cutoff:
|
|
323
|
+
self, data: TimeSeriesDataFrame, cutoff: int | None = None, name: str = "data"
|
|
315
324
|
) -> TimeSeriesDataFrame:
|
|
316
325
|
"""
|
|
317
326
|
Make sure that provided evaluation data includes both historical and future time series values.
|
|
@@ -351,36 +360,10 @@ class TimeSeriesPredictor:
|
|
|
351
360
|
f"Median time series length is {median_length:.0f} (min={min_length}, max={max_length}). "
|
|
352
361
|
)
|
|
353
362
|
|
|
354
|
-
def _reduce_num_val_windows_if_necessary(
|
|
355
|
-
self,
|
|
356
|
-
train_data: TimeSeriesDataFrame,
|
|
357
|
-
original_num_val_windows: int,
|
|
358
|
-
val_step_size: int,
|
|
359
|
-
) -> int:
|
|
360
|
-
"""Adjust num_val_windows based on the length of time series in train_data.
|
|
361
|
-
|
|
362
|
-
Chooses num_val_windows such that TS with median length is long enough to perform num_val_windows validations
|
|
363
|
-
(at least 1, at most `original_num_val_windows`).
|
|
364
|
-
|
|
365
|
-
In other words, find largest `num_val_windows` that satisfies
|
|
366
|
-
median_length >= min_train_length + prediction_length + (num_val_windows - 1) * val_step_size
|
|
367
|
-
"""
|
|
368
|
-
median_length = train_data.num_timesteps_per_item().median()
|
|
369
|
-
num_val_windows_for_median_ts = int(
|
|
370
|
-
(median_length - self._min_train_length - self.prediction_length) // val_step_size + 1
|
|
371
|
-
)
|
|
372
|
-
new_num_val_windows = min(original_num_val_windows, max(1, num_val_windows_for_median_ts))
|
|
373
|
-
if new_num_val_windows < original_num_val_windows:
|
|
374
|
-
logger.warning(
|
|
375
|
-
f"Time series in train_data are too short for chosen num_val_windows={original_num_val_windows}. "
|
|
376
|
-
f"Reducing num_val_windows to {new_num_val_windows}."
|
|
377
|
-
)
|
|
378
|
-
return new_num_val_windows
|
|
379
|
-
|
|
380
363
|
def _filter_useless_train_data(
|
|
381
364
|
self,
|
|
382
365
|
train_data: TimeSeriesDataFrame,
|
|
383
|
-
num_val_windows: int,
|
|
366
|
+
num_val_windows: tuple[int, ...],
|
|
384
367
|
val_step_size: int,
|
|
385
368
|
) -> TimeSeriesDataFrame:
|
|
386
369
|
"""Remove time series from train_data that either contain all NaNs or are too short for chosen settings.
|
|
@@ -391,7 +374,8 @@ class TimeSeriesPredictor:
|
|
|
391
374
|
In other words, this method removes from train_data all time series with only NaN values or length less than
|
|
392
375
|
min_train_length + prediction_length + (num_val_windows - 1) * val_step_size
|
|
393
376
|
"""
|
|
394
|
-
|
|
377
|
+
total_num_val_windows = sum(num_val_windows)
|
|
378
|
+
min_length = self._min_train_length + self.prediction_length + (total_num_val_windows - 1) * val_step_size
|
|
395
379
|
train_lengths = train_data.num_timesteps_per_item()
|
|
396
380
|
too_short_items = train_lengths.index[train_lengths < min_length]
|
|
397
381
|
|
|
@@ -422,27 +406,28 @@ class TimeSeriesPredictor:
|
|
|
422
406
|
@apply_presets(get_predictor_presets())
|
|
423
407
|
def fit(
|
|
424
408
|
self,
|
|
425
|
-
train_data:
|
|
426
|
-
tuning_data:
|
|
427
|
-
time_limit:
|
|
428
|
-
presets:
|
|
429
|
-
hyperparameters:
|
|
430
|
-
hyperparameter_tune_kwargs:
|
|
431
|
-
excluded_model_types:
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
409
|
+
train_data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
|
|
410
|
+
tuning_data: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
|
|
411
|
+
time_limit: int | None = None,
|
|
412
|
+
presets: str | None = None,
|
|
413
|
+
hyperparameters: str | dict[str | Type, Any] | None = None,
|
|
414
|
+
hyperparameter_tune_kwargs: str | dict | None = None,
|
|
415
|
+
excluded_model_types: list[str] | None = None,
|
|
416
|
+
ensemble_hyperparameters: dict[str, Any] | list[dict[str, Any]] | None = None,
|
|
417
|
+
num_val_windows: int | tuple[int, ...] | Literal["auto"] = 1,
|
|
418
|
+
val_step_size: int | None = None,
|
|
419
|
+
refit_every_n_windows: int | None | Literal["auto"] = 1,
|
|
435
420
|
refit_full: bool = False,
|
|
436
421
|
enable_ensemble: bool = True,
|
|
437
422
|
skip_model_selection: bool = False,
|
|
438
|
-
random_seed:
|
|
439
|
-
verbosity:
|
|
423
|
+
random_seed: int | None = 123,
|
|
424
|
+
verbosity: int | None = None,
|
|
440
425
|
) -> "TimeSeriesPredictor":
|
|
441
426
|
"""Fit probabilistic forecasting models to the given time series dataset.
|
|
442
427
|
|
|
443
428
|
Parameters
|
|
444
429
|
----------
|
|
445
|
-
train_data :
|
|
430
|
+
train_data : TimeSeriesDataFrame | pd.DataFrame | Path | str
|
|
446
431
|
Training data in the :class:`~autogluon.timeseries.TimeSeriesDataFrame` format.
|
|
447
432
|
|
|
448
433
|
Time series with length ``<= (num_val_windows + 1) * prediction_length`` will be ignored during training.
|
|
@@ -468,7 +453,7 @@ class TimeSeriesPredictor:
|
|
|
468
453
|
|
|
469
454
|
If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
|
|
470
455
|
If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
|
|
471
|
-
tuning_data :
|
|
456
|
+
tuning_data : TimeSeriesDataFrame | pd.DataFrame | Path | str, optional
|
|
472
457
|
Data reserved for model selection and hyperparameter tuning, rather than training individual models. Also
|
|
473
458
|
used to compute the validation scores. Note that only the last ``prediction_length`` time steps of each
|
|
474
459
|
time series are used for computing the validation score.
|
|
@@ -502,18 +487,23 @@ class TimeSeriesPredictor:
|
|
|
502
487
|
Available presets:
|
|
503
488
|
|
|
504
489
|
- ``"fast_training"``: Simple statistical and tree-based ML models. These models are fast to train but may not be very accurate.
|
|
505
|
-
- ``"medium_quality"``: Same models as above, plus deep learning models ``TemporalFusionTransformer`` and Chronos-
|
|
490
|
+
- ``"medium_quality"``: Same models as above, plus deep learning models ``TemporalFusionTransformer`` and Chronos-2 (small). Produces good forecasts with reasonable training time.
|
|
506
491
|
- ``"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.
|
|
507
492
|
- ``"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.
|
|
508
493
|
|
|
509
|
-
Available presets with the `Chronos-Bolt <https://github.com/amazon-science/chronos-forecasting>`_
|
|
494
|
+
Available presets with the `Chronos-2 and Chronos-Bolt <https://github.com/amazon-science/chronos-forecasting>`_ models:
|
|
510
495
|
|
|
496
|
+
- ``"chronos2"``: `Chronos-2 <https://huggingface.co/amazon/chronos-2>`_ base model for zero-shot forecasting.
|
|
497
|
+
- ``"chronos2_small"``: Smaller Chronos-2 model for faster zero-shot forecasting with lower memory footprint.
|
|
498
|
+
- ``"chronos2_ensemble"``: Ensemble combining zero-shot Chronos-2 base model with fine-tuned Chronos-2 small model for improved accuracy.
|
|
511
499
|
- ``"bolt_{model_size}"``: where model size is one of ``tiny,mini,small,base``. Uses the Chronos-Bolt pretrained model for zero-shot forecasting.
|
|
512
|
-
See the documentation for ``ChronosModel`` or see `Hugging Face <https://huggingface.co/collections/amazon/chronos-models-65f1791d630a8d57cb718444>`_ for more information.
|
|
513
500
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
501
|
+
See the documentation for ``Chronos2`` and ``Chronos`` models in :ref:`Forecasting Time Series - Model Zoo <forecasting_model_zoo>`
|
|
502
|
+
or see `Hugging Face <https://huggingface.co/collections/amazon/chronos-models-65f1791d630a8d57cb718444>`_ for more information.
|
|
503
|
+
|
|
504
|
+
Exact definitions of all presets can be found in the source code
|
|
505
|
+
[`1 <https://github.com/autogluon/autogluon/blob/stable/timeseries/src/autogluon/timeseries/configs/predictor_presets.py>`_,
|
|
506
|
+
`2 <https://github.com/autogluon/autogluon/blob/stable/timeseries/src/autogluon/timeseries/configs/hyperparameter_presets.py>`_].
|
|
517
507
|
|
|
518
508
|
If no ``presets`` are selected, user-provided values for ``hyperparameters`` will be used (defaulting to their
|
|
519
509
|
default values specified below).
|
|
@@ -589,6 +579,8 @@ class TimeSeriesPredictor:
|
|
|
589
579
|
* "bayes": Perform HPO with HyperOpt on GluonTS-backed models via Ray tune. Perform random search on other models.
|
|
590
580
|
* "auto": alias for "bayes"
|
|
591
581
|
|
|
582
|
+
To enable HyperOpt, install the corresponding extra with ``pip install "autogluon.timeseries[ray]"``.
|
|
583
|
+
|
|
592
584
|
The "scheduler" and "searcher" key are required when providing a dict.
|
|
593
585
|
|
|
594
586
|
Example::
|
|
@@ -610,13 +602,39 @@ class TimeSeriesPredictor:
|
|
|
610
602
|
presets="high_quality",
|
|
611
603
|
excluded_model_types=["DeepAR"],
|
|
612
604
|
)
|
|
613
|
-
|
|
605
|
+
ensemble_hyperparameters : dict or list of dict, optional
|
|
606
|
+
Hyperparameters for ensemble models. Can be a single dict for one ensemble layer, or a list of dicts
|
|
607
|
+
for multiple ensemble layers (multi-layer stacking).
|
|
608
|
+
|
|
609
|
+
For single-layer ensembling (default)::
|
|
610
|
+
|
|
611
|
+
predictor.fit(
|
|
612
|
+
...,
|
|
613
|
+
ensemble_hyperparameters={"WeightedEnsemble": {"ensemble_size": 10}},
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
For multi-layer ensembling, provide a list where each element configures one ensemble layer::
|
|
617
|
+
|
|
618
|
+
predictor.fit(
|
|
619
|
+
...,
|
|
620
|
+
num_val_windows=(2, 3),
|
|
621
|
+
ensemble_hyperparameters=[
|
|
622
|
+
{"WeightedEnsemble": {"ensemble_size": 5}, "SimpleAverageEnsemble": {}}, # Layer 1
|
|
623
|
+
{"PerformanceWeightedEnsemble": {}}, # Layer 2
|
|
624
|
+
],
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
When using multi-layer ensembling, ``num_val_windows`` must be a tuple of integers, and ``len(ensemble_hyperparameters)`` must match ``len(num_val_windows)``.
|
|
628
|
+
num_val_windows : int | tuple[int, ...] | "auto", default = 1
|
|
614
629
|
Number of backtests done on ``train_data`` for each trained model to estimate the validation performance.
|
|
615
|
-
|
|
616
|
-
of time series in ``train_data`` are long enough for the chosen number of backtests.
|
|
630
|
+
This parameter is also used to control multi-layer ensembling.
|
|
617
631
|
|
|
618
|
-
|
|
619
|
-
|
|
632
|
+
If set to ``"auto"``, the value will be determined automatically based on dataset properties (number of
|
|
633
|
+
time series and median time series length).
|
|
634
|
+
|
|
635
|
+
Increasing this parameter increases the training time roughly by a factor of
|
|
636
|
+
``num_val_windows // refit_every_n_windows``. See ``refit_every_n_windows`` and ``val_step_size`` for
|
|
637
|
+
details.
|
|
620
638
|
|
|
621
639
|
For example, for ``prediction_length=2``, ``num_val_windows=3`` and ``val_step_size=1`` the folds are::
|
|
622
640
|
|
|
@@ -627,17 +645,41 @@ class TimeSeriesPredictor:
|
|
|
627
645
|
|
|
628
646
|
where ``x`` are the train time steps and ``y`` are the validation time steps.
|
|
629
647
|
|
|
630
|
-
This
|
|
648
|
+
This parameter can also be used to control how many of the backtesting windows are reserved for training
|
|
649
|
+
multiple layers of ensemble models. By default, AutoGluon-TimeSeries uses only a single layer of ensembles
|
|
650
|
+
trained on the backtest windows specified by the ``num_val_windows`` parameter. However, the
|
|
651
|
+
``ensemble_hyperparameters`` argument can be used to specify multiple layers of ensembles. In this case,
|
|
652
|
+
a tuple of integers can be provided in ``num_val_windows`` to control how many of the backtesting windows
|
|
653
|
+
will be used to train which ensemble layers.
|
|
654
|
+
|
|
655
|
+
For example, if ``len(ensemble_hyperparameters) == 2``, a 2-tuple ``num_val_windows=(2, 3)`` is analogous
|
|
656
|
+
to ``num_val_windows=5``, except the first layer of ensemble models will be trained on the first two
|
|
657
|
+
backtest windows, and the second layer will be trained on the latter three. Validation scores of all models
|
|
658
|
+
will be computed on the last three windows.
|
|
659
|
+
|
|
660
|
+
If ``len(ensemble_hyperparameters) == 1``, then ``num_val_windows=(5,)`` has the same effect as
|
|
661
|
+
``num_val_windows=5``.
|
|
662
|
+
|
|
663
|
+
If ``tuning_data`` is provided and ``len(ensemble_hyperparameters) == 1``, then this parameter is ignored.
|
|
664
|
+
Validation and ensemble training will be performed on ``tuning_data``.
|
|
665
|
+
|
|
666
|
+
If ``tuning_data`` is provided and ``len(ensemble_hyperparameters) > 1``, then this method expects that
|
|
667
|
+
``len(num_val_windows) > 1``. In this case, the last element of ``num_val_windows`` will be ignored. The
|
|
668
|
+
last layer of ensemble training will be performed on ``tuning_data``. Validation scores will likewise be
|
|
669
|
+
computed on ``tuning_data``.
|
|
670
|
+
|
|
631
671
|
val_step_size : int or None, default = None
|
|
632
672
|
Step size between consecutive validation windows. If set to ``None``, defaults to ``prediction_length``
|
|
633
673
|
provided when creating the predictor.
|
|
634
674
|
|
|
635
|
-
|
|
636
|
-
refit_every_n_windows: int
|
|
675
|
+
If ``tuning_data`` is provided and ``len(ensemble_hyperparameters) == 1``, then this parameter is ignored.
|
|
676
|
+
refit_every_n_windows: int | None | "auto", default = 1
|
|
637
677
|
When performing cross validation, each model will be retrained every ``refit_every_n_windows`` validation
|
|
638
678
|
windows, where the number of validation windows is specified by ``num_val_windows``. Note that in the
|
|
639
679
|
default setting where ``num_val_windows=1``, this argument has no effect.
|
|
640
680
|
|
|
681
|
+
If set to ``"auto"``, the value will be determined automatically based on ``num_val_windows``.
|
|
682
|
+
|
|
641
683
|
If set to ``None``, models will only be fit once for the first (oldest) validation window. By default,
|
|
642
684
|
``refit_every_n_windows=1``, i.e., all models will be refit for each validation window.
|
|
643
685
|
refit_full : bool, default = False
|
|
@@ -660,8 +702,10 @@ class TimeSeriesPredictor:
|
|
|
660
702
|
|
|
661
703
|
"""
|
|
662
704
|
time_start = time.time()
|
|
663
|
-
if self.
|
|
664
|
-
raise AssertionError(
|
|
705
|
+
if self.is_fit:
|
|
706
|
+
raise AssertionError(
|
|
707
|
+
"Predictor is already fit! To fit additional models create a new `TimeSeriesPredictor`."
|
|
708
|
+
)
|
|
665
709
|
|
|
666
710
|
if verbosity is None:
|
|
667
711
|
verbosity = self.verbosity
|
|
@@ -707,36 +751,57 @@ class TimeSeriesPredictor:
|
|
|
707
751
|
|
|
708
752
|
if val_step_size is None:
|
|
709
753
|
val_step_size = self.prediction_length
|
|
754
|
+
median_timeseries_length = int(train_data.num_timesteps_per_item().median())
|
|
755
|
+
|
|
756
|
+
# Early validation: check length mismatch when num_val_windows is explicitly provided
|
|
757
|
+
if num_val_windows != "auto" and ensemble_hyperparameters is not None:
|
|
758
|
+
num_layers = len(ensemble_hyperparameters) if isinstance(ensemble_hyperparameters, list) else 1
|
|
759
|
+
num_windows_tuple = num_val_windows if isinstance(num_val_windows, tuple) else (num_val_windows,)
|
|
760
|
+
if len(num_windows_tuple) != num_layers:
|
|
761
|
+
raise ValueError(
|
|
762
|
+
f"Length mismatch: num_val_windows has {len(num_windows_tuple)} element(s) but "
|
|
763
|
+
f"ensemble_hyperparameters has {num_layers} layer(s). These must match when num_val_windows "
|
|
764
|
+
f"is explicitly provided. Use num_val_windows='auto' to automatically determine the number of windows."
|
|
765
|
+
)
|
|
710
766
|
|
|
711
|
-
if num_val_windows
|
|
712
|
-
num_val_windows = self.
|
|
713
|
-
|
|
767
|
+
if num_val_windows == "auto":
|
|
768
|
+
num_val_windows = self._recommend_num_val_windows_auto(
|
|
769
|
+
median_timeseries_length=median_timeseries_length,
|
|
770
|
+
val_step_size=val_step_size,
|
|
771
|
+
num_items=train_data.num_items,
|
|
772
|
+
ensemble_hyperparameters=ensemble_hyperparameters,
|
|
714
773
|
)
|
|
774
|
+
logger.info(f"Automatically setting num_val_windows={num_val_windows} based on dataset properties")
|
|
775
|
+
|
|
776
|
+
num_val_windows, ensemble_hyperparameters = self._validate_and_normalize_validation_and_ensemble_inputs(
|
|
777
|
+
num_val_windows=num_val_windows,
|
|
778
|
+
ensemble_hyperparameters=ensemble_hyperparameters,
|
|
779
|
+
val_step_size=val_step_size,
|
|
780
|
+
median_timeseries_length=median_timeseries_length,
|
|
781
|
+
tuning_data_provided=tuning_data is not None,
|
|
782
|
+
)
|
|
715
783
|
|
|
716
784
|
if tuning_data is not None:
|
|
717
785
|
tuning_data = self._check_and_prepare_data_frame(tuning_data, name="tuning_data")
|
|
718
786
|
tuning_data = self._check_and_prepare_data_frame_for_evaluation(tuning_data, name="tuning_data")
|
|
719
787
|
logger.info(f"Provided tuning_data has {self._get_dataset_stats(tuning_data)}")
|
|
720
|
-
# TODO: Use num_val_windows to perform multi-window backtests on tuning_data
|
|
721
|
-
if num_val_windows > 0:
|
|
722
|
-
logger.warning(
|
|
723
|
-
"\tSetting num_val_windows = 0 (disabling backtesting on train_data) because tuning_data is provided."
|
|
724
|
-
)
|
|
725
|
-
num_val_windows = 0
|
|
726
788
|
|
|
727
|
-
if
|
|
728
|
-
|
|
789
|
+
if refit_every_n_windows == "auto":
|
|
790
|
+
refit_every_n_windows = self._recommend_refit_every_n_windows_auto(num_val_windows)
|
|
791
|
+
logger.info(
|
|
792
|
+
f"Automatically setting refit_every_n_windows={refit_every_n_windows} based on num_val_windows"
|
|
793
|
+
)
|
|
729
794
|
|
|
730
|
-
if num_val_windows <= 1 and refit_every_n_windows is not None and refit_every_n_windows > 1:
|
|
795
|
+
if sum(num_val_windows) <= 1 and refit_every_n_windows is not None and refit_every_n_windows > 1:
|
|
731
796
|
logger.warning(
|
|
732
|
-
f"\trefit_every_n_windows provided as {refit_every_n_windows} but num_val_windows is set to
|
|
733
|
-
"
|
|
797
|
+
f"\trefit_every_n_windows provided as {refit_every_n_windows} but num_val_windows is set to "
|
|
798
|
+
f"{num_val_windows}. refit_every_n_windows will have no effect."
|
|
734
799
|
)
|
|
735
800
|
|
|
736
801
|
if not skip_model_selection:
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
)
|
|
802
|
+
# When tuning_data is provided, ignore the last element of num_val_windows for filtering purposes
|
|
803
|
+
filter_num_val_windows = num_val_windows[:-1] if tuning_data is not None else num_val_windows
|
|
804
|
+
train_data = self._filter_useless_train_data(train_data, filter_num_val_windows, val_step_size)
|
|
740
805
|
|
|
741
806
|
time_left = None if time_limit is None else time_limit - (time.time() - time_start)
|
|
742
807
|
self._learner.fit(
|
|
@@ -745,6 +810,7 @@ class TimeSeriesPredictor:
|
|
|
745
810
|
val_data=tuning_data,
|
|
746
811
|
hyperparameter_tune_kwargs=hyperparameter_tune_kwargs,
|
|
747
812
|
excluded_model_types=excluded_model_types,
|
|
813
|
+
ensemble_hyperparameters=ensemble_hyperparameters,
|
|
748
814
|
time_limit=time_left,
|
|
749
815
|
verbosity=verbosity,
|
|
750
816
|
num_val_windows=num_val_windows,
|
|
@@ -763,23 +829,152 @@ class TimeSeriesPredictor:
|
|
|
763
829
|
self.save()
|
|
764
830
|
return self
|
|
765
831
|
|
|
832
|
+
def _recommend_num_val_windows_auto(
|
|
833
|
+
self,
|
|
834
|
+
num_items: int,
|
|
835
|
+
median_timeseries_length: int,
|
|
836
|
+
val_step_size: int,
|
|
837
|
+
ensemble_hyperparameters: dict[str, Any] | list[dict[str, Any]] | None = None,
|
|
838
|
+
) -> tuple[int, ...]:
|
|
839
|
+
if num_items < 20:
|
|
840
|
+
recommended_windows = 5
|
|
841
|
+
elif num_items < 100:
|
|
842
|
+
recommended_windows = 3
|
|
843
|
+
else:
|
|
844
|
+
recommended_windows = 2
|
|
845
|
+
|
|
846
|
+
min_train_length = max(2 * self.prediction_length + 1, 10)
|
|
847
|
+
max_windows = int((median_timeseries_length - min_train_length - self.prediction_length) // val_step_size + 1)
|
|
848
|
+
total_windows = min(recommended_windows, max(1, max_windows))
|
|
849
|
+
|
|
850
|
+
num_layers = len(ensemble_hyperparameters) if isinstance(ensemble_hyperparameters, list) else 1
|
|
851
|
+
if total_windows >= num_layers:
|
|
852
|
+
# Distribute windows: most to first layer, 1 to each remaining layer
|
|
853
|
+
return (total_windows - num_layers + 1,) + (1,) * (num_layers - 1)
|
|
854
|
+
else:
|
|
855
|
+
# Insufficient windows: return tuple matching num_layers, will be reduced downstream
|
|
856
|
+
return (1,) * num_layers
|
|
857
|
+
|
|
858
|
+
def _recommend_refit_every_n_windows_auto(self, num_val_windows: tuple[int, ...]) -> int:
|
|
859
|
+
# Simple mapping for total_windows -> refit_ever_n_windows: 1 -> 1, 2 -> 1, 3 -> 2, 4 -> 2, 5 -> 2
|
|
860
|
+
total_windows = sum(num_val_windows)
|
|
861
|
+
return int(round(total_windows**0.5))
|
|
862
|
+
|
|
863
|
+
def _validate_and_normalize_validation_and_ensemble_inputs(
|
|
864
|
+
self,
|
|
865
|
+
num_val_windows: int | tuple[int, ...],
|
|
866
|
+
ensemble_hyperparameters: dict[str, Any] | list[dict[str, Any]] | None,
|
|
867
|
+
val_step_size: int,
|
|
868
|
+
median_timeseries_length: float,
|
|
869
|
+
tuning_data_provided: bool,
|
|
870
|
+
) -> tuple[tuple[int, ...], list[dict[str, Any]] | None]:
|
|
871
|
+
"""Validate and normalize num_val_windows and ensemble_hyperparameters for multi-layer ensembling."""
|
|
872
|
+
if ensemble_hyperparameters is not None and isinstance(ensemble_hyperparameters, dict):
|
|
873
|
+
ensemble_hyperparameters = [ensemble_hyperparameters]
|
|
874
|
+
|
|
875
|
+
num_val_windows = self._normalize_num_val_windows_input(num_val_windows, tuning_data_provided)
|
|
876
|
+
num_val_windows = self._reduce_num_val_windows_if_necessary(
|
|
877
|
+
num_val_windows, val_step_size, median_timeseries_length, tuning_data_provided
|
|
878
|
+
)
|
|
879
|
+
|
|
880
|
+
if ensemble_hyperparameters is not None and len(num_val_windows) < len(ensemble_hyperparameters):
|
|
881
|
+
logger.warning(
|
|
882
|
+
f"Time series too short: reducing ensemble layers from {len(ensemble_hyperparameters)} to "
|
|
883
|
+
f"{len(num_val_windows)}. Only the first {len(num_val_windows)} ensemble layer(s) will be trained."
|
|
884
|
+
)
|
|
885
|
+
ensemble_hyperparameters = ensemble_hyperparameters[: len(num_val_windows)]
|
|
886
|
+
|
|
887
|
+
return num_val_windows, ensemble_hyperparameters
|
|
888
|
+
|
|
889
|
+
def _normalize_num_val_windows_input(
|
|
890
|
+
self,
|
|
891
|
+
num_val_windows: int | tuple[int, ...],
|
|
892
|
+
tuning_data_provided: bool,
|
|
893
|
+
) -> tuple[int, ...]:
|
|
894
|
+
if isinstance(num_val_windows, int):
|
|
895
|
+
num_val_windows = (num_val_windows,)
|
|
896
|
+
if not isinstance(num_val_windows, tuple):
|
|
897
|
+
raise TypeError(f"num_val_windows must be int or tuple[int, ...], got {type(num_val_windows)}")
|
|
898
|
+
if len(num_val_windows) == 0:
|
|
899
|
+
raise ValueError("num_val_windows tuple cannot be empty")
|
|
900
|
+
if tuning_data_provided:
|
|
901
|
+
num_val_windows = num_val_windows[:-1] + (1,)
|
|
902
|
+
logger.warning(
|
|
903
|
+
f"\tTuning data is provided. Setting num_val_windows = {num_val_windows}. Validation scores will"
|
|
904
|
+
" be computed on a single window of tuning_data."
|
|
905
|
+
)
|
|
906
|
+
if not all(isinstance(n, int) and n > 0 for n in num_val_windows):
|
|
907
|
+
raise ValueError("All elements of num_val_windows must be positive integers.")
|
|
908
|
+
return num_val_windows
|
|
909
|
+
|
|
910
|
+
def _reduce_num_val_windows_if_necessary(
|
|
911
|
+
self,
|
|
912
|
+
num_val_windows: tuple[int, ...],
|
|
913
|
+
val_step_size: int,
|
|
914
|
+
median_time_series_length: float,
|
|
915
|
+
tuning_data_provided: bool,
|
|
916
|
+
) -> tuple[int, ...]:
|
|
917
|
+
"""Adjust num_val_windows based on the length of time series in train_data.
|
|
918
|
+
|
|
919
|
+
Chooses num_val_windows such that TS with median length is long enough to perform num_val_windows validations
|
|
920
|
+
(at least 1, at most `original_num_val_windows`).
|
|
921
|
+
|
|
922
|
+
In other words, find largest `num_val_windows` that satisfies
|
|
923
|
+
median_length >= min_train_length + prediction_length + (num_val_windows - 1) * val_step_size
|
|
924
|
+
|
|
925
|
+
If tuning_data is provided, the last element of `num_val_windows` is ignored when computing the number of
|
|
926
|
+
requested validation windows.
|
|
927
|
+
"""
|
|
928
|
+
num_val_windows_for_median_ts = int(
|
|
929
|
+
(median_time_series_length - self._min_train_length - self.prediction_length) // val_step_size + 1
|
|
930
|
+
)
|
|
931
|
+
max_allowed = max(1, num_val_windows_for_median_ts)
|
|
932
|
+
total_requested = sum(num_val_windows) if not tuning_data_provided else sum(num_val_windows[:-1])
|
|
933
|
+
|
|
934
|
+
if max_allowed >= total_requested:
|
|
935
|
+
return num_val_windows
|
|
936
|
+
|
|
937
|
+
logger.warning(
|
|
938
|
+
f"Time series in train_data are too short for chosen num_val_windows={num_val_windows}. "
|
|
939
|
+
f"Reducing num_val_windows to {max_allowed} total windows."
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
result = list(num_val_windows)
|
|
943
|
+
|
|
944
|
+
# Starting from the last group of windows, reduce number of windows in each group by 1,
|
|
945
|
+
# until sum(num_val_windows) <= max_allowed is satisfied.
|
|
946
|
+
for i in range(len(result) - 1, -1, -1):
|
|
947
|
+
while result[i] > 1 and sum(result) > max_allowed:
|
|
948
|
+
result[i] -= 1
|
|
949
|
+
if sum(result) <= max_allowed:
|
|
950
|
+
break
|
|
951
|
+
|
|
952
|
+
# It is possible that the above for loop reduced the number of windows in each group to 1
|
|
953
|
+
# (i.e. result = [1] * len(num_val_windows)), but still sum(result) > max_allowed. In this
|
|
954
|
+
# case we set result = [1] * max_allowed
|
|
955
|
+
if sum(result) > max_allowed:
|
|
956
|
+
result = [1] * max_allowed
|
|
957
|
+
|
|
958
|
+
return tuple(result)
|
|
959
|
+
|
|
766
960
|
def model_names(self) -> list[str]:
|
|
767
961
|
"""Returns the list of model names trained by this predictor object."""
|
|
962
|
+
self._assert_is_fit("model_names")
|
|
768
963
|
return self._trainer.get_model_names()
|
|
769
964
|
|
|
770
965
|
def predict(
|
|
771
966
|
self,
|
|
772
|
-
data:
|
|
773
|
-
known_covariates:
|
|
774
|
-
model:
|
|
967
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
|
|
968
|
+
known_covariates: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
|
|
969
|
+
model: str | None = None,
|
|
775
970
|
use_cache: bool = True,
|
|
776
|
-
random_seed:
|
|
971
|
+
random_seed: int | None = 123,
|
|
777
972
|
) -> TimeSeriesDataFrame:
|
|
778
973
|
"""Return quantile and mean forecasts for the given dataset, starting from the end of each time series.
|
|
779
974
|
|
|
780
975
|
Parameters
|
|
781
976
|
----------
|
|
782
|
-
data :
|
|
977
|
+
data : TimeSeriesDataFrame | pd.DataFrame | Path | str
|
|
783
978
|
Historical time series data for which the forecast needs to be made.
|
|
784
979
|
|
|
785
980
|
The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
|
|
@@ -787,7 +982,7 @@ class TimeSeriesPredictor:
|
|
|
787
982
|
|
|
788
983
|
If provided data is a ``pandas.DataFrame``, AutoGluon will attempt to convert it to a ``TimeSeriesDataFrame``.
|
|
789
984
|
If a ``str`` or a ``Path`` is provided, AutoGluon will attempt to load this file.
|
|
790
|
-
known_covariates :
|
|
985
|
+
known_covariates : TimeSeriesDataFrame | pd.DataFrame | Path | str, optional
|
|
791
986
|
If ``known_covariates_names`` were specified when creating the predictor, it is necessary to provide the
|
|
792
987
|
values of the known covariates for each time series during the forecast horizon. Specifically:
|
|
793
988
|
|
|
@@ -837,6 +1032,7 @@ class TimeSeriesPredictor:
|
|
|
837
1032
|
B 2020-03-04 17.1
|
|
838
1033
|
2020-03-05 8.3
|
|
839
1034
|
"""
|
|
1035
|
+
self._assert_is_fit("predict")
|
|
840
1036
|
# Save original item_id order to return predictions in the same order as input data
|
|
841
1037
|
data = self._to_data_frame(data)
|
|
842
1038
|
original_item_id_order = data.item_ids
|
|
@@ -852,12 +1048,207 @@ class TimeSeriesPredictor:
|
|
|
852
1048
|
)
|
|
853
1049
|
return cast(TimeSeriesDataFrame, predictions.reindex(original_item_id_order, level=TimeSeriesDataFrame.ITEMID))
|
|
854
1050
|
|
|
1051
|
+
@overload
|
|
1052
|
+
def backtest_predictions(
|
|
1053
|
+
self,
|
|
1054
|
+
data: TimeSeriesDataFrame | None = None,
|
|
1055
|
+
*,
|
|
1056
|
+
model: str | None = None,
|
|
1057
|
+
num_val_windows: int | None = None,
|
|
1058
|
+
val_step_size: int | None = None,
|
|
1059
|
+
use_cache: bool = True,
|
|
1060
|
+
) -> list[TimeSeriesDataFrame]: ...
|
|
1061
|
+
|
|
1062
|
+
@overload
|
|
1063
|
+
def backtest_predictions(
|
|
1064
|
+
self,
|
|
1065
|
+
data: TimeSeriesDataFrame | None = None,
|
|
1066
|
+
*,
|
|
1067
|
+
model: list[str],
|
|
1068
|
+
num_val_windows: int | None = None,
|
|
1069
|
+
val_step_size: int | None = None,
|
|
1070
|
+
use_cache: bool = True,
|
|
1071
|
+
) -> dict[str, list[TimeSeriesDataFrame]]: ...
|
|
1072
|
+
|
|
1073
|
+
def backtest_predictions(
|
|
1074
|
+
self,
|
|
1075
|
+
data: TimeSeriesDataFrame | None = None,
|
|
1076
|
+
*,
|
|
1077
|
+
model: str | list[str] | None = None,
|
|
1078
|
+
num_val_windows: int | None = None,
|
|
1079
|
+
val_step_size: int | None = None,
|
|
1080
|
+
use_cache: bool = True,
|
|
1081
|
+
) -> list[TimeSeriesDataFrame] | dict[str, list[TimeSeriesDataFrame]]:
|
|
1082
|
+
"""Return predictions for multiple validation windows.
|
|
1083
|
+
|
|
1084
|
+
When ``data=None``, returns the predictions that were saved during training. Otherwise, generates new
|
|
1085
|
+
predictions by splitting ``data`` into multiple windows using an expanding window strategy.
|
|
1086
|
+
|
|
1087
|
+
The corresponding target values for each window can be obtained using
|
|
1088
|
+
:meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_targets`.
|
|
1089
|
+
|
|
1090
|
+
Parameters
|
|
1091
|
+
----------
|
|
1092
|
+
data : TimeSeriesDataFrame, optional
|
|
1093
|
+
Time series data to generate predictions for. If ``None``, returns the predictions that were saved
|
|
1094
|
+
during training on ``train_data``.
|
|
1095
|
+
|
|
1096
|
+
If provided, all time series in ``data`` must have length at least
|
|
1097
|
+
``prediction_length + (num_val_windows - 1) * val_step_size + 1``.
|
|
1098
|
+
|
|
1099
|
+
The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
|
|
1100
|
+
the predictor.
|
|
1101
|
+
model : str, list[str], or None, default = None
|
|
1102
|
+
Name of the model(s) to generate predictions with. By default, the best model during training
|
|
1103
|
+
(with highest validation score) will be used.
|
|
1104
|
+
|
|
1105
|
+
- If ``str``: Returns predictions for a single model as a list.
|
|
1106
|
+
- If ``list[str]``: Returns predictions for multiple models as a dict mapping model names to lists.
|
|
1107
|
+
- If ``None``: Uses the best model.
|
|
1108
|
+
num_val_windows : int, optional
|
|
1109
|
+
Number of validation windows to generate. If ``None``, uses the ``num_val_windows`` value from training
|
|
1110
|
+
configuration when ``data=None``, otherwise defaults to 1.
|
|
1111
|
+
|
|
1112
|
+
For example, with ``prediction_length=2``, ``num_val_windows=3``, and ``val_step_size=1``, the validation
|
|
1113
|
+
windows are::
|
|
1114
|
+
|
|
1115
|
+
|-------------------|
|
|
1116
|
+
| x x x x x y y - - |
|
|
1117
|
+
| x x x x x x y y - |
|
|
1118
|
+
| x x x x x x x y y |
|
|
1119
|
+
|
|
1120
|
+
where ``x`` denotes training time steps and ``y`` denotes validation time steps for each window.
|
|
1121
|
+
val_step_size : int, optional
|
|
1122
|
+
Number of time steps between the start of consecutive validation windows. If ``None``, defaults to
|
|
1123
|
+
``prediction_length``.
|
|
1124
|
+
use_cache : bool, default = True
|
|
1125
|
+
If True, will attempt to use cached predictions. If False, cached predictions will be ignored.
|
|
1126
|
+
This argument is ignored if ``cache_predictions`` was set to False when creating the ``TimeSeriesPredictor``.
|
|
1127
|
+
|
|
1128
|
+
Returns
|
|
1129
|
+
-------
|
|
1130
|
+
list[TimeSeriesDataFrame] or dict[str, list[TimeSeriesDataFrame]]
|
|
1131
|
+
Predictions for each validation window.
|
|
1132
|
+
|
|
1133
|
+
- If ``model`` is a ``str`` or ``None``: Returns a list of length ``num_val_windows``, where each element
|
|
1134
|
+
contains the predictions for one validation window.
|
|
1135
|
+
- If ``model`` is a ``list[str]``: Returns a dict mapping each model name to a list of predictions for
|
|
1136
|
+
each validation window.
|
|
1137
|
+
|
|
1138
|
+
Examples
|
|
1139
|
+
--------
|
|
1140
|
+
Make predictions on new data with the best model
|
|
1141
|
+
|
|
1142
|
+
>>> predictor.backtest_predictions(test_data, num_val_windows=2)
|
|
1143
|
+
|
|
1144
|
+
Load validation predictions for all models that were saved during training
|
|
1145
|
+
|
|
1146
|
+
>>> predictor.backtest_predictions(model=predictor.model_names())
|
|
1147
|
+
|
|
1148
|
+
See Also
|
|
1149
|
+
--------
|
|
1150
|
+
backtest_targets
|
|
1151
|
+
Return target values aligned with predictions.
|
|
1152
|
+
evaluate
|
|
1153
|
+
Evaluate forecast accuracy on a hold-out set.
|
|
1154
|
+
predict
|
|
1155
|
+
Generate forecasts for future time steps.
|
|
1156
|
+
"""
|
|
1157
|
+
self._assert_is_fit("backtest_predictions")
|
|
1158
|
+
if data is not None:
|
|
1159
|
+
data = self._check_and_prepare_data_frame(data)
|
|
1160
|
+
|
|
1161
|
+
if model is None:
|
|
1162
|
+
model_names = [self.model_best]
|
|
1163
|
+
elif isinstance(model, str):
|
|
1164
|
+
model_names = [model]
|
|
1165
|
+
else:
|
|
1166
|
+
model_names = model
|
|
1167
|
+
|
|
1168
|
+
result = self._learner.backtest_predictions(
|
|
1169
|
+
data=data,
|
|
1170
|
+
model_names=model_names,
|
|
1171
|
+
num_val_windows=num_val_windows,
|
|
1172
|
+
val_step_size=val_step_size,
|
|
1173
|
+
use_cache=use_cache,
|
|
1174
|
+
)
|
|
1175
|
+
|
|
1176
|
+
if isinstance(model, list):
|
|
1177
|
+
return result
|
|
1178
|
+
else:
|
|
1179
|
+
return result[model_names[0]]
|
|
1180
|
+
|
|
1181
|
+
def backtest_targets(
|
|
1182
|
+
self,
|
|
1183
|
+
data: TimeSeriesDataFrame | None = None,
|
|
1184
|
+
*,
|
|
1185
|
+
num_val_windows: int | None = None,
|
|
1186
|
+
val_step_size: int | None = None,
|
|
1187
|
+
) -> list[TimeSeriesDataFrame]:
|
|
1188
|
+
"""Return target values for each validation window.
|
|
1189
|
+
|
|
1190
|
+
Returns the actual target values corresponding to each validation window used in
|
|
1191
|
+
:meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`. The returned targets are aligned
|
|
1192
|
+
with the predictions, making it easy to compute custom evaluation metrics or analyze forecast errors.
|
|
1193
|
+
|
|
1194
|
+
Parameters
|
|
1195
|
+
----------
|
|
1196
|
+
data : TimeSeriesDataFrame, optional
|
|
1197
|
+
Time series data to extract targets from. If ``None``, returns the targets from the validation windows
|
|
1198
|
+
used during training.
|
|
1199
|
+
|
|
1200
|
+
If provided, all time series in ``data`` must have length at least
|
|
1201
|
+
``prediction_length + (num_val_windows - 1) * val_step_size + 1``.
|
|
1202
|
+
|
|
1203
|
+
The names and dtypes of columns and static features in ``data`` must match the ``train_data`` used to train
|
|
1204
|
+
the predictor.
|
|
1205
|
+
num_val_windows : int, optional
|
|
1206
|
+
Number of validation windows to extract targets for. If ``None``, uses the ``num_val_windows`` value from
|
|
1207
|
+
training configuration when ``data=None``, otherwise defaults to 1.
|
|
1208
|
+
|
|
1209
|
+
This should match the ``num_val_windows`` argument passed to
|
|
1210
|
+
:meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`.
|
|
1211
|
+
val_step_size : int, optional
|
|
1212
|
+
Number of time steps between the start of consecutive validation windows. If ``None``, defaults to
|
|
1213
|
+
``prediction_length``.
|
|
1214
|
+
|
|
1215
|
+
This should match the ``val_step_size`` argument passed to
|
|
1216
|
+
:meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`.
|
|
1217
|
+
|
|
1218
|
+
Returns
|
|
1219
|
+
-------
|
|
1220
|
+
list[TimeSeriesDataFrame]
|
|
1221
|
+
Target values for each validation window. Returns a list of length ``num_val_windows``,
|
|
1222
|
+
where each element contains the full time series data for one validation window.
|
|
1223
|
+
Each dataframe includes both historical context and the last ``prediction_length`` time steps
|
|
1224
|
+
that represent the target values to compare against predictions.
|
|
1225
|
+
|
|
1226
|
+
The returned targets are aligned with the output of
|
|
1227
|
+
:meth:`~autogluon.timeseries.TimeSeriesPredictor.backtest_predictions`, so ``targets[i]`` corresponds
|
|
1228
|
+
to ``predictions[i]`` for the i-th validation window.
|
|
1229
|
+
|
|
1230
|
+
See Also
|
|
1231
|
+
--------
|
|
1232
|
+
backtest_predictions
|
|
1233
|
+
Return predictions for multiple validation windows.
|
|
1234
|
+
evaluate
|
|
1235
|
+
Evaluate forecast accuracy on a hold-out set.
|
|
1236
|
+
"""
|
|
1237
|
+
self._assert_is_fit("backtest_targets")
|
|
1238
|
+
if data is not None:
|
|
1239
|
+
data = self._check_and_prepare_data_frame(data)
|
|
1240
|
+
return self._learner.backtest_targets(
|
|
1241
|
+
data=data,
|
|
1242
|
+
num_val_windows=num_val_windows,
|
|
1243
|
+
val_step_size=val_step_size,
|
|
1244
|
+
)
|
|
1245
|
+
|
|
855
1246
|
def evaluate(
|
|
856
1247
|
self,
|
|
857
|
-
data:
|
|
858
|
-
model:
|
|
859
|
-
metrics:
|
|
860
|
-
cutoff:
|
|
1248
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
|
|
1249
|
+
model: str | None = None,
|
|
1250
|
+
metrics: str | TimeSeriesScorer | list[str | TimeSeriesScorer] | None = None,
|
|
1251
|
+
cutoff: int | None = None,
|
|
861
1252
|
display: bool = False,
|
|
862
1253
|
use_cache: bool = True,
|
|
863
1254
|
) -> dict[str, float]:
|
|
@@ -874,7 +1265,7 @@ class TimeSeriesPredictor:
|
|
|
874
1265
|
|
|
875
1266
|
Parameters
|
|
876
1267
|
----------
|
|
877
|
-
data :
|
|
1268
|
+
data : TimeSeriesDataFrame | pd.DataFrame | Path | str
|
|
878
1269
|
The data to evaluate the best model on. If a ``cutoff`` is not provided, the last ``prediction_length``
|
|
879
1270
|
time steps of each time series in ``data`` will be held out for prediction and forecast accuracy will
|
|
880
1271
|
be calculated on these time steps. When a ``cutoff`` is provided, the ``-cutoff``-th to the
|
|
@@ -891,7 +1282,7 @@ class TimeSeriesPredictor:
|
|
|
891
1282
|
model : str, optional
|
|
892
1283
|
Name of the model that you would like to evaluate. By default, the best model during training
|
|
893
1284
|
(with highest validation score) will be used.
|
|
894
|
-
metrics : str, TimeSeriesScorer or list[
|
|
1285
|
+
metrics : str, TimeSeriesScorer or list[str | TimeSeriesScorer], optional
|
|
895
1286
|
Metric or a list of metrics to compute scores with. Defaults to ``self.eval_metric``. Supports both
|
|
896
1287
|
metric names as strings and custom metrics based on TimeSeriesScorer.
|
|
897
1288
|
cutoff : int, optional
|
|
@@ -912,7 +1303,7 @@ class TimeSeriesPredictor:
|
|
|
912
1303
|
will have their signs flipped to obey this convention. For example, negative MAPE values will be reported.
|
|
913
1304
|
To get the ``eval_metric`` score, do ``output[predictor.eval_metric.name]``.
|
|
914
1305
|
"""
|
|
915
|
-
|
|
1306
|
+
self._assert_is_fit("evaluate")
|
|
916
1307
|
data = self._check_and_prepare_data_frame(data)
|
|
917
1308
|
data = self._check_and_prepare_data_frame_for_evaluation(data, cutoff=cutoff)
|
|
918
1309
|
|
|
@@ -924,15 +1315,15 @@ class TimeSeriesPredictor:
|
|
|
924
1315
|
|
|
925
1316
|
def feature_importance(
|
|
926
1317
|
self,
|
|
927
|
-
data:
|
|
928
|
-
model:
|
|
929
|
-
metric:
|
|
930
|
-
features:
|
|
931
|
-
time_limit:
|
|
1318
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
|
|
1319
|
+
model: str | None = None,
|
|
1320
|
+
metric: str | TimeSeriesScorer | None = None,
|
|
1321
|
+
features: list[str] | None = None,
|
|
1322
|
+
time_limit: float | None = None,
|
|
932
1323
|
method: Literal["naive", "permutation"] = "permutation",
|
|
933
1324
|
subsample_size: int = 50,
|
|
934
|
-
num_iterations:
|
|
935
|
-
random_seed:
|
|
1325
|
+
num_iterations: int | None = None,
|
|
1326
|
+
random_seed: int | None = 123,
|
|
936
1327
|
relative_scores: bool = False,
|
|
937
1328
|
include_confidence_band: bool = True,
|
|
938
1329
|
confidence_level: float = 0.99,
|
|
@@ -1029,6 +1420,7 @@ class TimeSeriesPredictor:
|
|
|
1029
1420
|
'importance': The estimated feature importance score.
|
|
1030
1421
|
'stddev': The standard deviation of the feature importance score. If NaN, then not enough ``num_iterations`` were used.
|
|
1031
1422
|
"""
|
|
1423
|
+
self._assert_is_fit("feature_importance")
|
|
1032
1424
|
if data is not None:
|
|
1033
1425
|
data = self._check_and_prepare_data_frame(data)
|
|
1034
1426
|
data = self._check_and_prepare_data_frame_for_evaluation(data)
|
|
@@ -1047,7 +1439,7 @@ class TimeSeriesPredictor:
|
|
|
1047
1439
|
include_confidence_band=include_confidence_band,
|
|
1048
1440
|
confidence_level=confidence_level,
|
|
1049
1441
|
)
|
|
1050
|
-
return fi_df
|
|
1442
|
+
return fi_df.sort_values("importance", ascending=False)
|
|
1051
1443
|
|
|
1052
1444
|
@classmethod
|
|
1053
1445
|
def _load_version_file(cls, path: str) -> str:
|
|
@@ -1075,7 +1467,7 @@ class TimeSeriesPredictor:
|
|
|
1075
1467
|
return version
|
|
1076
1468
|
|
|
1077
1469
|
@classmethod
|
|
1078
|
-
def load(cls, path:
|
|
1470
|
+
def load(cls, path: str | Path, require_version_match: bool = True) -> "TimeSeriesPredictor":
|
|
1079
1471
|
"""Load an existing ``TimeSeriesPredictor`` from given ``path``.
|
|
1080
1472
|
|
|
1081
1473
|
.. warning::
|
|
@@ -1159,15 +1551,14 @@ class TimeSeriesPredictor:
|
|
|
1159
1551
|
@property
|
|
1160
1552
|
def model_best(self) -> str:
|
|
1161
1553
|
"""Returns the name of the best model from trainer."""
|
|
1554
|
+
self._assert_is_fit("model_best")
|
|
1162
1555
|
if self._trainer.model_best is not None:
|
|
1163
1556
|
models = self._trainer.get_model_names()
|
|
1164
1557
|
if self._trainer.model_best in models:
|
|
1165
1558
|
return self._trainer.model_best
|
|
1166
1559
|
return self._trainer.get_model_best()
|
|
1167
1560
|
|
|
1168
|
-
def persist(
|
|
1169
|
-
self, models: Union[Literal["all", "best"], list[str]] = "best", with_ancestors: bool = True
|
|
1170
|
-
) -> list[str]:
|
|
1561
|
+
def persist(self, models: Literal["all", "best"] | list[str] = "best", with_ancestors: bool = True) -> list[str]:
|
|
1171
1562
|
"""Persist models in memory for reduced inference latency. This is particularly important if the models are being used for online
|
|
1172
1563
|
inference where low latency is critical. If models are not persisted in memory, they are loaded from disk every time they are
|
|
1173
1564
|
asked to make predictions. This is especially cumbersome for large deep learning based models which have to be loaded into
|
|
@@ -1190,6 +1581,7 @@ class TimeSeriesPredictor:
|
|
|
1190
1581
|
list_of_models : list[str]
|
|
1191
1582
|
List of persisted model names.
|
|
1192
1583
|
"""
|
|
1584
|
+
self._assert_is_fit("persist")
|
|
1193
1585
|
return self._learner.persist_trainer(models=models, with_ancestors=with_ancestors)
|
|
1194
1586
|
|
|
1195
1587
|
def unpersist(self) -> list[str]:
|
|
@@ -1208,10 +1600,10 @@ class TimeSeriesPredictor:
|
|
|
1208
1600
|
|
|
1209
1601
|
def leaderboard(
|
|
1210
1602
|
self,
|
|
1211
|
-
data:
|
|
1212
|
-
cutoff:
|
|
1603
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str | None = None,
|
|
1604
|
+
cutoff: int | None = None,
|
|
1213
1605
|
extra_info: bool = False,
|
|
1214
|
-
extra_metrics:
|
|
1606
|
+
extra_metrics: list[str | TimeSeriesScorer] | None = None,
|
|
1215
1607
|
display: bool = False,
|
|
1216
1608
|
use_cache: bool = True,
|
|
1217
1609
|
**kwargs,
|
|
@@ -1236,7 +1628,7 @@ class TimeSeriesPredictor:
|
|
|
1236
1628
|
|
|
1237
1629
|
Parameters
|
|
1238
1630
|
----------
|
|
1239
|
-
data :
|
|
1631
|
+
data : TimeSeriesDataFrame | pd.DataFrame | Path | str, optional
|
|
1240
1632
|
dataset used for additional evaluation. Must include both historical and future data (i.e., length of all
|
|
1241
1633
|
time series in ``data`` must be at least ``prediction_length + 1``, if ``cutoff`` is not provided,
|
|
1242
1634
|
``-cutoff + 1`` otherwise).
|
|
@@ -1255,7 +1647,7 @@ class TimeSeriesPredictor:
|
|
|
1255
1647
|
If True, the leaderboard will contain an additional column ``hyperparameters`` with the hyperparameters used
|
|
1256
1648
|
by each model during training. An empty dictionary ``{}`` means that the model was trained with default
|
|
1257
1649
|
hyperparameters.
|
|
1258
|
-
extra_metrics : list[
|
|
1650
|
+
extra_metrics : list[str | TimeSeriesScorer], optional
|
|
1259
1651
|
A list of metrics to calculate scores for and include in the output DataFrame.
|
|
1260
1652
|
|
|
1261
1653
|
Only valid when ``data`` is specified. The scores refer to the scores on ``data`` (same data as used to
|
|
@@ -1277,6 +1669,7 @@ class TimeSeriesPredictor:
|
|
|
1277
1669
|
The leaderboard containing information on all models and in order of best model to worst in terms of
|
|
1278
1670
|
test performance.
|
|
1279
1671
|
"""
|
|
1672
|
+
self._assert_is_fit("leaderboard")
|
|
1280
1673
|
if "silent" in kwargs:
|
|
1281
1674
|
# keep `silent` logic for backwards compatibility
|
|
1282
1675
|
assert isinstance(kwargs["silent"], bool)
|
|
@@ -1301,12 +1694,12 @@ class TimeSeriesPredictor:
|
|
|
1301
1694
|
print(leaderboard)
|
|
1302
1695
|
return leaderboard
|
|
1303
1696
|
|
|
1304
|
-
def make_future_data_frame(self, data:
|
|
1697
|
+
def make_future_data_frame(self, data: TimeSeriesDataFrame | pd.DataFrame | Path | str) -> pd.DataFrame:
|
|
1305
1698
|
"""Generate a dataframe with the ``item_id`` and ``timestamp`` values corresponding to the forecast horizon.
|
|
1306
1699
|
|
|
1307
1700
|
Parameters
|
|
1308
1701
|
----------
|
|
1309
|
-
data :
|
|
1702
|
+
data : TimeSeriesDataFrame | pd.DataFrame | Path | str
|
|
1310
1703
|
Historical time series data.
|
|
1311
1704
|
|
|
1312
1705
|
Returns
|
|
@@ -1354,6 +1747,7 @@ class TimeSeriesPredictor:
|
|
|
1354
1747
|
Dict containing various detailed information. We do not recommend directly printing this dict as it may
|
|
1355
1748
|
be very large.
|
|
1356
1749
|
"""
|
|
1750
|
+
self._assert_is_fit("fit_summary")
|
|
1357
1751
|
# TODO: HPO-specific information currently not reported in fit_summary
|
|
1358
1752
|
# TODO: Revisit after ray tune integration
|
|
1359
1753
|
|
|
@@ -1414,6 +1808,7 @@ class TimeSeriesPredictor:
|
|
|
1414
1808
|
``predictor.predict(data)`` is called will be the refit_full version instead of the original version of the
|
|
1415
1809
|
model. Has no effect if ``model`` is not the best model.
|
|
1416
1810
|
"""
|
|
1811
|
+
self._assert_is_fit("refit_full")
|
|
1417
1812
|
logger.warning(
|
|
1418
1813
|
"\tWARNING: refit_full functionality for TimeSeriesPredictor is experimental "
|
|
1419
1814
|
"and is not yet supported by all models."
|
|
@@ -1466,7 +1861,7 @@ class TimeSeriesPredictor:
|
|
|
1466
1861
|
trainer = self._trainer
|
|
1467
1862
|
train_data = trainer.load_train_data()
|
|
1468
1863
|
val_data = trainer.load_val_data()
|
|
1469
|
-
base_model_names = trainer.get_model_names(
|
|
1864
|
+
base_model_names = trainer.get_model_names(layer=0)
|
|
1470
1865
|
pred_proba_dict_val: dict[str, list[TimeSeriesDataFrame]] = {
|
|
1471
1866
|
model_name: trainer._get_model_oof_predictions(model_name)
|
|
1472
1867
|
for model_name in base_model_names
|
|
@@ -1502,27 +1897,27 @@ class TimeSeriesPredictor:
|
|
|
1502
1897
|
|
|
1503
1898
|
def plot(
|
|
1504
1899
|
self,
|
|
1505
|
-
data:
|
|
1506
|
-
predictions:
|
|
1507
|
-
quantile_levels:
|
|
1508
|
-
item_ids:
|
|
1900
|
+
data: TimeSeriesDataFrame | pd.DataFrame | Path | str,
|
|
1901
|
+
predictions: TimeSeriesDataFrame | None = None,
|
|
1902
|
+
quantile_levels: list[float] | None = None,
|
|
1903
|
+
item_ids: list[str | int] | None = None,
|
|
1509
1904
|
max_num_item_ids: int = 8,
|
|
1510
|
-
max_history_length:
|
|
1511
|
-
point_forecast_column:
|
|
1512
|
-
matplotlib_rc_params:
|
|
1905
|
+
max_history_length: int | None = None,
|
|
1906
|
+
point_forecast_column: str | None = None,
|
|
1907
|
+
matplotlib_rc_params: dict | None = None,
|
|
1513
1908
|
):
|
|
1514
1909
|
"""Plot historical time series values and the forecasts.
|
|
1515
1910
|
|
|
1516
1911
|
Parameters
|
|
1517
1912
|
----------
|
|
1518
|
-
data :
|
|
1913
|
+
data : TimeSeriesDataFrame | pd.DataFrame | Path | str
|
|
1519
1914
|
Observed time series data.
|
|
1520
1915
|
predictions : TimeSeriesDataFrame, optional
|
|
1521
1916
|
Predictions generated by calling :meth:`~autogluon.timeseries.TimeSeriesPredictor.predict`.
|
|
1522
1917
|
quantile_levels : list[float], optional
|
|
1523
1918
|
Quantile levels for which to plot the prediction intervals. Defaults to lowest & highest quantile levels
|
|
1524
1919
|
available in ``predictions``.
|
|
1525
|
-
item_ids : list[
|
|
1920
|
+
item_ids : list[str | int], optional
|
|
1526
1921
|
If provided, plots will only be generated for time series with these item IDs. By default (if set to
|
|
1527
1922
|
``None``), item IDs are selected randomly. In either case, plots are generated for at most
|
|
1528
1923
|
``max_num_item_ids`` time series.
|