Full-featured base forecasting model with Bayesian tuning and SHAP.
This module extends ForecasterRecursiveModel from spotforecast2-safe with real Bayesian hyperparameter search (Optuna) and SHAP-based feature importance (shap.TreeExplainer).
Examples
from spotforecast2.models import ForecasterRecursiveModelFullmodel = ForecasterRecursiveModelFull(iteration=0)asserthasattr(model, "tune")asserthasattr(model, "get_global_shap_feature_importance")print(f"n_trials={model.n_trials}, iteration={model.iteration}")
How tune() should treat NaN rows when calling load_timeseries(). Defaults to "passthrough" since the next step (LinearlyInterpolateTS) imputes the gaps.
import numpy as npimport pandas as pdfrom spotforecast2.models import ForecasterRecursiveLGBMFullrng = np.random.default_rng(0)y = pd.Series( rng.random(30), index=pd.date_range("2023-01-01", periods=30, freq="h", tz="UTC"), name="load",)# Untuned: best_params is None so an empty Series is returned.model_untuned = ForecasterRecursiveLGBMFull(iteration=0, lags=3)model_untuned.forecaster.fit(y=y)X_train_u, y_train_u = model_untuned.forecaster.create_train_X_y(y=y)model_untuned._get_training_data =lambda: (X_train_u, y_train_u)result_empty = model_untuned.get_global_shap_feature_importance()assertisinstance(result_empty, pd.Series)assertlen(result_empty) ==0print(f"Untuned result dtype: {result_empty.dtype}")
╭─────────────────────────────── IgnoredArgumentWarning ───────────────────────────────╮│ The number of bins has been reduced from 10 to 1 due to duplicated edges caused by ││ repeated predicted values. ││││ Category : spotforecast2.exceptions.IgnoredArgumentWarning ││ Location : ││ /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/spotforecast2_s ││ afe/preprocessing/_binner.py:259 ││ Suppress : warnings.simplefilter('ignore', category=IgnoredArgumentWarning) │╰──────────────────────────────────────────────────────────────────────────────────────╯
Model is not tuned — returning empty Series.
Untuned result dtype: float64
import numpy as npimport pandas as pdfrom spotforecast2.models import ForecasterRecursiveLGBMFullrng = np.random.default_rng(0)y = pd.Series( rng.random(30), index=pd.date_range("2023-01-01", periods=30, freq="h", tz="UTC"), name="load",)model = ForecasterRecursiveLGBMFull(iteration=0, lags=3)model.forecaster.fit(y=y)# Set best_params and best_lags to bypass the early-return guard.model.best_params = {}model.best_lags = [1, 2, 3]# Supply pre-computed training matrices to avoid live data loading.X_train, y_train = model.forecaster.create_train_X_y(y=y)model._get_training_data =lambda: (X_train, y_train)importance = model.get_global_shap_feature_importance(frac=1.0)assertisinstance(importance, pd.Series)assertset(importance.index) == {"lag_1", "lag_2", "lag_3"}print(f"Feature importance index: {list(importance.index)}")print(importance)
╭─────────────────────────────── IgnoredArgumentWarning ───────────────────────────────╮│ The number of bins has been reduced from 10 to 1 due to duplicated edges caused by ││ repeated predicted values. ││││ Category : spotforecast2.exceptions.IgnoredArgumentWarning ││ Location : ││ /opt/hostedtoolcache/Python/3.13.13/x64/lib/python3.13/site-packages/spotforecast2_s ││ afe/preprocessing/_binner.py:259 ││ Suppress : warnings.simplefilter('ignore', category=IgnoredArgumentWarning) │╰──────────────────────────────────────────────────────────────────────────────────────╯
Loads time-series data, builds exogenous features, and runs bayesian_search_forecaster over the search space registered for self.name in SEARCH_SPACES.
After tuning the model is fitted with the best parameters and, if self.save_model_to_file is True, persisted to disk.
from spotforecast2.models import ForecasterRecursiveLGBMFullmodel = ForecasterRecursiveLGBMFull(iteration=0)assertcallable(model.tune)print(f"tune is callable: {callable(model.tune)}")print(f"n_trials={model.n_trials}, name={model.name}")