Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions darts/models/forecasting/catboost_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __init__(
categorical_past_covariates: Optional[Union[str, list[str]]] = None,
categorical_future_covariates: Optional[Union[str, list[str]]] = None,
categorical_static_covariates: Optional[Union[str, list[str]]] = None,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""CatBoost Model
Expand Down Expand Up @@ -173,6 +174,10 @@ def encode_year(idx):
Optionally, string or list of strings specifying the static covariates that should be treated as categorical
by the underlying `CatBoostRegressor`. The components that
are specified as categorical must be integer-encoded.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `catboost.CatBoostRegressor`.
Native multi-output support can be achieved by using an appropriate `loss_function` ('MultiRMSE',
Expand Down Expand Up @@ -236,6 +241,7 @@ def encode_year(idx):
categorical_future_covariates=categorical_future_covariates,
categorical_static_covariates=categorical_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)

# if no loss provided, get the default loss from the model
Expand Down Expand Up @@ -477,6 +483,7 @@ def __init__(
categorical_past_covariates: Optional[Union[str, list[str]]] = None,
categorical_future_covariates: Optional[Union[str, list[str]]] = None,
categorical_static_covariates: Optional[Union[str, list[str]]] = None,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""CatBoost Model for classification forecasting
Expand Down Expand Up @@ -587,6 +594,10 @@ def encode_year(idx):
Optionally, string or list of strings specifying the static covariates that should be treated as categorical
by the underlying `CatBoostRegressor`. The components that
are specified as categorical must be integer-encoded.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `catboost.CatBoostClassifier`.

Expand Down Expand Up @@ -637,6 +648,7 @@ def encode_year(idx):
categorical_past_covariates=categorical_past_covariates,
categorical_future_covariates=categorical_future_covariates,
categorical_static_covariates=categorical_static_covariates,
dir_rec=dir_rec,
**kwargs,
)

Expand Down
12 changes: 12 additions & 0 deletions darts/models/forecasting/lgbm.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(
categorical_past_covariates: Optional[Union[str, list[str]]] = None,
categorical_future_covariates: Optional[Union[str, list[str]]] = None,
categorical_static_covariates: Optional[Union[str, list[str]]] = None,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""LGBM Model
Expand Down Expand Up @@ -165,6 +166,10 @@ def encode_year(idx):
Optionally, string or list of strings specifying the static covariates that should be treated as categorical
by the underlying `lightgbm.LightGBMRegressor`. The components that are specified as categorical
must be integer-encoded.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `lightgbm.LGBRegressor`.

Expand Down Expand Up @@ -222,6 +227,7 @@ def encode_year(idx):
categorical_future_covariates=categorical_future_covariates,
categorical_static_covariates=categorical_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)

@staticmethod
Expand Down Expand Up @@ -384,6 +390,7 @@ def __init__(
categorical_past_covariates: Optional[Union[str, list[str]]] = None,
categorical_future_covariates: Optional[Union[str, list[str]]] = None,
categorical_static_covariates: Optional[Union[str, list[str]]] = None,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""LGBM Model for classification forecasting
Expand Down Expand Up @@ -494,6 +501,10 @@ def encode_year(idx):
Optionally, string or list of strings specifying the static covariates that should be treated as categorical
by the underlying `lightgbm.LightGBMRegressor`. The components that are specified as categorical
must be integer-encoded.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `lightgbm.LGBClassifier`.

Expand Down Expand Up @@ -545,6 +556,7 @@ def encode_year(idx):
categorical_past_covariates=categorical_past_covariates,
categorical_future_covariates=categorical_future_covariates,
categorical_static_covariates=categorical_static_covariates,
dir_rec=dir_rec,
**kwargs,
)

Expand Down
6 changes: 6 additions & 0 deletions darts/models/forecasting/linear_regression_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(
random_state: Optional[int] = None,
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""Linear regression model.
Expand Down Expand Up @@ -140,6 +141,10 @@ def encode_year(idx):
Whether the model should use static covariate information in case the input `series` passed to ``fit()``
contain static covariates. If ``True``, and static covariates are available at fitting time, will enforce
that all target `series` have the same static covariate dimensionality in ``fit()`` and ``predict()``.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `sklearn.linear_model.LinearRegression` (by default), to
`sklearn.linear_model.PoissonRegressor` (if `likelihood="poisson"`), or to
Expand Down Expand Up @@ -204,6 +209,7 @@ def encode_year(idx):
multi_models=multi_models,
use_static_covariates=use_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)

def fit(
Expand Down
6 changes: 6 additions & 0 deletions darts/models/forecasting/random_forest.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
random_state: Optional[int] = None,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""Random Forest Model
Expand Down Expand Up @@ -139,6 +140,10 @@ def encode_year(idx):
that all target `series` have the same static covariate dimensionality in ``fit()`` and ``predict()``.
random_state
Controls the randomness for reproducible forecasting.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `sklearn.ensemble.RandomForestRegressor`.

Expand Down Expand Up @@ -190,6 +195,7 @@ def encode_year(idx):
model=RandomForestRegressor(**kwargs),
use_static_covariates=use_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)


Expand Down
64 changes: 57 additions & 7 deletions darts/models/forecasting/sklearn_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@
SKLearnLikelihood,
_get_likelihood,
)
from darts.utils.multioutput import MultiOutputMixin, get_multioutput_estimator_cls
from darts.utils.multioutput import (
MultiOutputMixin,
RecurrentMultiOutputMixin,
get_multioutput_estimator_cls,
get_recurrent_multioutput_estimator_cls,
)
from darts.utils.ts_utils import get_single_series, seq2series, series2seq
from darts.utils.utils import ModelType, random_method

Expand All @@ -119,6 +124,7 @@ def __init__(
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
random_state: Optional[int] = None,
dir_rec: Optional[bool] = False,
):
"""Regression Model
Can be used to fit any scikit-learn-like regressor class to predict the target time series from lagged values.
Expand Down Expand Up @@ -215,6 +221,10 @@ def encode_year(idx):
that all target `series` have the same static covariate dimensionality in ``fit()`` and ``predict()``.
random_state
Controls the randomness for reproducible forecasting.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.

Examples
--------
Expand Down Expand Up @@ -258,6 +268,7 @@ def encode_year(idx):
self._static_covariates_shape: Optional[tuple[int, int]] = None
self._lagged_feature_names: Optional[list[str]] = None
self._lagged_label_names: Optional[list[str]] = None
self.dir_rec = dir_rec

# optionally, the model can be wrapped in a likelihood model
self._likelihood: Optional[SKLearnLikelihood] = getattr(
Expand Down Expand Up @@ -730,8 +741,10 @@ def _add_val_set_to_kwargs(
last_static_covariates_shape=self._static_covariates_shape,
stride=stride,
)
# create validation sets for MultiOutputMixin
if val_labels.ndim == 2 and isinstance(self.model, MultiOutputMixin):
# create validation sets for MultiOutputMixin and RecurrentMultiOutputMixin
if val_labels.ndim == 2 and isinstance(
self.model, (MultiOutputMixin, RecurrentMultiOutputMixin)
):
val_sets, val_weights = [], []
for i in range(val_labels.shape[1]):
val_sets.append((val_samples, val_labels[:, i]))
Expand Down Expand Up @@ -1036,8 +1049,31 @@ def fit(
self.output_chunk_length > 1 and self.multi_models
)

# If multi-output required and model doesn't support it natively, wrap it in a MultiOutputMixin
if (
# If direct-recursive prediction is enabled, wrap it in RecurrentMultiOutputMixin
# Note: dir_rec ALWAYS requires wrapping (even if model supports native multi-output)
if self.dir_rec:
val_set_name, val_weight_name = self.val_set_params
mor_kwargs = {
"eval_set_name": val_set_name,
"eval_weight_name": val_weight_name,
}
mor_kwargs["output_chunk_length"] = self.output_chunk_length

if (
n_jobs_multioutput_wrapper is not None
and n_jobs_multioutput_wrapper != 1
):
logger.warning(
"Direct-recursive multi-output prediction is sequential by design. "
"`n_jobs_multioutput_wrapper` parameter will be ignored."
)

self.model = get_recurrent_multioutput_estimator_cls(self._model_type)(
estimator=self.model, **mor_kwargs
)

# Elif multi-output required and model doesn't support it natively, wrap it in a MultiOutputMixin
elif (
requires_multioutput
and not isinstance(self.model, MultiOutputMixin)
and (
Expand All @@ -1059,7 +1095,7 @@ def fit(
)

if (
not isinstance(self.model, MultiOutputMixin)
not isinstance(self.model, (MultiOutputMixin, RecurrentMultiOutputMixin))
and n_jobs_multioutput_wrapper is not None
):
logger.warning("Provided `n_jobs_multioutput_wrapper` wasn't used.")
Expand Down Expand Up @@ -1495,7 +1531,7 @@ def supports_sample_weight(self) -> bool:
"""Whether the model supports a validation set during training."""
return (
self.model.supports_sample_weight
if isinstance(self.model, MultiOutputMixin)
if isinstance(self.model, (MultiOutputMixin, RecurrentMultiOutputMixin))
else has_fit_parameter(self.model, "sample_weight")
)

Expand Down Expand Up @@ -1635,6 +1671,7 @@ def __init__(
categorical_future_covariates: Optional[Union[str, list[str]]] = None,
categorical_static_covariates: Optional[Union[str, list[str]]] = None,
random_state: Optional[int] = None,
dir_rec: Optional[bool] = False,
):
"""
Extension of `SKLearnModel` for regression models that support categorical features.
Expand Down Expand Up @@ -1738,6 +1775,10 @@ def encode_year(idx):
categorical.
random_state
Controls the randomness for reproducible forecasting.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
"""
super().__init__(
lags=lags,
Expand All @@ -1750,6 +1791,7 @@ def encode_year(idx):
multi_models=multi_models,
use_static_covariates=use_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)

if categorical_static_covariates is not None and not use_static_covariates:
Expand Down Expand Up @@ -1953,6 +1995,7 @@ def __init__(
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
random_state: Optional[int] = None,
dir_rec: Optional[bool] = False,
):
"""Regression Model
Can be used to fit any scikit-learn-like regressor class to predict the target time series from lagged values.
Expand Down Expand Up @@ -2052,6 +2095,10 @@ def encode_year(idx):
that all target `series` have the same static covariate dimensionality in ``fit()`` and ``predict()``.
random_state
Controls the randomness for reproducible forecasting.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.

Examples
--------
Expand Down Expand Up @@ -2100,6 +2147,7 @@ def encode_year(idx):
multi_models=multi_models,
use_static_covariates=use_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)


Expand Down Expand Up @@ -2210,6 +2258,7 @@ def __init__(
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
random_state: Optional[int] = None,
dir_rec: Optional[bool] = False,
):
"""SKLearn Classifier Model

Expand Down Expand Up @@ -2382,4 +2431,5 @@ def encode_year(idx):
multi_models=multi_models,
use_static_covariates=use_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)
12 changes: 12 additions & 0 deletions darts/models/forecasting/xgboost.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(
random_state: Optional[int] = None,
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""XGBoost Model
Expand Down Expand Up @@ -168,6 +169,10 @@ def encode_year(idx):
Whether the model should use static covariate information in case the input `series` passed to ``fit()``
contain static covariates. If ``True``, and static covariates are available at fitting time, will enforce
that all target `series` have the same static covariate dimensionality in ``fit()`` and ``predict()``.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `xgb.XGBRegressor`.

Expand Down Expand Up @@ -223,6 +228,7 @@ def encode_year(idx):
model=self._create_model(**self.kwargs),
use_static_covariates=use_static_covariates,
random_state=random_state,
dir_rec=dir_rec,
)

@staticmethod
Expand Down Expand Up @@ -383,6 +389,7 @@ def __init__(
random_state: Optional[int] = None,
multi_models: Optional[bool] = True,
use_static_covariates: bool = True,
dir_rec: Optional[bool] = False,
**kwargs,
):
"""XGBoost Model for classification forecasting
Expand Down Expand Up @@ -480,6 +487,10 @@ def encode_year(idx):
Whether the model should use static covariate information in case the input `series` passed to ``fit()``
contain static covariates. If ``True``, and static covariates are available at fitting time, will enforce
that all target `series` have the same static covariate dimensionality in ``fit()`` and ``predict()``.
dir_rec
Whether to use direct-recursive strategy for multi-step forecasting. When True, each forecast
horizon uses predictions from previous horizons as additional input features. This creates a
chained prediction where step t+2 uses the prediction for step t+1 as a feature. Default: False.
**kwargs
Additional keyword arguments passed to `xgb.XGBClassifier`.

Expand Down Expand Up @@ -526,6 +537,7 @@ def encode_year(idx):
random_state=random_state,
multi_models=multi_models,
use_static_covariates=use_static_covariates,
dir_rec=dir_rec,
**kwargs,
)

Expand Down
Loading
Loading