@@ -1083,7 +1083,7 @@ class DualCovariatesForecastingModel(ForecastingModel, ABC):
1083
1083
Among other things, it lets Darts forecasting models wrap around statsmodels models
1084
1084
having a `future_covariates` parameter, which corresponds to future-known covariates.
1085
1085
1086
- All implementations have to implement the `fit ()` and `predict ()` methods defined below.
1086
+ All implementations have to implement the `_fit ()` and `_predict ()` methods defined below.
1087
1087
"""
1088
1088
1089
1089
_expect_covariate = False
@@ -1137,6 +1137,7 @@ def predict(
1137
1137
n : int ,
1138
1138
future_covariates : Optional [TimeSeries ] = None ,
1139
1139
num_samples : int = 1 ,
1140
+ ** kwargs ,
1140
1141
) -> TimeSeries :
1141
1142
"""Forecasts values for `n` time steps after the end of the training series.
1142
1143
@@ -1159,8 +1160,7 @@ def predict(
1159
1160
TimeSeries, a single time series containing the `n` next points after then end of the training series.
1160
1161
"""
1161
1162
1162
- if future_covariates is None :
1163
- super ().predict (n , num_samples )
1163
+ super ().predict (n , num_samples )
1164
1164
1165
1165
if self ._expect_covariate and future_covariates is None :
1166
1166
raise_log (
@@ -1170,6 +1170,12 @@ def predict(
1170
1170
)
1171
1171
)
1172
1172
1173
+ raise_if (
1174
+ not self ._expect_covariate and future_covariates is not None ,
1175
+ "The model has been trained without `future_covariates` variable, but the "
1176
+ "`future_covariates` parameter provided to `predict()` is not None." ,
1177
+ )
1178
+
1173
1179
if future_covariates is not None :
1174
1180
start = self .training_series .end_time () + self .training_series .freq
1175
1181
@@ -1194,13 +1200,13 @@ def predict(
1194
1200
]
1195
1201
1196
1202
raise_if_not (
1197
- len (future_covariates ) == n and self . _expect_covariate ,
1203
+ len (future_covariates ) == n ,
1198
1204
invalid_time_span_error ,
1199
1205
logger ,
1200
1206
)
1201
1207
1202
1208
return self ._predict (
1203
- n , future_covariates = future_covariates , num_samples = num_samples
1209
+ n , future_covariates = future_covariates , num_samples = num_samples , ** kwargs
1204
1210
)
1205
1211
1206
1212
@abstractmethod
@@ -1234,3 +1240,132 @@ def _predict_wrapper(
1234
1240
return self .predict (
1235
1241
n , future_covariates = future_covariates , num_samples = num_samples
1236
1242
)
1243
+
1244
+
1245
+ class TransferableDualCovariatesForecastingModel (DualCovariatesForecastingModel , ABC ):
1246
+ """The base class for the forecasting models that are not global, but support future covariates, and can
1247
+ additionally be applied to new data unrelated to the original series used for fitting the model. Currently,
1248
+ all the derived classes wrap statsmodels models.
1249
+
1250
+ All implementations have to implement the `_fit()`, `_predict()` methods.
1251
+ """
1252
+
1253
+ def predict (
1254
+ self ,
1255
+ n : int ,
1256
+ series : Optional [TimeSeries ] = None ,
1257
+ future_covariates : Optional [TimeSeries ] = None ,
1258
+ num_samples : int = 1 ,
1259
+ ** kwargs ,
1260
+ ) -> TimeSeries :
1261
+ """If the `series` parameter is not set, forecasts values for `n` time steps after the end of the training
1262
+ series. If some future covariates were specified during the training, they must also be specified here.
1263
+
1264
+ If the `series` parameter is set, forecasts values for `n` time steps after the end of the new target
1265
+ series. If some future covariates were specified during the training, they must also be specified here.
1266
+
1267
+ Parameters
1268
+ ----------
1269
+ n
1270
+ Forecast horizon - the number of time steps after the end of the series for which to produce predictions.
1271
+ series
1272
+ Optionally, a new target series whose future values will be predicted. Defaults to `None`, meaning that the
1273
+ model will forecast the future value of the training series.
1274
+ future_covariates
1275
+ The time series of future-known covariates which can be fed as input to the model. It must correspond to
1276
+ the covariate time series that has been used with the :func:`fit()` method for training.
1277
+
1278
+ If `series` is not set, it must contain at least the next `n` time steps/indices after the end of the
1279
+ training target series. If `series` is set, it must contain at least the time steps/indices corresponding
1280
+ to the new target series (historic future covariates), plus the next `n` time steps/indices after the end.
1281
+ num_samples
1282
+ Number of times a prediction is sampled from a probabilistic model. Should be left set to 1
1283
+ for deterministic models.
1284
+
1285
+ Returns
1286
+ -------
1287
+ TimeSeries, a single time series containing the `n` next points after then end of the training series.
1288
+ """
1289
+
1290
+ if self ._expect_covariate and future_covariates is None :
1291
+ raise_log (
1292
+ ValueError (
1293
+ "The model has been trained with `future_covariates` variable. Some matching "
1294
+ "`future_covariates` variables have to be provided to `predict()`."
1295
+ )
1296
+ )
1297
+
1298
+ historic_future_covariates = None
1299
+
1300
+ if series is not None and future_covariates :
1301
+ raise_if_not (
1302
+ future_covariates .start_time () <= series .start_time ()
1303
+ and future_covariates .end_time () >= series .end_time () + n * series .freq ,
1304
+ "The provided `future_covariates` related to the new target series must contain at least the same time"
1305
+ "steps/indices as the target `series` + `n`." ,
1306
+ logger ,
1307
+ )
1308
+ # splitting the future covariates
1309
+ (
1310
+ historic_future_covariates ,
1311
+ future_covariates ,
1312
+ ) = future_covariates .split_after (series .end_time ())
1313
+
1314
+ # in case future covariate have more values on the left end side that we don't need
1315
+ if not series .has_same_time_as (historic_future_covariates ):
1316
+ historic_future_covariates = historic_future_covariates .slice_intersect (
1317
+ series
1318
+ )
1319
+
1320
+ # DualCovariatesForecastingModel performs some checks on self.training_series. We temporary replace that with
1321
+ # the new ts
1322
+ if series is not None :
1323
+ self ._orig_training_series = self .training_series
1324
+ self .training_series = series
1325
+
1326
+ result = super ().predict (
1327
+ n = n ,
1328
+ series = series ,
1329
+ historic_future_covariates = historic_future_covariates ,
1330
+ future_covariates = future_covariates ,
1331
+ num_samples = num_samples ,
1332
+ ** kwargs ,
1333
+ )
1334
+
1335
+ # restoring the original training ts
1336
+ if series is not None :
1337
+ self .training_series = self ._orig_training_series
1338
+
1339
+ return result
1340
+
1341
+ @abstractmethod
1342
+ def _predict (
1343
+ self ,
1344
+ n : int ,
1345
+ series : Optional [TimeSeries ] = None ,
1346
+ historic_future_covariates : Optional [TimeSeries ] = None ,
1347
+ future_covariates : Optional [TimeSeries ] = None ,
1348
+ num_samples : int = 1 ,
1349
+ ) -> TimeSeries :
1350
+ """Forecasts values for a certain number of time steps after the end of the series.
1351
+ TransferableDualCovariatesForecastingModel must implement the predict logic in this method.
1352
+ """
1353
+ pass
1354
+
1355
+ def _predict_wrapper (
1356
+ self ,
1357
+ n : int ,
1358
+ series : TimeSeries ,
1359
+ past_covariates : Optional [TimeSeries ],
1360
+ future_covariates : Optional [TimeSeries ],
1361
+ num_samples : int ,
1362
+ ) -> TimeSeries :
1363
+ return self .predict (
1364
+ n = n ,
1365
+ series = series ,
1366
+ future_covariates = future_covariates ,
1367
+ num_samples = num_samples ,
1368
+ )
1369
+
1370
+ def _supports_non_retrainable_historical_forecasts (self ) -> bool :
1371
+ return True
0 commit comments