@@ -48,6 +48,7 @@ def __init__(
48
48
lags_past_covariates : Union [int , List [int ]] = None ,
49
49
lags_future_covariates : Union [Tuple [int , int ], List [int ]] = None ,
50
50
output_chunk_length : int = 1 ,
51
+ add_encoders : Optional [dict ] = None ,
51
52
model = None ,
52
53
):
53
54
"""Regression Model
@@ -71,14 +72,34 @@ def __init__(
71
72
Number of time steps predicted at once by the internal regression model. Does not have to equal the forecast
72
73
horizon `n` used in `predict()`. However, setting `output_chunk_length` equal to the forecast horizon may
73
74
be useful if the covariates don't extend far enough into the future.
75
+ add_encoders
76
+ A large number of past and future covariates can be automatically generated with `add_encoders`.
77
+ This can be done by adding multiple pre-defined index encoders and/or custom user-made functions that
78
+ will be used as index encoders. Additionally, a transformer such as Darts' :class:`Scaler` can be added to
79
+ transform the generated covariates. This happens all under one hood and only needs to be specified at
80
+ model creation.
81
+ Read :meth:`SequentialEncoder <darts.utils.data.encoders.SequentialEncoder>` to find out more about
82
+ ``add_encoders``. Default: ``None``. An example showing some of ``add_encoders`` features:
83
+
84
+ .. highlight:: python
85
+ .. code-block:: python
86
+
87
+ add_encoders={
88
+ 'cyclic': {'future': ['month']},
89
+ 'datetime_attribute': {'future': ['hour', 'dayofweek']},
90
+ 'position': {'past': ['absolute'], 'future': ['relative']},
91
+ 'custom': {'past': [lambda idx: (idx.year - 1950) / 50]},
92
+ 'transformer': Scaler()
93
+ }
94
+ ..
74
95
model
75
96
Scikit-learn-like model with ``fit()`` and ``predict()`` methods. Also possible to use model that doesn't
76
97
support multi-output regression for multivariate timeseries, in which case one regressor
77
98
will be used per component in the multivariate series.
78
99
If None, defaults to: ``sklearn.linear_model.LinearRegression(n_jobs=-1)``.
79
100
"""
80
101
81
- super ().__init__ ()
102
+ super ().__init__ (add_encoders = add_encoders )
82
103
83
104
self .model = model
84
105
self .lags = {}
@@ -200,6 +221,46 @@ def __init__(
200
221
)
201
222
self .output_chunk_length = output_chunk_length
202
223
224
+ @property
225
+ def _model_encoder_settings (self ) -> Tuple [int , int , bool , bool ]:
226
+ lags_covariates = {
227
+ lag for key in ["past" , "future" ] for lag in self .lags .get (key , [])
228
+ }
229
+ if lags_covariates :
230
+ # for lags < 0 we need to take `n` steps backwards from past and/or historic future covariates
231
+ # for minimum lag = -1 -> steps_back_inclusive = 1
232
+ # inclusive means n steps back including the end of the target series
233
+ n_steps_back_inclusive = abs (min (min (lags_covariates ), 0 ))
234
+ # for lags >= 0 we need to take `n` steps ahead from future covariates
235
+ # for maximum lag = 0 -> output_chunk_length = 1
236
+ # exclusive means n steps ahead after the last step of the target series
237
+ n_steps_ahead_exclusive = max (max (lags_covariates ), 0 ) + 1
238
+ takes_past_covariates = "past" in self .lags
239
+ takes_future_covariates = "future" in self .lags
240
+ else :
241
+ n_steps_back_inclusive = 0
242
+ n_steps_ahead_exclusive = 0
243
+ takes_past_covariates = False
244
+ takes_future_covariates = False
245
+ return (
246
+ n_steps_back_inclusive ,
247
+ n_steps_ahead_exclusive ,
248
+ takes_past_covariates ,
249
+ takes_future_covariates ,
250
+ )
251
+
252
+ def _get_encoders_n (self , n ):
253
+ """Returns the `n` encoder prediction steps specific to RegressionModels.
254
+ This will generate slightly more past covariates than the minimum requirement when using past and future
255
+ covariate lags simultaneously. This is because encoders were written for TorchForecastingModels where we only
256
+ needed `n` future covariates. For RegressionModel we need `n + max_future_lag`
257
+ """
258
+ _ , n_steps_ahead , _ , takes_future_covariates = self ._model_encoder_settings
259
+ if not takes_future_covariates :
260
+ return n
261
+ else :
262
+ return n + (n_steps_ahead - 1 )
263
+
203
264
@property
204
265
def min_train_series_length (self ) -> int :
205
266
return max (
@@ -319,6 +380,7 @@ def _fit_model(
319
380
Function that fit the model. Deriving classes can override this method for adding additional parameters (e.g.,
320
381
adding validation data), keeping the sanity checks on series performed by fit().
321
382
"""
383
+
322
384
training_samples , training_labels = self ._create_lagged_data (
323
385
target_series , past_covariates , future_covariates , max_samples_per_ts
324
386
)
@@ -361,6 +423,15 @@ def fit(
361
423
**kwargs
362
424
Additional keyword arguments passed to the `fit` method of the model.
363
425
"""
426
+
427
+ self .encoders = self .initialize_encoders ()
428
+ if self .encoders .encoding_available :
429
+ past_covariates , future_covariates = self .encoders .encode_train (
430
+ target = series ,
431
+ past_covariate = past_covariates ,
432
+ future_covariate = future_covariates ,
433
+ )
434
+
364
435
super ().fit (
365
436
series = series ,
366
437
past_covariates = past_covariates ,
@@ -477,6 +548,14 @@ def predict(
477
548
logger ,
478
549
)
479
550
551
+ if self .encoders .encoding_available :
552
+ past_covariates , future_covariates = self .encoders .encode_inference (
553
+ n = self ._get_encoders_n (n ),
554
+ target = series ,
555
+ past_covariate = past_covariates ,
556
+ future_covariate = future_covariates ,
557
+ )
558
+
480
559
super ().predict (n , series , past_covariates , future_covariates , num_samples )
481
560
482
561
if series is None :
0 commit comments