autogluon.timeseries 1.2.1b20250425__py3-none-any.whl → 1.2.1b20250426__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.
- autogluon/timeseries/learner.py +1 -4
- autogluon/timeseries/metrics/__init__.py +36 -8
- autogluon/timeseries/metrics/abstract.py +77 -7
- autogluon/timeseries/metrics/point.py +136 -47
- autogluon/timeseries/metrics/quantile.py +42 -17
- autogluon/timeseries/models/abstract/abstract_timeseries_model.py +4 -18
- autogluon/timeseries/models/ensemble/greedy.py +8 -7
- autogluon/timeseries/models/presets.py +0 -2
- autogluon/timeseries/predictor.py +35 -27
- autogluon/timeseries/trainer.py +22 -15
- autogluon/timeseries/version.py +1 -1
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/METADATA +5 -5
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/RECORD +20 -20
- /autogluon.timeseries-1.2.1b20250425-py3.9-nspkg.pth → /autogluon.timeseries-1.2.1b20250426-py3.9-nspkg.pth +0 -0
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/LICENSE +0 -0
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/NOTICE +0 -0
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/WHEEL +0 -0
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/namespace_packages.txt +0 -0
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/top_level.txt +0 -0
- {autogluon.timeseries-1.2.1b20250425.dist-info → autogluon.timeseries-1.2.1b20250426.dist-info}/zip-safe +0 -0
autogluon/timeseries/learner.py
CHANGED
@@ -29,15 +29,13 @@ class TimeSeriesLearner(AbstractLearner):
|
|
29
29
|
known_covariates_names: Optional[List[str]] = None,
|
30
30
|
trainer_type: Type[TimeSeriesTrainer] = TimeSeriesTrainer,
|
31
31
|
eval_metric: Union[str, TimeSeriesScorer, None] = None,
|
32
|
-
eval_metric_seasonal_period: Optional[int] = None,
|
33
32
|
prediction_length: int = 1,
|
34
33
|
cache_predictions: bool = True,
|
35
34
|
ensemble_model_type: Optional[Type] = None,
|
36
35
|
**kwargs,
|
37
36
|
):
|
38
37
|
super().__init__(path_context=path_context)
|
39
|
-
self.eval_metric
|
40
|
-
self.eval_metric_seasonal_period = eval_metric_seasonal_period
|
38
|
+
self.eval_metric = check_get_evaluation_metric(eval_metric, prediction_length=prediction_length)
|
41
39
|
self.trainer_type = trainer_type
|
42
40
|
self.target = target
|
43
41
|
self.known_covariates_names = [] if known_covariates_names is None else known_covariates_names
|
@@ -82,7 +80,6 @@ class TimeSeriesLearner(AbstractLearner):
|
|
82
80
|
path=self.model_context,
|
83
81
|
prediction_length=self.prediction_length,
|
84
82
|
eval_metric=self.eval_metric,
|
85
|
-
eval_metric_seasonal_period=self.eval_metric_seasonal_period,
|
86
83
|
target=self.target,
|
87
84
|
quantile_levels=self.quantile_levels,
|
88
85
|
verbosity=kwargs.get("verbosity", 2),
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from pprint import pformat
|
2
|
-
from typing import Type, Union
|
2
|
+
from typing import Any, Dict, Optional, Sequence, Type, Union
|
3
|
+
|
4
|
+
import numpy as np
|
3
5
|
|
4
6
|
from .abstract import TimeSeriesScorer
|
5
7
|
from .point import MAE, MAPE, MASE, MSE, RMSE, RMSLE, RMSSE, SMAPE, WAPE, WCD
|
@@ -22,7 +24,7 @@ __all__ = [
|
|
22
24
|
|
23
25
|
DEFAULT_METRIC_NAME = "WQL"
|
24
26
|
|
25
|
-
AVAILABLE_METRICS = {
|
27
|
+
AVAILABLE_METRICS: Dict[str, Type[TimeSeriesScorer]] = {
|
26
28
|
"MASE": MASE,
|
27
29
|
"MAPE": MAPE,
|
28
30
|
"SMAPE": SMAPE,
|
@@ -42,33 +44,59 @@ DEPRECATED_METRICS = {
|
|
42
44
|
}
|
43
45
|
|
44
46
|
# Experimental metrics that are not yet user facing
|
45
|
-
EXPERIMENTAL_METRICS = {
|
47
|
+
EXPERIMENTAL_METRICS: Dict[str, Type[TimeSeriesScorer]] = {
|
46
48
|
"WCD": WCD,
|
47
49
|
}
|
48
50
|
|
49
51
|
|
50
52
|
def check_get_evaluation_metric(
|
51
|
-
eval_metric: Union[str, TimeSeriesScorer, Type[TimeSeriesScorer], None]
|
53
|
+
eval_metric: Union[str, TimeSeriesScorer, Type[TimeSeriesScorer], None],
|
54
|
+
prediction_length: int,
|
55
|
+
seasonal_period: Optional[int] = None,
|
56
|
+
horizon_weight: Optional[Sequence[float] | np.ndarray] = None,
|
52
57
|
) -> TimeSeriesScorer:
|
58
|
+
"""Factory method for TimeSeriesScorer objects.
|
59
|
+
|
60
|
+
Returns
|
61
|
+
-------
|
62
|
+
scorer :
|
63
|
+
A `TimeSeriesScorer` object based on the provided `eval_metric`.
|
64
|
+
|
65
|
+
`scorer.prediction_length` is always set to the `prediction_length` provided to this method.
|
66
|
+
|
67
|
+
If `seasonal_period` is not `None`, then `scorer.seasonal_period` is set to this value. Otherwise the original
|
68
|
+
value of `seasonal_period` is kept.
|
69
|
+
|
70
|
+
If `horizon_weight` is not `None`, then `scorer.horizon_weight` is set to this value. Otherwise the original
|
71
|
+
value of `horizon_weight` is kept.
|
72
|
+
"""
|
53
73
|
scorer: TimeSeriesScorer
|
74
|
+
metric_kwargs: Dict[str, Any] = dict(
|
75
|
+
prediction_length=prediction_length, seasonal_period=seasonal_period, horizon_weight=horizon_weight
|
76
|
+
)
|
54
77
|
if isinstance(eval_metric, TimeSeriesScorer):
|
55
78
|
scorer = eval_metric
|
79
|
+
scorer.prediction_length = prediction_length
|
80
|
+
if seasonal_period is not None:
|
81
|
+
scorer.seasonal_period = seasonal_period
|
82
|
+
if horizon_weight is not None:
|
83
|
+
scorer.horizon_weight = scorer.check_get_horizon_weight(horizon_weight, prediction_length=prediction_length)
|
56
84
|
elif isinstance(eval_metric, type) and issubclass(eval_metric, TimeSeriesScorer):
|
57
85
|
# e.g., user passed `eval_metric=CustomMetric` instead of `eval_metric=CustomMetric()`
|
58
|
-
scorer = eval_metric()
|
86
|
+
scorer = eval_metric(**metric_kwargs)
|
59
87
|
elif isinstance(eval_metric, str):
|
60
88
|
metric_name = DEPRECATED_METRICS.get(eval_metric, eval_metric).upper()
|
61
89
|
if metric_name in AVAILABLE_METRICS:
|
62
|
-
scorer = AVAILABLE_METRICS[metric_name]()
|
90
|
+
scorer = AVAILABLE_METRICS[metric_name](**metric_kwargs)
|
63
91
|
elif metric_name in EXPERIMENTAL_METRICS:
|
64
|
-
scorer = EXPERIMENTAL_METRICS[metric_name]()
|
92
|
+
scorer = EXPERIMENTAL_METRICS[metric_name](**metric_kwargs)
|
65
93
|
else:
|
66
94
|
raise ValueError(
|
67
95
|
f"Time series metric {eval_metric} not supported. Available metrics are:\n"
|
68
96
|
f"{pformat(sorted(AVAILABLE_METRICS.keys()))}"
|
69
97
|
)
|
70
98
|
elif eval_metric is None:
|
71
|
-
scorer = AVAILABLE_METRICS[DEFAULT_METRIC_NAME]()
|
99
|
+
scorer = AVAILABLE_METRICS[DEFAULT_METRIC_NAME](**metric_kwargs)
|
72
100
|
else:
|
73
101
|
raise ValueError(
|
74
102
|
f"eval_metric must be of type str, TimeSeriesScorer or None "
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
import warnings
|
2
|
+
from typing import Optional, Sequence, Tuple, Union, overload
|
2
3
|
|
3
4
|
import numpy as np
|
5
|
+
import numpy.typing as npt
|
4
6
|
import pandas as pd
|
5
7
|
|
6
8
|
from autogluon.timeseries import TimeSeriesDataFrame
|
@@ -15,6 +17,18 @@ class TimeSeriesScorer:
|
|
15
17
|
|
16
18
|
Follows the design of ``autogluon.core.metrics.Scorer``.
|
17
19
|
|
20
|
+
Parameters
|
21
|
+
----------
|
22
|
+
prediction_length : int, default = 1
|
23
|
+
The length of the forecast horizon. The predictions provided to the `TimeSeriesScorer` are expected to contain
|
24
|
+
a forecast for this many time steps for each time series.
|
25
|
+
seasonal_period : int or None, default = None
|
26
|
+
Seasonal period used to compute some evaluation metrics such as mean absolute scaled error (MASE). Defaults to
|
27
|
+
`None`, in which case the seasonal period is computed based on the data frequency.
|
28
|
+
horizon_weight : Sequence[float], np.ndarray or None, default = None
|
29
|
+
Weight assigned to each time step in the forecast horizon when computing the metric. If provided, the
|
30
|
+
`horizon_weight` will be stored as a numpy array of shape `[1, prediction_length]`.
|
31
|
+
|
18
32
|
Attributes
|
19
33
|
----------
|
20
34
|
greater_is_better_internal : bool, default = False
|
@@ -40,6 +54,18 @@ class TimeSeriesScorer:
|
|
40
54
|
needs_quantile: bool = False
|
41
55
|
equivalent_tabular_regression_metric: Optional[str] = None
|
42
56
|
|
57
|
+
def __init__(
|
58
|
+
self,
|
59
|
+
prediction_length: int = 1,
|
60
|
+
seasonal_period: Optional[int] = None,
|
61
|
+
horizon_weight: Optional[Sequence[float]] = None,
|
62
|
+
):
|
63
|
+
self.prediction_length = int(prediction_length)
|
64
|
+
if self.prediction_length < 1:
|
65
|
+
raise ValueError(f"prediction_length must be >= 1 (received {prediction_length})")
|
66
|
+
self.seasonal_period = seasonal_period
|
67
|
+
self.horizon_weight = self.check_get_horizon_weight(horizon_weight, prediction_length=prediction_length)
|
68
|
+
|
43
69
|
@property
|
44
70
|
def sign(self) -> int:
|
45
71
|
return 1 if self.greater_is_better_internal else -1
|
@@ -66,18 +92,25 @@ class TimeSeriesScorer:
|
|
66
92
|
self,
|
67
93
|
data: TimeSeriesDataFrame,
|
68
94
|
predictions: TimeSeriesDataFrame,
|
69
|
-
prediction_length: int = 1,
|
70
95
|
target: str = "target",
|
71
|
-
seasonal_period: Optional[int] = None,
|
72
96
|
**kwargs,
|
73
97
|
) -> float:
|
74
|
-
seasonal_period = get_seasonality(data.freq) if seasonal_period is None else seasonal_period
|
98
|
+
seasonal_period = get_seasonality(data.freq) if self.seasonal_period is None else self.seasonal_period
|
99
|
+
|
100
|
+
if "prediction_length" in kwargs:
|
101
|
+
warnings.warn(
|
102
|
+
"Passing `prediction_length` to `TimeSeriesScorer.__call__` is deprecated and will be removed in v2.0. "
|
103
|
+
"Please set the `eval_metric.prediction_length` attribute instead.",
|
104
|
+
category=FutureWarning,
|
105
|
+
)
|
106
|
+
self.prediction_length = kwargs["prediction_length"]
|
107
|
+
self.horizon_weight = self.check_get_horizon_weight(self.horizon_weight, self.prediction_length)
|
75
108
|
|
76
|
-
data_past = data.slice_by_timestep(None, -prediction_length)
|
77
|
-
data_future = data.slice_by_timestep(-prediction_length, None)
|
109
|
+
data_past = data.slice_by_timestep(None, -self.prediction_length)
|
110
|
+
data_future = data.slice_by_timestep(-self.prediction_length, None)
|
78
111
|
|
79
112
|
assert not predictions.isna().any().any(), "Predictions contain NaN values."
|
80
|
-
assert (predictions.num_timesteps_per_item() == prediction_length).all()
|
113
|
+
assert (predictions.num_timesteps_per_item() == self.prediction_length).all()
|
81
114
|
assert data_future.index.equals(predictions.index), "Prediction and data indices do not match."
|
82
115
|
|
83
116
|
try:
|
@@ -200,3 +233,40 @@ class TimeSeriesScorer:
|
|
200
233
|
q_pred = pd.DataFrame(predictions[quantile_columns])
|
201
234
|
quantile_levels = np.array(quantile_columns, dtype=float)
|
202
235
|
return y_true, q_pred, quantile_levels
|
236
|
+
|
237
|
+
@overload
|
238
|
+
@staticmethod
|
239
|
+
def check_get_horizon_weight(horizon_weight: None, prediction_length: int) -> None: ...
|
240
|
+
@overload
|
241
|
+
@staticmethod
|
242
|
+
def check_get_horizon_weight(
|
243
|
+
horizon_weight: Sequence[float] | np.ndarray, prediction_length: int
|
244
|
+
) -> npt.NDArray[np.float64]: ...
|
245
|
+
|
246
|
+
@staticmethod
|
247
|
+
def check_get_horizon_weight(
|
248
|
+
horizon_weight: Sequence[float] | np.ndarray | None, prediction_length: int
|
249
|
+
) -> Optional[npt.NDArray[np.float64]]:
|
250
|
+
"""Convert horizon_weight to a non-negative numpy array that sums up to prediction_length.
|
251
|
+
Raises an exception if horizon_weight has an invalid shape or contains invalid values.
|
252
|
+
|
253
|
+
Returns
|
254
|
+
-------
|
255
|
+
horizon_weight:
|
256
|
+
None if the input is None, otherwise a numpy array of shape [1, prediction_length].
|
257
|
+
"""
|
258
|
+
if horizon_weight is None:
|
259
|
+
return None
|
260
|
+
horizon_weight_np = np.ravel(horizon_weight).astype(np.float64)
|
261
|
+
if horizon_weight_np.shape != (prediction_length,):
|
262
|
+
raise ValueError(
|
263
|
+
f"horizon_weight must have length equal to {prediction_length=} (got {len(horizon_weight)=})"
|
264
|
+
)
|
265
|
+
if not (horizon_weight_np >= 0).all():
|
266
|
+
raise ValueError(f"All values in horizon_weight must be >= 0 (got {horizon_weight})")
|
267
|
+
if not horizon_weight_np.sum() > 0:
|
268
|
+
raise ValueError(f"At least some values in horizon_weight must be > 0 (got {horizon_weight})")
|
269
|
+
if not np.isfinite(horizon_weight_np).all():
|
270
|
+
raise ValueError(f"All horizon_weight values must be finite (got {horizon_weight})")
|
271
|
+
horizon_weight_np = horizon_weight_np * prediction_length / horizon_weight_np.sum()
|
272
|
+
return horizon_weight_np.reshape([1, prediction_length])
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import warnings
|
3
|
-
from typing import Optional
|
3
|
+
from typing import Optional, Sequence
|
4
4
|
|
5
5
|
import numpy as np
|
6
6
|
import pandas as pd
|
@@ -38,10 +38,18 @@ class RMSE(TimeSeriesScorer):
|
|
38
38
|
equivalent_tabular_regression_metric = "root_mean_squared_error"
|
39
39
|
|
40
40
|
def compute_metric(
|
41
|
-
self,
|
41
|
+
self,
|
42
|
+
data_future: TimeSeriesDataFrame,
|
43
|
+
predictions: TimeSeriesDataFrame,
|
44
|
+
target: str = "target",
|
45
|
+
**kwargs,
|
42
46
|
) -> float:
|
43
47
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
44
|
-
|
48
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
49
|
+
errors = ((y_true - y_pred) ** 2).reshape([-1, self.prediction_length])
|
50
|
+
if self.horizon_weight is not None:
|
51
|
+
errors *= self.horizon_weight
|
52
|
+
return np.sqrt(self._safemean(errors))
|
45
53
|
|
46
54
|
|
47
55
|
class MSE(TimeSeriesScorer):
|
@@ -69,10 +77,18 @@ class MSE(TimeSeriesScorer):
|
|
69
77
|
equivalent_tabular_regression_metric = "mean_squared_error"
|
70
78
|
|
71
79
|
def compute_metric(
|
72
|
-
self,
|
80
|
+
self,
|
81
|
+
data_future: TimeSeriesDataFrame,
|
82
|
+
predictions: TimeSeriesDataFrame,
|
83
|
+
target: str = "target",
|
84
|
+
**kwargs,
|
73
85
|
) -> float:
|
74
86
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
75
|
-
|
87
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
88
|
+
errors = ((y_true - y_pred) ** 2).reshape([-1, self.prediction_length])
|
89
|
+
if self.horizon_weight is not None:
|
90
|
+
errors *= self.horizon_weight
|
91
|
+
return self._safemean(errors)
|
76
92
|
|
77
93
|
|
78
94
|
class MAE(TimeSeriesScorer):
|
@@ -98,10 +114,18 @@ class MAE(TimeSeriesScorer):
|
|
98
114
|
equivalent_tabular_regression_metric = "mean_absolute_error"
|
99
115
|
|
100
116
|
def compute_metric(
|
101
|
-
self,
|
117
|
+
self,
|
118
|
+
data_future: TimeSeriesDataFrame,
|
119
|
+
predictions: TimeSeriesDataFrame,
|
120
|
+
target: str = "target",
|
121
|
+
**kwargs,
|
102
122
|
) -> float:
|
103
123
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
104
|
-
|
124
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
125
|
+
errors = np.abs(y_true - y_pred).reshape([-1, self.prediction_length])
|
126
|
+
if self.horizon_weight is not None:
|
127
|
+
errors *= self.horizon_weight
|
128
|
+
return self._safemean(errors)
|
105
129
|
|
106
130
|
|
107
131
|
class WAPE(TimeSeriesScorer):
|
@@ -119,6 +143,7 @@ class WAPE(TimeSeriesScorer):
|
|
119
143
|
- not sensitive to outliers
|
120
144
|
- prefers models that accurately estimate the median
|
121
145
|
|
146
|
+
If `self.horizon_weight` is provided, both the errors and the target time series in the denominator will be re-weighted.
|
122
147
|
|
123
148
|
References
|
124
149
|
----------
|
@@ -129,10 +154,19 @@ class WAPE(TimeSeriesScorer):
|
|
129
154
|
equivalent_tabular_regression_metric = "mean_absolute_error"
|
130
155
|
|
131
156
|
def compute_metric(
|
132
|
-
self,
|
157
|
+
self,
|
158
|
+
data_future: TimeSeriesDataFrame,
|
159
|
+
predictions: TimeSeriesDataFrame,
|
160
|
+
target: str = "target",
|
161
|
+
**kwargs,
|
133
162
|
) -> float:
|
134
163
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
135
|
-
|
164
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
165
|
+
errors = np.abs(y_true - y_pred).reshape([-1, self.prediction_length])
|
166
|
+
if self.horizon_weight is not None:
|
167
|
+
errors *= self.horizon_weight
|
168
|
+
y_true = y_true.reshape([-1, self.prediction_length]) * self.horizon_weight
|
169
|
+
return np.nansum(errors) / np.nansum(np.abs(y_true))
|
136
170
|
|
137
171
|
|
138
172
|
class SMAPE(TimeSeriesScorer):
|
@@ -158,10 +192,18 @@ class SMAPE(TimeSeriesScorer):
|
|
158
192
|
equivalent_tabular_regression_metric = "symmetric_mean_absolute_percentage_error"
|
159
193
|
|
160
194
|
def compute_metric(
|
161
|
-
self,
|
195
|
+
self,
|
196
|
+
data_future: TimeSeriesDataFrame,
|
197
|
+
predictions: TimeSeriesDataFrame,
|
198
|
+
target: str = "target",
|
199
|
+
**kwargs,
|
162
200
|
) -> float:
|
163
201
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
164
|
-
|
202
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
203
|
+
errors = (np.abs(y_true - y_pred) / (np.abs(y_true) + np.abs(y_pred))).reshape([-1, self.prediction_length])
|
204
|
+
if self.horizon_weight is not None:
|
205
|
+
errors *= self.horizon_weight
|
206
|
+
return 2 * self._safemean(errors)
|
165
207
|
|
166
208
|
|
167
209
|
class MAPE(TimeSeriesScorer):
|
@@ -187,10 +229,18 @@ class MAPE(TimeSeriesScorer):
|
|
187
229
|
equivalent_tabular_regression_metric = "mean_absolute_percentage_error"
|
188
230
|
|
189
231
|
def compute_metric(
|
190
|
-
self,
|
232
|
+
self,
|
233
|
+
data_future: TimeSeriesDataFrame,
|
234
|
+
predictions: TimeSeriesDataFrame,
|
235
|
+
target: str = "target",
|
236
|
+
**kwargs,
|
191
237
|
) -> float:
|
192
238
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
193
|
-
|
239
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
240
|
+
errors = (np.abs(y_true - y_pred) / np.abs(y_true)).reshape([-1, self.prediction_length])
|
241
|
+
if self.horizon_weight is not None:
|
242
|
+
errors *= self.horizon_weight
|
243
|
+
return self._safemean(errors)
|
194
244
|
|
195
245
|
|
196
246
|
class MASE(TimeSeriesScorer):
|
@@ -226,7 +276,15 @@ class MASE(TimeSeriesScorer):
|
|
226
276
|
optimized_by_median = True
|
227
277
|
equivalent_tabular_regression_metric = "mean_absolute_error"
|
228
278
|
|
229
|
-
def __init__(
|
279
|
+
def __init__(
|
280
|
+
self,
|
281
|
+
prediction_length: int = 1,
|
282
|
+
seasonal_period: Optional[int] = None,
|
283
|
+
horizon_weight: Optional[Sequence[float]] = None,
|
284
|
+
):
|
285
|
+
super().__init__(
|
286
|
+
prediction_length=prediction_length, seasonal_period=seasonal_period, horizon_weight=horizon_weight
|
287
|
+
)
|
230
288
|
self._past_abs_seasonal_error: Optional[pd.Series] = None
|
231
289
|
|
232
290
|
def save_past_metrics(
|
@@ -240,16 +298,22 @@ class MASE(TimeSeriesScorer):
|
|
240
298
|
self._past_abs_seasonal_error = None
|
241
299
|
|
242
300
|
def compute_metric(
|
243
|
-
self,
|
301
|
+
self,
|
302
|
+
data_future: TimeSeriesDataFrame,
|
303
|
+
predictions: TimeSeriesDataFrame,
|
304
|
+
target: str = "target",
|
305
|
+
**kwargs,
|
244
306
|
) -> float:
|
245
|
-
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
246
307
|
if self._past_abs_seasonal_error is None:
|
247
308
|
raise AssertionError("Call `save_past_metrics` before `compute_metric`")
|
248
309
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
310
|
+
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
311
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
312
|
+
|
313
|
+
errors = np.abs(y_true - y_pred).reshape([-1, self.prediction_length])
|
314
|
+
if self.horizon_weight is not None:
|
315
|
+
errors *= self.horizon_weight
|
316
|
+
return self._safemean(errors / self._past_abs_seasonal_error.to_numpy()[:, None])
|
253
317
|
|
254
318
|
|
255
319
|
class RMSSE(TimeSeriesScorer):
|
@@ -286,7 +350,15 @@ class RMSSE(TimeSeriesScorer):
|
|
286
350
|
|
287
351
|
equivalent_tabular_regression_metric = "root_mean_squared_error"
|
288
352
|
|
289
|
-
def __init__(
|
353
|
+
def __init__(
|
354
|
+
self,
|
355
|
+
prediction_length: int = 1,
|
356
|
+
seasonal_period: Optional[int] = None,
|
357
|
+
horizon_weight: Optional[Sequence[float]] = None,
|
358
|
+
):
|
359
|
+
super().__init__(
|
360
|
+
prediction_length=prediction_length, seasonal_period=seasonal_period, horizon_weight=horizon_weight
|
361
|
+
)
|
290
362
|
self._past_squared_seasonal_error: Optional[pd.Series] = None
|
291
363
|
|
292
364
|
def save_past_metrics(
|
@@ -300,16 +372,21 @@ class RMSSE(TimeSeriesScorer):
|
|
300
372
|
self._past_squared_seasonal_error = None
|
301
373
|
|
302
374
|
def compute_metric(
|
303
|
-
self,
|
375
|
+
self,
|
376
|
+
data_future: TimeSeriesDataFrame,
|
377
|
+
predictions: TimeSeriesDataFrame,
|
378
|
+
target: str = "target",
|
379
|
+
**kwargs,
|
304
380
|
) -> float:
|
305
|
-
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
306
381
|
if self._past_squared_seasonal_error is None:
|
307
382
|
raise AssertionError("Call `save_past_metrics` before `compute_metric`")
|
308
383
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
384
|
+
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
385
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
386
|
+
errors = ((y_true - y_pred) ** 2).reshape([-1, self.prediction_length])
|
387
|
+
if self.horizon_weight is not None:
|
388
|
+
errors *= self.horizon_weight
|
389
|
+
return np.sqrt(self._safemean(errors / self._past_squared_seasonal_error.to_numpy()[:, None]))
|
313
390
|
|
314
391
|
|
315
392
|
class RMSLE(TimeSeriesScorer):
|
@@ -336,20 +413,26 @@ class RMSLE(TimeSeriesScorer):
|
|
336
413
|
"""
|
337
414
|
|
338
415
|
def compute_metric(
|
339
|
-
self,
|
416
|
+
self,
|
417
|
+
data_future: TimeSeriesDataFrame,
|
418
|
+
predictions: TimeSeriesDataFrame,
|
419
|
+
target: str = "target",
|
420
|
+
**kwargs,
|
340
421
|
) -> float:
|
341
422
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
423
|
+
y_true, y_pred = y_true.to_numpy(), y_pred.to_numpy()
|
342
424
|
y_pred = np.clip(y_pred, a_min=0.0, a_max=None)
|
343
425
|
|
344
|
-
|
426
|
+
errors = np.power(np.log1p(y_pred) - np.log1p(y_true), 2).reshape([-1, self.prediction_length])
|
427
|
+
if self.horizon_weight is not None:
|
428
|
+
errors *= self.horizon_weight
|
429
|
+
return np.sqrt(self._safemean(errors))
|
345
430
|
|
346
431
|
def __call__(
|
347
432
|
self,
|
348
433
|
data: TimeSeriesDataFrame,
|
349
434
|
predictions: TimeSeriesDataFrame,
|
350
|
-
prediction_length: int = 1,
|
351
435
|
target: str = "target",
|
352
|
-
seasonal_period: Optional[int] = None,
|
353
436
|
**kwargs,
|
354
437
|
) -> float:
|
355
438
|
if (data[target] < 0).any():
|
@@ -357,9 +440,7 @@ class RMSLE(TimeSeriesScorer):
|
|
357
440
|
return super().__call__(
|
358
441
|
data=data,
|
359
442
|
predictions=predictions,
|
360
|
-
prediction_length=prediction_length,
|
361
443
|
target=target,
|
362
|
-
seasonal_period=seasonal_period,
|
363
444
|
**kwargs,
|
364
445
|
)
|
365
446
|
|
@@ -382,35 +463,43 @@ class WCD(TimeSeriesScorer):
|
|
382
463
|
Parameters
|
383
464
|
----------
|
384
465
|
alpha : float, default = 0.5
|
385
|
-
Values > 0.5
|
466
|
+
Values > 0.5 put a stronger penalty on underpredictions (when cumulative forecast is below the
|
386
467
|
cumulative actual value). Values < 0.5 put a stronger penalty on overpredictions.
|
387
468
|
"""
|
388
469
|
|
389
|
-
def __init__(
|
470
|
+
def __init__(
|
471
|
+
self,
|
472
|
+
alpha: float = 0.5,
|
473
|
+
prediction_length: int = 1,
|
474
|
+
seasonal_period: Optional[int] = None,
|
475
|
+
horizon_weight: Optional[Sequence[float]] = None,
|
476
|
+
):
|
477
|
+
super().__init__(
|
478
|
+
prediction_length=prediction_length, seasonal_period=seasonal_period, horizon_weight=horizon_weight
|
479
|
+
)
|
390
480
|
assert 0 < alpha < 1, "alpha must be in (0, 1)"
|
391
481
|
self.alpha = alpha
|
392
|
-
self.num_items: Optional[int] = None
|
393
482
|
warnings.warn(
|
394
483
|
f"{self.name} is an experimental metric. Its behavior may change in the future version of AutoGluon."
|
395
484
|
)
|
396
485
|
|
397
|
-
def save_past_metrics(
|
398
|
-
self, data_past: TimeSeriesDataFrame, target: str = "target", seasonal_period: int = 1, **kwargs
|
399
|
-
) -> None:
|
400
|
-
self.num_items = data_past.num_items
|
401
|
-
|
402
486
|
def _fast_cumsum(self, y: np.ndarray) -> np.ndarray:
|
403
|
-
"""Compute the cumulative sum for each consecutive `prediction_length` items in the array."""
|
404
|
-
|
405
|
-
y = y.reshape(self.num_items, -1)
|
487
|
+
"""Compute the cumulative sum for each consecutive `self.prediction_length` items in the array."""
|
488
|
+
y = y.reshape(-1, self.prediction_length)
|
406
489
|
return np.nancumsum(y, axis=1).ravel()
|
407
490
|
|
408
491
|
def compute_metric(
|
409
|
-
self,
|
492
|
+
self,
|
493
|
+
data_future: TimeSeriesDataFrame,
|
494
|
+
predictions: TimeSeriesDataFrame,
|
495
|
+
target: str = "target",
|
496
|
+
**kwargs,
|
410
497
|
) -> float:
|
411
498
|
y_true, y_pred = self._get_point_forecast_score_inputs(data_future, predictions, target=target)
|
412
499
|
cumsum_true = self._fast_cumsum(y_true.to_numpy())
|
413
500
|
cumsum_pred = self._fast_cumsum(y_pred.to_numpy())
|
414
501
|
diffs = cumsum_pred - cumsum_true
|
415
|
-
|
416
|
-
|
502
|
+
errors = (diffs * np.where(diffs < 0, -self.alpha, (1 - self.alpha))).reshape([-1, self.prediction_length])
|
503
|
+
if self.horizon_weight is not None:
|
504
|
+
errors *= self.horizon_weight
|
505
|
+
return 2 * self._safemean(errors)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Optional
|
1
|
+
from typing import Optional, Sequence
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
import pandas as pd
|
@@ -25,6 +25,7 @@ class WQL(TimeSeriesScorer):
|
|
25
25
|
- scale-dependent (time series with large absolute value contribute more to the loss)
|
26
26
|
- equivalent to WAPE if ``quantile_levels = [0.5]``
|
27
27
|
|
28
|
+
If `horizon_weight` is provided, both the errors and the target time series in the denominator will be re-weighted.
|
28
29
|
|
29
30
|
References
|
30
31
|
----------
|
@@ -34,16 +35,25 @@ class WQL(TimeSeriesScorer):
|
|
34
35
|
needs_quantile = True
|
35
36
|
|
36
37
|
def compute_metric(
|
37
|
-
self,
|
38
|
+
self,
|
39
|
+
data_future: TimeSeriesDataFrame,
|
40
|
+
predictions: TimeSeriesDataFrame,
|
41
|
+
target: str = "target",
|
42
|
+
**kwargs,
|
38
43
|
) -> float:
|
39
44
|
y_true, q_pred, quantile_levels = self._get_quantile_forecast_score_inputs(data_future, predictions, target)
|
40
|
-
|
41
|
-
|
45
|
+
y_true = y_true.to_numpy()[:, None] # shape [N, 1]
|
46
|
+
q_pred = q_pred.to_numpy() # shape [N, len(quantile_levels)]
|
42
47
|
|
43
|
-
|
44
|
-
np.
|
45
|
-
|
48
|
+
errors = (
|
49
|
+
np.abs((q_pred - y_true) * ((y_true <= q_pred) - quantile_levels))
|
50
|
+
.mean(axis=1)
|
51
|
+
.reshape([-1, self.prediction_length])
|
46
52
|
)
|
53
|
+
if self.horizon_weight is not None:
|
54
|
+
errors *= self.horizon_weight
|
55
|
+
y_true = y_true.reshape([-1, self.prediction_length]) * self.horizon_weight
|
56
|
+
return 2 * np.nansum(errors) / np.nansum(np.abs(y_true))
|
47
57
|
|
48
58
|
|
49
59
|
class SQL(TimeSeriesScorer):
|
@@ -79,7 +89,15 @@ class SQL(TimeSeriesScorer):
|
|
79
89
|
|
80
90
|
needs_quantile = True
|
81
91
|
|
82
|
-
def __init__(
|
92
|
+
def __init__(
|
93
|
+
self,
|
94
|
+
prediction_length: int = 1,
|
95
|
+
seasonal_period: Optional[int] = None,
|
96
|
+
horizon_weight: Optional[Sequence[float]] = None,
|
97
|
+
):
|
98
|
+
super().__init__(
|
99
|
+
prediction_length=prediction_length, seasonal_period=seasonal_period, horizon_weight=horizon_weight
|
100
|
+
)
|
83
101
|
self._past_abs_seasonal_error: Optional[pd.Series] = None
|
84
102
|
|
85
103
|
def save_past_metrics(
|
@@ -93,17 +111,24 @@ class SQL(TimeSeriesScorer):
|
|
93
111
|
self._past_abs_seasonal_error = None
|
94
112
|
|
95
113
|
def compute_metric(
|
96
|
-
self,
|
114
|
+
self,
|
115
|
+
data_future: TimeSeriesDataFrame,
|
116
|
+
predictions: TimeSeriesDataFrame,
|
117
|
+
target: str = "target",
|
118
|
+
**kwargs,
|
97
119
|
) -> float:
|
98
120
|
if self._past_abs_seasonal_error is None:
|
99
121
|
raise AssertionError("Call `save_past_metrics` before `compute_metric`")
|
100
122
|
|
101
123
|
y_true, q_pred, quantile_levels = self._get_quantile_forecast_score_inputs(data_future, predictions, target)
|
102
|
-
q_pred = q_pred.
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
124
|
+
q_pred = q_pred.to_numpy()
|
125
|
+
y_true = y_true.to_numpy()[:, None] # shape [N, 1]
|
126
|
+
|
127
|
+
errors = (
|
128
|
+
np.abs((q_pred - y_true) * ((y_true <= q_pred) - quantile_levels))
|
129
|
+
.mean(axis=1)
|
130
|
+
.reshape([-1, self.prediction_length])
|
131
|
+
)
|
132
|
+
if self.horizon_weight is not None:
|
133
|
+
errors *= self.horizon_weight
|
134
|
+
return 2 * self._safemean(errors / self._past_abs_seasonal_error.to_numpy()[:, None])
|
@@ -57,9 +57,6 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
57
57
|
Metric by which predictions will be ultimately evaluated on future test data. This only impacts
|
58
58
|
``model.score()``, as eval_metric is not used during training. Available metrics can be found in
|
59
59
|
``autogluon.timeseries.metrics``.
|
60
|
-
eval_metric_seasonal_period : int, optional
|
61
|
-
Seasonal period used to compute some evaluation metrics such as mean absolute scaled error (MASE). Defaults to
|
62
|
-
``None``, in which case the seasonal period is computed based on the data frequency.
|
63
60
|
hyperparameters : dict, default = None
|
64
61
|
Hyperparameters that will be used by the model (can be search spaces instead of fixed values).
|
65
62
|
If None, model defaults are used. This is identical to passing an empty dictionary.
|
@@ -88,7 +85,6 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
88
85
|
target: str = "target",
|
89
86
|
quantile_levels: Sequence[float] = (0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9),
|
90
87
|
eval_metric: Union[str, TimeSeriesScorer, None] = None,
|
91
|
-
eval_metric_seasonal_period: Optional[int] = None,
|
92
88
|
):
|
93
89
|
self.name = name or re.sub(r"Model$", "", self.__class__.__name__)
|
94
90
|
|
@@ -103,8 +99,7 @@ class TimeSeriesModelBase(ModelBase, ABC):
|
|
103
99
|
|
104
100
|
self.path = os.path.join(self.path_root, self.name)
|
105
101
|
|
106
|
-
self.eval_metric
|
107
|
-
self.eval_metric_seasonal_period = eval_metric_seasonal_period
|
102
|
+
self.eval_metric = check_get_evaluation_metric(eval_metric, prediction_length=prediction_length)
|
108
103
|
self.target: str = target
|
109
104
|
self.covariate_metadata = covariate_metadata or CovariateMetadata()
|
110
105
|
|
@@ -393,7 +388,6 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
393
388
|
target: str = "target",
|
394
389
|
quantile_levels: Sequence[float] = (0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9),
|
395
390
|
eval_metric: Union[str, TimeSeriesScorer, None] = None,
|
396
|
-
eval_metric_seasonal_period: Optional[int] = None,
|
397
391
|
):
|
398
392
|
# TODO: make freq a required argument in AbstractTimeSeriesModel
|
399
393
|
super().__init__(
|
@@ -406,7 +400,6 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
406
400
|
target=target,
|
407
401
|
quantile_levels=quantile_levels,
|
408
402
|
eval_metric=eval_metric,
|
409
|
-
eval_metric_seasonal_period=eval_metric_seasonal_period,
|
410
403
|
)
|
411
404
|
self.target_scaler: Optional[TargetScaler]
|
412
405
|
self.covariate_scaler: Optional[CovariateScaler]
|
@@ -700,19 +693,15 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
700
693
|
self,
|
701
694
|
data: TimeSeriesDataFrame,
|
702
695
|
predictions: TimeSeriesDataFrame,
|
703
|
-
metric: Optional[str] = None,
|
704
696
|
) -> float:
|
705
697
|
"""Compute the score measuring how well the predictions align with the data."""
|
706
|
-
|
707
|
-
return eval_metric.score(
|
698
|
+
return self.eval_metric.score(
|
708
699
|
data=data,
|
709
700
|
predictions=predictions,
|
710
|
-
prediction_length=self.prediction_length,
|
711
701
|
target=self.target,
|
712
|
-
seasonal_period=self.eval_metric_seasonal_period,
|
713
702
|
)
|
714
703
|
|
715
|
-
def score(self, data: TimeSeriesDataFrame
|
704
|
+
def score(self, data: TimeSeriesDataFrame) -> float:
|
716
705
|
"""Return the evaluation scores for given metric and dataset. The last
|
717
706
|
`self.prediction_length` time steps of each time series in the input data set
|
718
707
|
will be held out and used for computing the evaluation score. Time series
|
@@ -722,9 +711,6 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
722
711
|
----------
|
723
712
|
data: TimeSeriesDataFrame
|
724
713
|
Dataset used for scoring.
|
725
|
-
metric: str
|
726
|
-
String identifier of evaluation metric to use, from one of
|
727
|
-
`autogluon.timeseries.utils.metric_utils.AVAILABLE_METRICS`.
|
728
714
|
|
729
715
|
Returns
|
730
716
|
-------
|
@@ -736,7 +722,7 @@ class AbstractTimeSeriesModel(TimeSeriesModelBase, TimeSeriesTunable, ABC):
|
|
736
722
|
prediction_length=self.prediction_length, known_covariates_names=self.covariate_metadata.known_covariates
|
737
723
|
)
|
738
724
|
predictions = self.predict(past_data, known_covariates=known_covariates)
|
739
|
-
return self._score_with_predictions(data=data, predictions=predictions
|
725
|
+
return self._score_with_predictions(data=data, predictions=predictions)
|
740
726
|
|
741
727
|
def score_and_cache_oof(
|
742
728
|
self,
|
@@ -28,7 +28,6 @@ class TimeSeriesEnsembleSelection(EnsembleSelection):
|
|
28
28
|
random_state: Optional[np.random.RandomState] = None,
|
29
29
|
prediction_length: int = 1,
|
30
30
|
target: str = "target",
|
31
|
-
eval_metric_seasonal_period: int = 1,
|
32
31
|
**kwargs,
|
33
32
|
):
|
34
33
|
super().__init__(
|
@@ -43,7 +42,6 @@ class TimeSeriesEnsembleSelection(EnsembleSelection):
|
|
43
42
|
)
|
44
43
|
self.prediction_length = prediction_length
|
45
44
|
self.target = target
|
46
|
-
self.eval_metric_seasonal_period = eval_metric_seasonal_period
|
47
45
|
self.metric: TimeSeriesScorer
|
48
46
|
|
49
47
|
self.dummy_pred_per_window = []
|
@@ -79,6 +77,10 @@ class TimeSeriesEnsembleSelection(EnsembleSelection):
|
|
79
77
|
self.scorer_per_window = []
|
80
78
|
self.data_future_per_window = []
|
81
79
|
|
80
|
+
seasonal_period = self.metric.seasonal_period
|
81
|
+
if seasonal_period is None:
|
82
|
+
seasonal_period = get_seasonality(labels[0].freq)
|
83
|
+
|
82
84
|
for window_idx, data in enumerate(labels):
|
83
85
|
dummy_pred = copy.deepcopy(predictions[0][window_idx])
|
84
86
|
# This should never happen; sanity check to make sure that all predictions have the same index
|
@@ -90,7 +92,7 @@ class TimeSeriesEnsembleSelection(EnsembleSelection):
|
|
90
92
|
# Split the observed time series once to avoid repeated computations inside the evaluator
|
91
93
|
data_past = data.slice_by_timestep(None, -self.prediction_length)
|
92
94
|
data_future = data.slice_by_timestep(-self.prediction_length, None)
|
93
|
-
scorer.save_past_metrics(data_past, target=self.target, seasonal_period=
|
95
|
+
scorer.save_past_metrics(data_past, target=self.target, seasonal_period=seasonal_period)
|
94
96
|
self.scorer_per_window.append(scorer)
|
95
97
|
self.data_future_per_window.append(data_future)
|
96
98
|
|
@@ -122,7 +124,9 @@ class TimeSeriesEnsembleSelection(EnsembleSelection):
|
|
122
124
|
dummy_pred[list(dummy_pred.columns)] = y_pred_proba[window_idx]
|
123
125
|
# We use scorer.compute_metric instead of scorer.score to avoid repeated calls to scorer.save_past_metrics
|
124
126
|
metric_value = self.scorer_per_window[window_idx].compute_metric(
|
125
|
-
data_future,
|
127
|
+
data_future,
|
128
|
+
dummy_pred,
|
129
|
+
target=self.target,
|
126
130
|
)
|
127
131
|
total_score += metric.sign * metric_value
|
128
132
|
avg_score = total_score / len(self.data_future_per_window)
|
@@ -162,14 +166,11 @@ class GreedyEnsemble(AbstractWeightedTimeSeriesEnsembleModel):
|
|
162
166
|
model_scores: Optional[Dict[str, float]] = None,
|
163
167
|
time_limit: Optional[float] = None,
|
164
168
|
):
|
165
|
-
if self.eval_metric_seasonal_period is None:
|
166
|
-
self.eval_metric_seasonal_period = get_seasonality(self.freq)
|
167
169
|
ensemble_selection = TimeSeriesEnsembleSelection(
|
168
170
|
ensemble_size=self.get_hyperparameters()["ensemble_size"],
|
169
171
|
metric=self.eval_metric,
|
170
172
|
prediction_length=self.prediction_length,
|
171
173
|
target=self.target,
|
172
|
-
eval_metric_seasonal_period=self.eval_metric_seasonal_period,
|
173
174
|
)
|
174
175
|
ensemble_selection.fit(
|
175
176
|
predictions=list(predictions_per_window.values()),
|
@@ -183,7 +183,6 @@ def get_preset_models(
|
|
183
183
|
prediction_length: int,
|
184
184
|
path: str,
|
185
185
|
eval_metric: Union[str, TimeSeriesScorer],
|
186
|
-
eval_metric_seasonal_period: Optional[int],
|
187
186
|
hyperparameters: Union[str, Dict, None],
|
188
187
|
hyperparameter_tune: bool,
|
189
188
|
covariate_metadata: CovariateMetadata,
|
@@ -260,7 +259,6 @@ def get_preset_models(
|
|
260
259
|
freq=freq,
|
261
260
|
prediction_length=prediction_length,
|
262
261
|
eval_metric=eval_metric,
|
263
|
-
eval_metric_seasonal_period=eval_metric_seasonal_period,
|
264
262
|
covariate_metadata=covariate_metadata,
|
265
263
|
hyperparameters=model_hps,
|
266
264
|
**kwargs,
|
@@ -93,6 +93,14 @@ class TimeSeriesPredictor:
|
|
93
93
|
eval_metric_seasonal_period : int, optional
|
94
94
|
Seasonal period used to compute some evaluation metrics such as mean absolute scaled error (MASE). Defaults to
|
95
95
|
``None``, in which case the seasonal period is computed based on the data frequency.
|
96
|
+
horizon_weight : List[float], optional
|
97
|
+
Weight assigned to each time step in the forecast horizon when computing the `eval_metric`. If provided, this
|
98
|
+
must be a list with `prediction_length` non-negative values, where at least some values are greater than zero.
|
99
|
+
AutoGluon will automatically normalize the weights so that they sum up to `prediction_length`. By default, all
|
100
|
+
time steps in the forecast horizon have the same weight, which is equivalent to setting `horizon_weight = [1] * prediction_length`.
|
101
|
+
|
102
|
+
This parameter only affects model selection and ensemble construction; it has no effect on the loss function of
|
103
|
+
the individual forecasting models.
|
96
104
|
known_covariates_names: List[str], optional
|
97
105
|
Names of the covariates that are known in advance for all time steps in the forecast horizon. These are also
|
98
106
|
known as dynamic features, exogenous variables, additional regressors or related time series. Examples of such
|
@@ -107,7 +115,7 @@ class TimeSeriesPredictor:
|
|
107
115
|
List of increasing decimals that specifies which quantiles should be estimated when making distributional
|
108
116
|
forecasts. Defaults to ``[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]``.
|
109
117
|
path : str or pathlib.Path, optional
|
110
|
-
Path to the directory where models and intermediate outputs will be saved. Defaults to a timestamped folder
|
118
|
+
Path to the local directory where models and intermediate outputs will be saved. Defaults to a timestamped folder
|
111
119
|
``AutogluonModels/ag-[TIMESTAMP]`` that will be created in the working directory.
|
112
120
|
verbosity : int, default = 2
|
113
121
|
Verbosity levels range from 0 to 4 and control how much information is printed to stdout. Higher levels
|
@@ -144,6 +152,7 @@ class TimeSeriesPredictor:
|
|
144
152
|
freq: Optional[str] = None,
|
145
153
|
eval_metric: Union[str, TimeSeriesScorer, None] = None,
|
146
154
|
eval_metric_seasonal_period: Optional[int] = None,
|
155
|
+
horizon_weight: Optional[List[float]] = None,
|
147
156
|
path: Optional[Union[str, Path]] = None,
|
148
157
|
verbosity: int = 2,
|
149
158
|
log_to_file: bool = True,
|
@@ -156,6 +165,11 @@ class TimeSeriesPredictor:
|
|
156
165
|
self.verbosity = verbosity
|
157
166
|
set_logger_verbosity(self.verbosity, logger=logger)
|
158
167
|
self.path = setup_outputdir(path)
|
168
|
+
if self.path.lower().startswith("s3://"):
|
169
|
+
logger.warning(
|
170
|
+
"Warning: S3 paths are not supported for the `path` argument in TimeSeriesPredictor. "
|
171
|
+
"Use a local path and upload the trained predictor to S3 manually if needed"
|
172
|
+
)
|
159
173
|
self._setup_log_to_file(log_to_file=log_to_file, log_file_path=log_file_path)
|
160
174
|
|
161
175
|
self.cache_predictions = cache_predictions
|
@@ -187,15 +201,18 @@ class TimeSeriesPredictor:
|
|
187
201
|
if std_freq != str(self.freq):
|
188
202
|
logger.info(f"Frequency '{self.freq}' stored as '{std_freq}'")
|
189
203
|
self.freq = std_freq
|
190
|
-
self.eval_metric = check_get_evaluation_metric(
|
191
|
-
|
204
|
+
self.eval_metric: TimeSeriesScorer = check_get_evaluation_metric(
|
205
|
+
eval_metric,
|
206
|
+
prediction_length=prediction_length,
|
207
|
+
seasonal_period=eval_metric_seasonal_period,
|
208
|
+
horizon_weight=horizon_weight,
|
209
|
+
)
|
192
210
|
if quantile_levels is None:
|
193
211
|
quantile_levels = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
|
194
212
|
self.quantile_levels = sorted(quantile_levels)
|
195
213
|
self._learner: TimeSeriesLearner = self._learner_type(
|
196
214
|
path_context=self.path,
|
197
|
-
eval_metric=eval_metric,
|
198
|
-
eval_metric_seasonal_period=eval_metric_seasonal_period,
|
215
|
+
eval_metric=self.eval_metric,
|
199
216
|
target=self.target,
|
200
217
|
known_covariates_names=self.known_covariates_names,
|
201
218
|
prediction_length=self.prediction_length,
|
@@ -494,33 +511,22 @@ class TimeSeriesPredictor:
|
|
494
511
|
|
495
512
|
Available presets:
|
496
513
|
|
497
|
-
- ``"fast_training"``:
|
498
|
-
|
499
|
-
- ``"
|
500
|
-
- ``"high_quality"``: All ML models available in AutoGluon + additional statistical models (``NPTS``, ``AutoETS``,
|
501
|
-
``DynamicOptimizedTheta``). Much more accurate than ``medium_quality``, but takes longer to train.
|
514
|
+
- ``"fast_training"``: Simple statistical and tree-based ML models. These models are fast to train but may not be very accurate.
|
515
|
+
- ``"medium_quality"``: Same models as above, plus deep learning models ``TemporalFusionTransformer`` and Chronos-Bolt (small). Produces good forecasts with reasonable training time.
|
516
|
+
- ``"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.
|
502
517
|
- ``"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.
|
503
518
|
|
504
|
-
Available presets with the
|
519
|
+
Available presets with the `Chronos-Bolt <https://github.com/amazon-science/chronos-forecasting>`_ model:
|
505
520
|
|
506
521
|
- ``"bolt_{model_size}"``: where model size is one of ``tiny,mini,small,base``. Uses the Chronos-Bolt pretrained model for zero-shot forecasting.
|
507
522
|
See the documentation for ``ChronosModel`` or see `Hugging Face <https://huggingface.co/collections/amazon/chronos-models-65f1791d630a8d57cb718444>`_ for more information.
|
508
523
|
|
509
|
-
|
510
|
-
|
524
|
+
Exact definitions of these presets can be found in the source code
|
525
|
+
[`1 <https://github.com/autogluon/autogluon/blob/stable/timeseries/src/autogluon/timeseries/configs/presets_configs.py>`_,
|
526
|
+
`2 <https://github.com/autogluon/autogluon/blob/stable/timeseries/src/autogluon/timeseries/models/presets.py>`_].
|
511
527
|
|
512
|
-
|
513
|
-
|
514
|
-
Note that a GPU is required for model sizes ``small``, ``base`` and ``large``.
|
515
|
-
- ``"chronos"``: alias for ``"chronos_small"``.
|
516
|
-
- ``"chronos_ensemble"``: builds an ensemble of seasonal naive, tree-based and deep learning models with fast inference
|
517
|
-
and ``"chronos_small"``.
|
518
|
-
- ``"chronos_large_ensemble"``: builds an ensemble of seasonal naive, tree-based and deep learning models
|
519
|
-
with fast inference and ``"chronos_large"``.
|
520
|
-
|
521
|
-
Details for these presets can be found in ``autogluon/timeseries/configs/presets_configs.py``. If not
|
522
|
-
provided, user-provided values for ``hyperparameters`` and ``hyperparameter_tune_kwargs`` will be used
|
523
|
-
(defaulting to their default values specified below).
|
528
|
+
If no `presets` are selected, user-provided values for `hyperparameters` will be used (defaulting to their
|
529
|
+
default values specified below).
|
524
530
|
hyperparameters : str or dict, optional
|
525
531
|
Determines what models are trained and what hyperparameters are used by each model.
|
526
532
|
|
@@ -684,7 +690,8 @@ class TimeSeriesPredictor:
|
|
684
690
|
target=self.target,
|
685
691
|
known_covariates_names=self.known_covariates_names,
|
686
692
|
eval_metric=self.eval_metric,
|
687
|
-
eval_metric_seasonal_period=self.
|
693
|
+
eval_metric_seasonal_period=self.eval_metric.seasonal_period,
|
694
|
+
horizon_weight=self.eval_metric.horizon_weight,
|
688
695
|
quantile_levels=self.quantile_levels,
|
689
696
|
freq=self.freq,
|
690
697
|
time_limit=time_limit,
|
@@ -1500,7 +1507,8 @@ class TimeSeriesPredictor:
|
|
1500
1507
|
target=self.target,
|
1501
1508
|
prediction_length=self.prediction_length,
|
1502
1509
|
eval_metric=self.eval_metric.name,
|
1503
|
-
eval_metric_seasonal_period=self.
|
1510
|
+
eval_metric_seasonal_period=self.eval_metric.seasonal_period,
|
1511
|
+
horizon_weight=self.eval_metric.horizon_weight,
|
1504
1512
|
quantile_levels=self.quantile_levels,
|
1505
1513
|
)
|
1506
1514
|
return simulation_dict
|
autogluon/timeseries/trainer.py
CHANGED
@@ -46,7 +46,6 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
46
46
|
path: str,
|
47
47
|
prediction_length: int = 1,
|
48
48
|
eval_metric: Union[str, TimeSeriesScorer, None] = None,
|
49
|
-
eval_metric_seasonal_period: Optional[int] = None,
|
50
49
|
save_data: bool = True,
|
51
50
|
skip_model_selection: bool = False,
|
52
51
|
enable_ensemble: bool = True,
|
@@ -86,8 +85,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
86
85
|
#: self.refit_single_full() and self.refit_full().
|
87
86
|
self.model_refit_map = {}
|
88
87
|
|
89
|
-
self.eval_metric
|
90
|
-
self.eval_metric_seasonal_period = eval_metric_seasonal_period
|
88
|
+
self.eval_metric = check_get_evaluation_metric(eval_metric, prediction_length=prediction_length)
|
91
89
|
if val_splitter is None:
|
92
90
|
val_splitter = ExpandingWindowSplitter(prediction_length=self.prediction_length)
|
93
91
|
assert isinstance(val_splitter, AbstractWindowSplitter), "val_splitter must be of type AbstractWindowSplitter"
|
@@ -577,7 +575,6 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
577
575
|
ensemble = self.ensemble_model_type(
|
578
576
|
name=self._get_ensemble_model_name(),
|
579
577
|
eval_metric=self.eval_metric,
|
580
|
-
eval_metric_seasonal_period=self.eval_metric_seasonal_period,
|
581
578
|
target=self.target,
|
582
579
|
prediction_length=self.prediction_length,
|
583
580
|
path=self.path,
|
@@ -791,6 +788,17 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
791
788
|
raise ValueError(f"Model {model_name} failed to predict. Please check the model's logs.")
|
792
789
|
return predictions
|
793
790
|
|
791
|
+
def _get_eval_metric(self, metric: Union[str, TimeSeriesScorer, None]) -> TimeSeriesScorer:
|
792
|
+
if metric is None:
|
793
|
+
return self.eval_metric
|
794
|
+
else:
|
795
|
+
return check_get_evaluation_metric(
|
796
|
+
metric,
|
797
|
+
prediction_length=self.prediction_length,
|
798
|
+
seasonal_period=self.eval_metric.seasonal_period,
|
799
|
+
horizon_weight=self.eval_metric.horizon_weight,
|
800
|
+
)
|
801
|
+
|
794
802
|
def _score_with_predictions(
|
795
803
|
self,
|
796
804
|
data: TimeSeriesDataFrame,
|
@@ -798,13 +806,11 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
798
806
|
metric: Union[str, TimeSeriesScorer, None] = None,
|
799
807
|
) -> float:
|
800
808
|
"""Compute the score measuring how well the predictions align with the data."""
|
801
|
-
|
802
|
-
return eval_metric.score(
|
809
|
+
return self._get_eval_metric(metric).score(
|
803
810
|
data=data,
|
804
811
|
predictions=predictions,
|
805
812
|
prediction_length=self.prediction_length,
|
806
813
|
target=self.target,
|
807
|
-
seasonal_period=self.eval_metric_seasonal_period,
|
808
814
|
)
|
809
815
|
|
810
816
|
def score(
|
@@ -814,7 +820,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
814
820
|
metric: Union[str, TimeSeriesScorer, None] = None,
|
815
821
|
use_cache: bool = True,
|
816
822
|
) -> float:
|
817
|
-
eval_metric = self.
|
823
|
+
eval_metric = self._get_eval_metric(metric)
|
818
824
|
scores_dict = self.evaluate(data=data, model=model, metrics=[eval_metric], use_cache=use_cache)
|
819
825
|
return scores_dict[eval_metric.name]
|
820
826
|
|
@@ -833,7 +839,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
833
839
|
metrics_ = [metrics] if not isinstance(metrics, list) else metrics
|
834
840
|
scores_dict = {}
|
835
841
|
for metric in metrics_:
|
836
|
-
eval_metric = self.
|
842
|
+
eval_metric = self._get_eval_metric(metric)
|
837
843
|
scores_dict[eval_metric.name] = self._score_with_predictions(
|
838
844
|
data=data, predictions=predictions, metric=eval_metric
|
839
845
|
)
|
@@ -855,7 +861,7 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
855
861
|
confidence_level: float = 0.99,
|
856
862
|
) -> pd.DataFrame:
|
857
863
|
assert method in ["naive", "permutation"], f"Invalid feature importance method {method}."
|
858
|
-
|
864
|
+
eval_metric = self._get_eval_metric(metric)
|
859
865
|
|
860
866
|
logger.info("Computing feature importance")
|
861
867
|
|
@@ -906,7 +912,9 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
906
912
|
else:
|
907
913
|
data_sample = data
|
908
914
|
|
909
|
-
base_score = self.evaluate(data=data_sample, model=model, metrics=
|
915
|
+
base_score = self.evaluate(data=data_sample, model=model, metrics=eval_metric, use_cache=False)[
|
916
|
+
eval_metric.name
|
917
|
+
]
|
910
918
|
|
911
919
|
for feature in features:
|
912
920
|
# override importance for unused features
|
@@ -914,9 +922,9 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
914
922
|
continue
|
915
923
|
else:
|
916
924
|
data_sample_replaced = importance_transform.transform(data_sample, feature_name=feature)
|
917
|
-
score = self.evaluate(
|
918
|
-
|
919
|
-
]
|
925
|
+
score = self.evaluate(
|
926
|
+
data=data_sample_replaced, model=model, metrics=eval_metric, use_cache=False
|
927
|
+
)[eval_metric.name]
|
920
928
|
|
921
929
|
importance = base_score - score
|
922
930
|
if relative_scores:
|
@@ -1266,7 +1274,6 @@ class TimeSeriesTrainer(AbstractTrainer[TimeSeriesModelBase]):
|
|
1266
1274
|
return get_preset_models(
|
1267
1275
|
path=self.path,
|
1268
1276
|
eval_metric=self.eval_metric,
|
1269
|
-
eval_metric_seasonal_period=self.eval_metric_seasonal_period,
|
1270
1277
|
prediction_length=self.prediction_length,
|
1271
1278
|
freq=freq,
|
1272
1279
|
hyperparameters=hyperparameters,
|
autogluon/timeseries/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: autogluon.timeseries
|
3
|
-
Version: 1.2.
|
3
|
+
Version: 1.2.1b20250426
|
4
4
|
Summary: Fast and Accurate ML in 3 Lines of Code
|
5
5
|
Home-page: https://github.com/autogluon/autogluon
|
6
6
|
Author: AutoGluon Community
|
@@ -55,10 +55,10 @@ Requires-Dist: fugue>=0.9.0
|
|
55
55
|
Requires-Dist: tqdm<5,>=4.38
|
56
56
|
Requires-Dist: orjson~=3.9
|
57
57
|
Requires-Dist: tensorboard<3,>=2.9
|
58
|
-
Requires-Dist: autogluon.core[raytune]==1.2.
|
59
|
-
Requires-Dist: autogluon.common==1.2.
|
60
|
-
Requires-Dist: autogluon.features==1.2.
|
61
|
-
Requires-Dist: autogluon.tabular[catboost,lightgbm,xgboost]==1.2.
|
58
|
+
Requires-Dist: autogluon.core[raytune]==1.2.1b20250426
|
59
|
+
Requires-Dist: autogluon.common==1.2.1b20250426
|
60
|
+
Requires-Dist: autogluon.features==1.2.1b20250426
|
61
|
+
Requires-Dist: autogluon.tabular[catboost,lightgbm,xgboost]==1.2.1b20250426
|
62
62
|
Provides-Extra: all
|
63
63
|
Provides-Extra: chronos-onnx
|
64
64
|
Requires-Dist: optimum[onnxruntime]<1.23,>=1.17; extra == "chronos-onnx"
|
@@ -1,25 +1,25 @@
|
|
1
|
-
autogluon.timeseries-1.2.
|
1
|
+
autogluon.timeseries-1.2.1b20250426-py3.9-nspkg.pth,sha256=cQGwpuGPqg1GXscIwt-7PmME1OnSpD-7ixkikJ31WAY,554
|
2
2
|
autogluon/timeseries/__init__.py,sha256=_CrLLc1fkjen7UzWoO0Os8WZoHOgvZbHKy46I8v_4k4,304
|
3
3
|
autogluon/timeseries/evaluator.py,sha256=l642tYfTHsl8WVIq_vV6qhgAFVFr9UuZD7gLra3A_Kc,250
|
4
|
-
autogluon/timeseries/learner.py,sha256=
|
5
|
-
autogluon/timeseries/predictor.py,sha256=
|
4
|
+
autogluon/timeseries/learner.py,sha256=pIn4YSOk0aqCWyBpIlwnAsFnG4h7PLXk8guFH3wFS-w,13923
|
5
|
+
autogluon/timeseries/predictor.py,sha256=Dz-LJVU5sjlFCOqHTeYPt77DuGavdAXB0DkclpM55rY,88173
|
6
6
|
autogluon/timeseries/regressor.py,sha256=xw5VPrXS-NQ_Ts4ppDjoNV0TdqUYjW4VINUtb_BZdiI,11868
|
7
7
|
autogluon/timeseries/splitter.py,sha256=yzPca9p2bWV-_VJAptUyyzQsxu-uixAdpMoGQtDzMD4,3205
|
8
|
-
autogluon/timeseries/trainer.py,sha256=
|
9
|
-
autogluon/timeseries/version.py,sha256=
|
8
|
+
autogluon/timeseries/trainer.py,sha256=57OyqlTAVahDPxF5GmdDljIr1RbjnIUL_d5TbrkTJ2c,58075
|
9
|
+
autogluon/timeseries/version.py,sha256=OsP4ql15uiax-GUPsVJ1sGZMaTOWMBXWWMnLOe5tRrY,91
|
10
10
|
autogluon/timeseries/configs/__init__.py,sha256=BTtHIPCYeGjqgOcvqb8qPD4VNX-ICKOg6wnkew1cPOE,98
|
11
11
|
autogluon/timeseries/configs/presets_configs.py,sha256=cLat8ecLlWrI-SC5KLBDCX2SbVXaucemy2pjxJAtSY0,2543
|
12
12
|
autogluon/timeseries/dataset/__init__.py,sha256=UvnhAN5tjgxXTHoZMQDy64YMDj4Xxa68yY7NP4vAw0o,81
|
13
13
|
autogluon/timeseries/dataset/ts_dataframe.py,sha256=W3VE65lFyWmqMQ3XHN4Jhrqf_dO1EOLneNL2QDvVxeY,48120
|
14
|
-
autogluon/timeseries/metrics/__init__.py,sha256=
|
15
|
-
autogluon/timeseries/metrics/abstract.py,sha256=
|
16
|
-
autogluon/timeseries/metrics/point.py,sha256=
|
17
|
-
autogluon/timeseries/metrics/quantile.py,sha256=
|
14
|
+
autogluon/timeseries/metrics/__init__.py,sha256=6v6aFjC_zbSTcSjU2hr0_LQf3MhCArMgKow3IfU2g-M,3477
|
15
|
+
autogluon/timeseries/metrics/abstract.py,sha256=Z4ThftPBgLl9AgPQoJWcjHpVaOWWr6X3s3WwpvpAUOg,11818
|
16
|
+
autogluon/timeseries/metrics/point.py,sha256=xllyGh11otbmUVHyIaceROPR3qyllWPQ9xlSmIGI3EI,18306
|
17
|
+
autogluon/timeseries/metrics/quantile.py,sha256=vhmETtjPsIfVlvtILNAT6F2PtIDNPrOroy-U1FQbgw8,4632
|
18
18
|
autogluon/timeseries/metrics/utils.py,sha256=HuDe1BNe8yJU4f_DKM913nNrUueoRaw6zhxm1-S20s0,910
|
19
19
|
autogluon/timeseries/models/__init__.py,sha256=MYD9JJ-wUDE5B6jW6E6LU2eXQ6vflfQBvqQJkdzJa3A,1189
|
20
|
-
autogluon/timeseries/models/presets.py,sha256=
|
20
|
+
autogluon/timeseries/models/presets.py,sha256=HEACiRpnY6dcff7W44gnM0x1KRgr2bNf5D6zcaHgHxo,12201
|
21
21
|
autogluon/timeseries/models/abstract/__init__.py,sha256=Htfkjjc3vo92RvyM8rIlQ0PLWt3jcrCKZES07UvCMV0,146
|
22
|
-
autogluon/timeseries/models/abstract/abstract_timeseries_model.py,sha256=
|
22
|
+
autogluon/timeseries/models/abstract/abstract_timeseries_model.py,sha256=94TG7tsdfENP41QATr4IeMofaFt8ySjrrrH4MxZZ3Xc,32104
|
23
23
|
autogluon/timeseries/models/abstract/model_trial.py,sha256=ENPg_7nsdxIvaNM0o0UShZ3x8jFlRmwRc5m0fGPC0TM,3720
|
24
24
|
autogluon/timeseries/models/abstract/tunable.py,sha256=SFl4vjkb6BfFFaRPVdftnnLYlIyCThutLHxiiAlV6tY,7168
|
25
25
|
autogluon/timeseries/models/autogluon_tabular/__init__.py,sha256=r9i6jWcyeLHYClkcMSKRVsfrkBUMxpDrTATNTBc_qgQ,136
|
@@ -36,7 +36,7 @@ autogluon/timeseries/models/chronos/pipeline/utils.py,sha256=dtDX5Pyu95bGv7qmqgf
|
|
36
36
|
autogluon/timeseries/models/ensemble/__init__.py,sha256=_BivnZaOWJiIvu93IQy0mrLdCZKT2NHHSqkf31hwF2s,158
|
37
37
|
autogluon/timeseries/models/ensemble/abstract.py,sha256=ie-BKD4JIkQQoKqtf6sYI5Aix7dSgywFsSdeGPxoElk,5821
|
38
38
|
autogluon/timeseries/models/ensemble/basic.py,sha256=BRPWg_Wgfb87iInFSoTRE75BRHaovRR5HFRvzxET_wU,3423
|
39
|
-
autogluon/timeseries/models/ensemble/greedy.py,sha256=
|
39
|
+
autogluon/timeseries/models/ensemble/greedy.py,sha256=oW2d3-cce1Xck3NOtTh_8uHnjmc-2hGntPGoJQHUibE,7213
|
40
40
|
autogluon/timeseries/models/gluonts/__init__.py,sha256=asC1PTj4j9xMbilvk1IT1julnpeoKbv5ZNuAR6-DFgA,361
|
41
41
|
autogluon/timeseries/models/gluonts/abstract_gluonts.py,sha256=35T8rty6sPGiaSFNpiVNmeseo1_qpn664UcWo92W5eI,32906
|
42
42
|
autogluon/timeseries/models/gluonts/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -60,11 +60,11 @@ autogluon/timeseries/utils/datetime/base.py,sha256=3NdsH3NDq4cVAOSoy3XpaNixyNlbj
|
|
60
60
|
autogluon/timeseries/utils/datetime/lags.py,sha256=gQDk5_zmsY5DUWDUpSaCKYkQ9nHKKY-LsywJQRAoYSk,5988
|
61
61
|
autogluon/timeseries/utils/datetime/seasonality.py,sha256=YK_2k8hvYIMW-sJPnjGWRtCnvIOthwA2hATB3nwVoD4,834
|
62
62
|
autogluon/timeseries/utils/datetime/time_features.py,sha256=MjLi3zQ00uWWJtXH9oGX2GJkTbvjdSiuabSa4kcVuxE,2672
|
63
|
-
autogluon.timeseries-1.2.
|
64
|
-
autogluon.timeseries-1.2.
|
65
|
-
autogluon.timeseries-1.2.
|
66
|
-
autogluon.timeseries-1.2.
|
67
|
-
autogluon.timeseries-1.2.
|
68
|
-
autogluon.timeseries-1.2.
|
69
|
-
autogluon.timeseries-1.2.
|
70
|
-
autogluon.timeseries-1.2.
|
63
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
|
64
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/METADATA,sha256=Wbji_e1nnEoqojfqovvTcoocs8z-u88kylcXK9PnXkA,12737
|
65
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/NOTICE,sha256=7nPQuj8Kp-uXsU0S5so3-2dNU5EctS5hDXvvzzehd7E,114
|
66
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
67
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/namespace_packages.txt,sha256=giERA4R78OkJf2ijn5slgjURlhRPzfLr7waIcGkzYAo,10
|
68
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/top_level.txt,sha256=giERA4R78OkJf2ijn5slgjURlhRPzfLr7waIcGkzYAo,10
|
69
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
70
|
+
autogluon.timeseries-1.2.1b20250426.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|