autogluon.timeseries 1.2.1b20250304__py3-none-any.whl → 1.2.1b20250306__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.
Files changed (22) hide show
  1. autogluon/timeseries/models/abstract/abstract_timeseries_model.py +246 -446
  2. autogluon/timeseries/models/abstract/tunable.py +189 -0
  3. autogluon/timeseries/models/autogluon_tabular/mlforecast.py +3 -4
  4. autogluon/timeseries/models/autogluon_tabular/transforms.py +2 -2
  5. autogluon/timeseries/models/ensemble/abstract_timeseries_ensemble.py +8 -0
  6. autogluon/timeseries/models/ensemble/greedy_ensemble.py +4 -2
  7. autogluon/timeseries/models/multi_window/multi_window_model.py +0 -5
  8. autogluon/timeseries/models/presets.py +0 -3
  9. autogluon/timeseries/regressor.py +54 -6
  10. autogluon/timeseries/transforms/__init__.py +2 -13
  11. autogluon/timeseries/transforms/covariate_scaler.py +28 -34
  12. autogluon/timeseries/transforms/target_scaler.py +22 -5
  13. autogluon/timeseries/version.py +1 -1
  14. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/METADATA +4 -4
  15. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/RECORD +22 -21
  16. /autogluon.timeseries-1.2.1b20250304-py3.9-nspkg.pth → /autogluon.timeseries-1.2.1b20250306-py3.9-nspkg.pth +0 -0
  17. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/LICENSE +0 -0
  18. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/NOTICE +0 -0
  19. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/WHEEL +0 -0
  20. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/namespace_packages.txt +0 -0
  21. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/top_level.txt +0 -0
  22. {autogluon.timeseries-1.2.1b20250304.dist-info → autogluon.timeseries-1.2.1b20250306.dist-info}/zip-safe +0 -0
@@ -0,0 +1,189 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+ import time
6
+ from abc import ABC, abstractmethod
7
+ from contextlib import nullcontext
8
+ from typing import Any, Dict, Optional, Tuple, Union
9
+
10
+ from typing_extensions import Self
11
+
12
+ from autogluon.common.savers import save_pkl
13
+ from autogluon.common.utils.distribute_utils import DistributedContext
14
+ from autogluon.common.utils.log_utils import DuplicateFilter
15
+ from autogluon.common.utils.try_import import try_import_ray
16
+ from autogluon.core.hpo.constants import CUSTOM_BACKEND, RAY_BACKEND
17
+ from autogluon.core.hpo.exceptions import EmptySearchSpace
18
+ from autogluon.core.hpo.executors import HpoExecutor, HpoExecutorFactory, RayHpoExecutor
19
+ from autogluon.core.models import Tunable
20
+ from autogluon.timeseries.dataset import TimeSeriesDataFrame
21
+ from autogluon.timeseries.utils.warning_filters import disable_stdout, warning_filter
22
+
23
+ from .model_trial import model_trial, skip_hpo
24
+
25
+ logger = logging.getLogger(__name__)
26
+ dup_filter = DuplicateFilter()
27
+ logger.addFilter(dup_filter)
28
+
29
+
30
+ class TimeSeriesTunable(Tunable, ABC):
31
+ @abstractmethod
32
+ def __init__(self) -> None:
33
+ self.name: str
34
+ self.path: str
35
+ self.path_root: str
36
+
37
+ def hyperparameter_tune(
38
+ self,
39
+ train_data: TimeSeriesDataFrame,
40
+ val_data: Optional[TimeSeriesDataFrame],
41
+ val_splitter: Any = None,
42
+ default_num_trials: Optional[int] = 1,
43
+ refit_every_n_windows: Optional[int] = 1,
44
+ hyperparameter_tune_kwargs: Union[str, dict] = "auto",
45
+ time_limit: Optional[float] = None,
46
+ ) -> Tuple[Dict[str, Any], Any]:
47
+ hpo_executor = self._get_default_hpo_executor()
48
+ hpo_executor.initialize(
49
+ hyperparameter_tune_kwargs, default_num_trials=default_num_trials, time_limit=time_limit
50
+ )
51
+
52
+ # we use k_fold=1 to circumvent autogluon.core logic to manage resources during parallelization
53
+ # of different folds
54
+ # FIXME: we pass in self which currently does not inherit from AbstractModel
55
+ hpo_executor.register_resources(self, k_fold=1, **self._get_system_resources()) # type: ignore
56
+
57
+ time_start = time.time()
58
+ logger.debug(f"\tStarting hyperparameter tuning for {self.name}")
59
+ search_space = self._get_search_space()
60
+
61
+ try:
62
+ hpo_executor.validate_search_space(search_space, self.name)
63
+ except EmptySearchSpace:
64
+ return skip_hpo(self, train_data, val_data, time_limit=hpo_executor.time_limit)
65
+
66
+ train_path, val_path = self._save_with_data(train_data, val_data)
67
+
68
+ train_fn_kwargs = self._get_hpo_train_fn_kwargs(
69
+ model_cls=self.__class__,
70
+ init_params=self.get_params(),
71
+ time_start=time_start,
72
+ time_limit=hpo_executor.time_limit,
73
+ fit_kwargs=dict(
74
+ val_splitter=val_splitter,
75
+ refit_every_n_windows=refit_every_n_windows,
76
+ ),
77
+ train_path=train_path,
78
+ val_path=val_path,
79
+ hpo_executor=hpo_executor,
80
+ )
81
+
82
+ minimum_resources = self.get_minimum_resources(is_gpu_available=self._is_gpu_available())
83
+ hpo_context = disable_stdout if isinstance(hpo_executor, RayHpoExecutor) else nullcontext
84
+
85
+ minimum_cpu_per_trial = minimum_resources.get("num_cpus", 1)
86
+ if not isinstance(minimum_cpu_per_trial, int):
87
+ logger.warning(
88
+ f"Minimum number of CPUs per trial for {self.name} is not an integer. "
89
+ f"Setting to 1. Minimum number of CPUs per trial: {minimum_cpu_per_trial}"
90
+ )
91
+ minimum_cpu_per_trial = 1
92
+
93
+ with hpo_context(), warning_filter(): # prevent Ray from outputting its results to stdout with print
94
+ hpo_executor.execute(
95
+ model_trial=model_trial,
96
+ train_fn_kwargs=train_fn_kwargs,
97
+ directory=self.path,
98
+ minimum_cpu_per_trial=minimum_cpu_per_trial,
99
+ minimum_gpu_per_trial=minimum_resources.get("num_gpus", 0),
100
+ model_estimate_memory_usage=None, # type: ignore
101
+ adapter_type="timeseries",
102
+ )
103
+
104
+ assert self.path_root is not None
105
+ hpo_models, analysis = hpo_executor.get_hpo_results(
106
+ model_name=self.name,
107
+ model_path_root=self.path_root,
108
+ time_start=time_start,
109
+ )
110
+
111
+ return hpo_models, analysis
112
+
113
+ def _get_default_hpo_executor(self) -> HpoExecutor:
114
+ backend = (
115
+ self._get_model_base()._get_hpo_backend()
116
+ ) # If ensemble, will use the base model to determine backend
117
+ if backend == RAY_BACKEND:
118
+ try:
119
+ try_import_ray()
120
+ except Exception as e:
121
+ warning_msg = f"Will use custom hpo logic because ray import failed. Reason: {str(e)}"
122
+ dup_filter.attach_filter_targets(warning_msg)
123
+ logger.warning(warning_msg)
124
+ backend = CUSTOM_BACKEND
125
+ hpo_executor = HpoExecutorFactory.get_hpo_executor(backend)() # type: ignore
126
+ return hpo_executor
127
+
128
+ def _get_hpo_backend(self) -> str:
129
+ """Choose which backend("ray" or "custom") to use for hpo"""
130
+ if DistributedContext.is_distributed_mode():
131
+ return RAY_BACKEND
132
+ return CUSTOM_BACKEND
133
+
134
+ def _get_hpo_train_fn_kwargs(self, **train_fn_kwargs) -> dict:
135
+ """Update kwargs passed to model_trial depending on the model configuration.
136
+
137
+ These kwargs need to be updated, for example, by MultiWindowBacktestingModel.
138
+ """
139
+ return train_fn_kwargs
140
+
141
+ def estimate_memory_usage(self, *args, **kwargs) -> float | None:
142
+ """Return the estimated memory usage of the model. None if memory usage cannot be
143
+ estimated.
144
+ """
145
+ return None
146
+
147
+ def get_minimum_resources(self, is_gpu_available: bool = False) -> Dict[str, Union[int, float]]:
148
+ return {
149
+ "num_cpus": 1,
150
+ }
151
+
152
+ def _save_with_data(
153
+ self, train_data: TimeSeriesDataFrame, val_data: Optional[TimeSeriesDataFrame]
154
+ ) -> Tuple[str, str]:
155
+ self.path = os.path.abspath(self.path)
156
+ self.path_root = self.path.rsplit(self.name, 1)[0]
157
+
158
+ dataset_train_filename = "dataset_train.pkl"
159
+ train_path = os.path.join(self.path, dataset_train_filename)
160
+ save_pkl.save(path=train_path, object=train_data)
161
+
162
+ dataset_val_filename = "dataset_val.pkl"
163
+ val_path = os.path.join(self.path, dataset_val_filename)
164
+ save_pkl.save(path=val_path, object=val_data)
165
+ return train_path, val_path
166
+
167
+ @abstractmethod
168
+ def _get_model_base(self) -> Self:
169
+ pass
170
+
171
+ @abstractmethod
172
+ def _is_gpu_available(self) -> bool:
173
+ pass
174
+
175
+ @abstractmethod
176
+ def _get_search_space(self) -> Dict[str, Any]:
177
+ pass
178
+
179
+ @abstractmethod
180
+ def get_params(self) -> dict:
181
+ """Return a clean copy of constructor parameters that can be used to
182
+ clone the current model.
183
+ """
184
+ pass
185
+
186
+ @staticmethod
187
+ @abstractmethod
188
+ def _get_system_resources() -> Dict[str, Any]:
189
+ pass
@@ -88,6 +88,9 @@ class AbstractMLForecastModel(AbstractTimeSeriesModel):
88
88
  self._train_target_median: Optional[float] = None
89
89
  self._non_boolean_real_covariates: List[str] = []
90
90
 
91
+ # Do not create a scaler in the model, scaler will be passed to MLForecast
92
+ self.target_scaler = None
93
+
91
94
  @property
92
95
  def tabular_predictor_path(self) -> str:
93
96
  return os.path.join(self.path, "tabular_predictor")
@@ -420,10 +423,6 @@ class AbstractMLForecastModel(AbstractTimeSeriesModel):
420
423
  def _more_tags(self) -> dict:
421
424
  return {"allow_nan": True, "can_refit_full": True}
422
425
 
423
- def _create_target_scaler(self):
424
- # Do not create a scaler in the model, scaler will be passed to MLForecast
425
- return None
426
-
427
426
 
428
427
  class DirectTabularModel(AbstractMLForecastModel):
429
428
  """Predict all future time series values simultaneously using TabularPredictor from AutoGluon-Tabular.
@@ -13,7 +13,7 @@ from autogluon.timeseries.dataset.ts_dataframe import (
13
13
  TIMESTAMP,
14
14
  TimeSeriesDataFrame,
15
15
  )
16
- from autogluon.timeseries.transforms.target_scaler import LocalTargetScaler, get_target_scaler_from_name
16
+ from autogluon.timeseries.transforms.target_scaler import LocalTargetScaler, get_target_scaler
17
17
 
18
18
  from .utils import MLF_ITEMID, MLF_TIMESTAMP
19
19
 
@@ -31,7 +31,7 @@ class MLForecastScaler(BaseTargetTransform):
31
31
  return pd.DataFrame(ts_df).reset_index().rename(columns={ITEMID: self.id_col, TIMESTAMP: self.time_col})
32
32
 
33
33
  def fit_transform(self, df: pd.DataFrame) -> pd.DataFrame:
34
- self.ag_scaler = get_target_scaler_from_name(name=self.scaler_type, target=self.target_col)
34
+ self.ag_scaler = get_target_scaler(name=self.scaler_type, target=self.target_col)
35
35
  transformed = self.ag_scaler.fit_transform(self._df_to_tsdf(df)).reset_index()
36
36
  return self._tsdf_to_df(transformed)
37
37
 
@@ -76,3 +76,11 @@ class AbstractTimeSeriesEnsembleModel(AbstractTimeSeriesModel):
76
76
  This method should be called after performing refit_full to point to the refitted base models, if necessary.
77
77
  """
78
78
  raise NotImplementedError
79
+
80
+ # TODO: remove
81
+ def _fit(*args, **kwargs):
82
+ pass
83
+
84
+ # TODO: remove
85
+ def _predict(*args, **kwargs):
86
+ pass
@@ -101,7 +101,9 @@ class TimeSeriesEnsembleSelection(EnsembleSelection):
101
101
  class TimeSeriesGreedyEnsemble(AbstractTimeSeriesEnsembleModel):
102
102
  """Constructs a weighted ensemble using the greedy Ensemble Selection algorithm."""
103
103
 
104
- def __init__(self, name: str, ensemble_size: int = 100, **kwargs):
104
+ def __init__(self, name: Optional[str] = None, ensemble_size: int = 100, **kwargs):
105
+ if name is None:
106
+ name = "WeightedEnsemble"
105
107
  super().__init__(name=name, **kwargs)
106
108
  self.ensemble_size = ensemble_size
107
109
  self.model_to_weight: Dict[str, float] = {}
@@ -144,7 +146,7 @@ class TimeSeriesGreedyEnsemble(AbstractTimeSeriesEnsembleModel):
144
146
  return np.array(list(self.model_to_weight.values()), dtype=np.float64)
145
147
 
146
148
  def predict(self, data: Dict[str, Optional[TimeSeriesDataFrame]], **kwargs) -> TimeSeriesDataFrame:
147
- if set(data.keys()) != set(self.model_names):
149
+ if not set(self.model_names).issubset(set(data.keys())):
148
150
  raise ValueError(
149
151
  f"Set of models given for prediction in {self.name} differ from those provided during initialization."
150
152
  )
@@ -222,11 +222,6 @@ class MultiWindowBacktestingModel(AbstractTimeSeriesModel):
222
222
  # Do not initialize the target_scaler and covariate_regressor in the multi window model!
223
223
  pass
224
224
 
225
- def initialize(self, **kwargs) -> dict:
226
- super().initialize(**kwargs)
227
- self.model_base.initialize(**kwargs)
228
- return kwargs
229
-
230
225
  def _get_hpo_train_fn_kwargs(self, **train_fn_kwargs) -> dict:
231
226
  train_fn_kwargs["is_bagged_model"] = True
232
227
  train_fn_kwargs["init_params"]["model_base"] = self.model_base.__class__
@@ -235,9 +235,6 @@ def get_preset_models(
235
235
  "is present in `excluded_model_types` and will be removed."
236
236
  )
237
237
  continue
238
- if "mxnet" in model.lower():
239
- logger.info(f"\tMXNet model '{model}' given in `hyperparameters` is deprecated and won't be trained. ")
240
- continue
241
238
  model_type = MODEL_TYPES[model]
242
239
  elif isinstance(model, type):
243
240
  if not issubclass(model, AbstractTimeSeriesModel):
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import time
3
- from typing import Any, Dict, Optional
3
+ from typing import Any, Dict, Optional, Protocol, Union, overload, runtime_checkable
4
4
 
5
5
  import numpy as np
6
6
  import pandas as pd
@@ -13,7 +13,27 @@ from autogluon.timeseries.utils.features import CovariateMetadata
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
- class CovariateRegressor:
16
+ @runtime_checkable
17
+ class CovariateRegressor(Protocol):
18
+ def is_fit(self) -> bool: ...
19
+
20
+ def fit(self, data: TimeSeriesDataFrame, time_limit: Optional[float] = None, **kwargs) -> "CovariateRegressor": ...
21
+
22
+ def transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame: ...
23
+
24
+ def fit_transform(
25
+ self, data: TimeSeriesDataFrame, time_limit: Optional[float] = None, **kwargs
26
+ ) -> TimeSeriesDataFrame: ...
27
+
28
+ def inverse_transform(
29
+ self,
30
+ predictions: TimeSeriesDataFrame,
31
+ known_covariates: TimeSeriesDataFrame,
32
+ static_features: Optional[pd.DataFrame],
33
+ ) -> TimeSeriesDataFrame: ...
34
+
35
+
36
+ class GlobalCovariateRegressor(CovariateRegressor):
17
37
  """Predicts target values from the covariates for the same observation.
18
38
 
19
39
  The model construct the feature matrix using known_covariates and static_features.
@@ -33,7 +53,7 @@ class CovariateRegressor:
33
53
  `transform`.
34
54
  max_num_samples : int or None
35
55
  If not None, training dataset passed to regression model will contain at most this many rows.
36
- metadata : CovariateMetadata
56
+ covariate_metadata : CovariateMetadata
37
57
  Metadata object describing the covariates available in the dataset.
38
58
  target : str
39
59
  Name of the target column.
@@ -58,7 +78,7 @@ class CovariateRegressor:
58
78
  eval_metric: str = "mean_absolute_error",
59
79
  refit_during_predict: bool = False,
60
80
  max_num_samples: Optional[int] = 500_000,
61
- metadata: Optional[CovariateMetadata] = None,
81
+ covariate_metadata: Optional[CovariateMetadata] = None,
62
82
  target: str = "target",
63
83
  validation_fraction: Optional[float] = 0.1,
64
84
  fit_time_fraction: float = 0.5,
@@ -83,7 +103,7 @@ class CovariateRegressor:
83
103
 
84
104
  self.model: Optional[AbstractModel] = None
85
105
  self.disabled = False
86
- self.metadata = metadata or CovariateMetadata()
106
+ self.covariate_metadata = covariate_metadata or CovariateMetadata()
87
107
 
88
108
  def is_fit(self) -> bool:
89
109
  return self.model is not None
@@ -188,7 +208,7 @@ class CovariateRegressor:
188
208
  include_target: bool = False,
189
209
  ) -> pd.DataFrame:
190
210
  """Construct a tabular dataframe from known covariates and static features."""
191
- available_columns = [ITEMID] + self.metadata.known_covariates
211
+ available_columns = [ITEMID] + self.covariate_metadata.known_covariates
192
212
  if include_target:
193
213
  available_columns += [self.target]
194
214
  tabular_df = pd.DataFrame(data).reset_index()[available_columns].astype({ITEMID: "category"})
@@ -201,3 +221,31 @@ class CovariateRegressor:
201
221
  if self.max_num_samples is not None and len(df) > self.max_num_samples:
202
222
  df = df.sample(n=self.max_num_samples)
203
223
  return df
224
+
225
+
226
+ @overload
227
+ def get_covariate_regressor(covariate_regressor: None, target: str, covariate_metadata: CovariateMetadata) -> None: ...
228
+ @overload
229
+ def get_covariate_regressor(
230
+ covariate_regressor: Union[str, dict], target: str, covariate_metadata: CovariateMetadata
231
+ ) -> CovariateRegressor: ...
232
+ def get_covariate_regressor(
233
+ covariate_regressor: Optional[Union[str, dict]], target: str, covariate_metadata: CovariateMetadata
234
+ ) -> Optional[CovariateRegressor]:
235
+ """Create a CovariateRegressor object based on the value of the `covariate_regressor` hyperparameter."""
236
+ if covariate_regressor is None:
237
+ return None
238
+ elif len(covariate_metadata.known_covariates + covariate_metadata.static_features) == 0:
239
+ logger.info("\tSkipping covariate_regressor since the dataset contains no covariates or static features.")
240
+ return None
241
+ else:
242
+ if isinstance(covariate_regressor, str):
243
+ return GlobalCovariateRegressor(covariate_regressor, target=target, covariate_metadata=covariate_metadata)
244
+ elif isinstance(covariate_regressor, dict):
245
+ return GlobalCovariateRegressor(
246
+ **covariate_regressor, target=target, covariate_metadata=covariate_metadata
247
+ )
248
+ else:
249
+ raise ValueError(
250
+ f"Invalid value for covariate_regressor {covariate_regressor} of type {type(covariate_regressor)}"
251
+ )
@@ -1,13 +1,2 @@
1
- from .covariate_scaler import (
2
- CovariateScaler,
3
- GlobalCovariateScaler,
4
- get_covariate_scaler_from_name,
5
- )
6
- from .target_scaler import (
7
- LocalStandardScaler,
8
- LocalMinMaxScaler,
9
- LocalMeanAbsScaler,
10
- LocalRobustScaler,
11
- LocalTargetScaler,
12
- get_target_scaler_from_name
13
- )
1
+ from .covariate_scaler import CovariateScaler, get_covariate_scaler
2
+ from .target_scaler import TargetScaler, get_target_scaler
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Dict, List, Literal, Optional
2
+ from typing import Dict, List, Literal, Optional, Protocol, overload, runtime_checkable
3
3
 
4
4
  import numpy as np
5
5
  import pandas as pd
@@ -13,35 +13,20 @@ from autogluon.timeseries.utils.warning_filters import warning_filter
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
- class CovariateScaler:
16
+ @runtime_checkable
17
+ class CovariateScaler(Protocol):
17
18
  """Apply scaling to covariates and static features.
18
19
 
19
20
  This can be helpful for deep learning models that assume that the inputs are normalized.
20
21
  """
21
22
 
22
- def __init__(
23
- self,
24
- metadata: CovariateMetadata,
25
- use_known_covariates: bool = True,
26
- use_past_covariates: bool = True,
27
- use_static_features: bool = True,
28
- **kwargs,
29
- ):
30
- self.metadata = metadata
31
- self.use_known_covariates = use_known_covariates
32
- self.use_past_covariates = use_past_covariates
33
- self.use_static_features = use_static_features
34
-
35
- def fit_transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame:
36
- raise NotImplementedError
23
+ def fit_transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame: ...
37
24
 
38
- def transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame:
39
- raise NotImplementedError
25
+ def transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame: ...
40
26
 
41
27
  def transform_known_covariates(
42
28
  self, known_covariates: Optional[TimeSeriesDataFrame] = None
43
- ) -> Optional[TimeSeriesDataFrame]:
44
- raise NotImplementedError
29
+ ) -> Optional[TimeSeriesDataFrame]: ...
45
30
 
46
31
 
47
32
  class GlobalCovariateScaler(CovariateScaler):
@@ -57,13 +42,16 @@ class GlobalCovariateScaler(CovariateScaler):
57
42
 
58
43
  def __init__(
59
44
  self,
60
- metadata: CovariateMetadata,
45
+ covariate_metadata: CovariateMetadata,
61
46
  use_known_covariates: bool = True,
62
47
  use_past_covariates: bool = True,
63
48
  use_static_features: bool = True,
64
49
  skew_threshold: float = 0.99,
65
50
  ):
66
- super().__init__(metadata, use_known_covariates, use_past_covariates, use_static_features)
51
+ self.covariate_metadata = covariate_metadata
52
+ self.use_known_covariates = use_known_covariates
53
+ self.use_past_covariates = use_past_covariates
54
+ self.use_static_features = use_static_features
67
55
  self.skew_threshold = skew_threshold
68
56
  self._column_transformers: Optional[Dict[Literal["known", "past", "static"], ColumnTransformer]] = None
69
57
 
@@ -73,18 +61,18 @@ class GlobalCovariateScaler(CovariateScaler):
73
61
  def fit(self, data: TimeSeriesDataFrame) -> "GlobalCovariateScaler":
74
62
  self._column_transformers = {}
75
63
 
76
- if self.use_known_covariates and len(self.metadata.known_covariates_real) > 0:
64
+ if self.use_known_covariates and len(self.covariate_metadata.known_covariates_real) > 0:
77
65
  self._column_transformers["known"] = self._get_transformer_for_columns(
78
- data, columns=self.metadata.known_covariates_real
66
+ data, columns=self.covariate_metadata.known_covariates_real
79
67
  )
80
- if self.use_past_covariates and len(self.metadata.past_covariates_real) > 0:
68
+ if self.use_past_covariates and len(self.covariate_metadata.past_covariates_real) > 0:
81
69
  self._column_transformers["past"] = self._get_transformer_for_columns(
82
- data, columns=self.metadata.past_covariates_real
70
+ data, columns=self.covariate_metadata.past_covariates_real
83
71
  )
84
- if self.use_static_features and len(self.metadata.static_features_real) > 0:
72
+ if self.use_static_features and len(self.covariate_metadata.static_features_real) > 0:
85
73
  assert data.static_features is not None
86
74
  self._column_transformers["static"] = self._get_transformer_for_columns(
87
- data.static_features, columns=self.metadata.static_features_real
75
+ data.static_features, columns=self.covariate_metadata.static_features_real
88
76
  )
89
77
 
90
78
  return self
@@ -100,15 +88,15 @@ class GlobalCovariateScaler(CovariateScaler):
100
88
  assert self._column_transformers is not None, "CovariateScaler must be fit before transform can be called"
101
89
 
102
90
  if "known" in self._column_transformers:
103
- columns = self.metadata.known_covariates_real
91
+ columns = self.covariate_metadata.known_covariates_real
104
92
  data[columns] = self._column_transformers["known"].transform(data[columns])
105
93
 
106
94
  if "past" in self._column_transformers:
107
- columns = self.metadata.past_covariates_real
95
+ columns = self.covariate_metadata.past_covariates_real
108
96
  data[columns] = self._column_transformers["past"].transform(data[columns])
109
97
 
110
98
  if "static" in self._column_transformers:
111
- columns = self.metadata.static_features_real
99
+ columns = self.covariate_metadata.static_features_real
112
100
  assert data.static_features is not None
113
101
 
114
102
  data.static_features[columns] = self._column_transformers["static"].transform(
@@ -122,7 +110,7 @@ class GlobalCovariateScaler(CovariateScaler):
122
110
  assert self._column_transformers is not None, "CovariateScaler must be fit before transform can be called"
123
111
 
124
112
  if "known" in self._column_transformers:
125
- columns = self.metadata.known_covariates_real
113
+ columns = self.covariate_metadata.known_covariates_real
126
114
  assert known_covariates is not None
127
115
 
128
116
  known_covariates = known_covariates.copy()
@@ -162,7 +150,13 @@ AVAILABLE_COVARIATE_SCALERS = {
162
150
  }
163
151
 
164
152
 
165
- def get_covariate_scaler_from_name(name: Literal["global"], **scaler_kwargs) -> CovariateScaler:
153
+ @overload
154
+ def get_covariate_scaler(name: None, **scaler_kwargs) -> None: ...
155
+ @overload
156
+ def get_covariate_scaler(name: Literal["global"], **scaler_kwargs) -> GlobalCovariateScaler: ...
157
+ def get_covariate_scaler(name: Optional[Literal["global"]] = None, **scaler_kwargs) -> Optional[CovariateScaler]:
158
+ if name is None:
159
+ return None
166
160
  if name not in AVAILABLE_COVARIATE_SCALERS:
167
161
  raise KeyError(
168
162
  f"Covariate scaler type {name} not supported. Available scalers: {list(AVAILABLE_COVARIATE_SCALERS)}"
@@ -1,12 +1,23 @@
1
- from typing import Literal, Optional, Tuple, Union
1
+ from typing import Literal, Optional, Protocol, Tuple, Union, overload
2
2
 
3
3
  import numpy as np
4
4
  import pandas as pd
5
+ from typing_extensions import Self
5
6
 
6
7
  from autogluon.timeseries.dataset.ts_dataframe import ITEMID, TimeSeriesDataFrame
7
8
 
8
9
 
9
- class LocalTargetScaler:
10
+ class TargetScaler(Protocol):
11
+ def fit_transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame: ...
12
+
13
+ def fit(self, data: TimeSeriesDataFrame) -> Self: ...
14
+
15
+ def transform(self, data: TimeSeriesDataFrame) -> TimeSeriesDataFrame: ...
16
+
17
+ def inverse_transform(self, predictions: TimeSeriesDataFrame) -> TimeSeriesDataFrame: ...
18
+
19
+
20
+ class LocalTargetScaler(TargetScaler):
10
21
  """Applies an affine transformation (x - loc) / scale independently to each time series in the dataset."""
11
22
 
12
23
  def __init__(
@@ -123,10 +134,16 @@ AVAILABLE_TARGET_SCALERS = {
123
134
  }
124
135
 
125
136
 
126
- def get_target_scaler_from_name(
127
- name: Literal["standard", "mean_abs", "min_max", "robust"], **scaler_kwargs
128
- ) -> LocalTargetScaler:
137
+ @overload
138
+ def get_target_scaler(name: None, **scaler_kwargs) -> None: ...
139
+ @overload
140
+ def get_target_scaler(name: Literal["standard", "mean_abs", "min_max", "robust"], **scaler_kwargs) -> TargetScaler: ...
141
+ def get_target_scaler(
142
+ name: Optional[Literal["standard", "mean_abs", "min_max", "robust"]], **scaler_kwargs
143
+ ) -> Optional[TargetScaler]:
129
144
  """Get LocalTargetScaler object from a string."""
145
+ if name is None:
146
+ return None
130
147
  if name not in AVAILABLE_TARGET_SCALERS:
131
148
  raise KeyError(f"Scaler type {name} not supported. Available scalers: {list(AVAILABLE_TARGET_SCALERS)}")
132
149
  return AVAILABLE_TARGET_SCALERS[name](**scaler_kwargs)
@@ -1,4 +1,4 @@
1
1
  """This is the autogluon version file."""
2
2
 
3
- __version__ = "1.2.1b20250304"
3
+ __version__ = "1.2.1b20250306"
4
4
  __lite__ = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: autogluon.timeseries
3
- Version: 1.2.1b20250304
3
+ Version: 1.2.1b20250306
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,9 +55,9 @@ 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.1b20250304
59
- Requires-Dist: autogluon.common==1.2.1b20250304
60
- Requires-Dist: autogluon.tabular[catboost,lightgbm,xgboost]==1.2.1b20250304
58
+ Requires-Dist: autogluon.core[raytune]==1.2.1b20250306
59
+ Requires-Dist: autogluon.common==1.2.1b20250306
60
+ Requires-Dist: autogluon.tabular[catboost,lightgbm,xgboost]==1.2.1b20250306
61
61
  Provides-Extra: all
62
62
  Provides-Extra: chronos-onnx
63
63
  Requires-Dist: optimum[onnxruntime]<1.20,>=1.17; extra == "chronos-onnx"