Save and load forecasters¶
skforecast models can be seamlessly serialized and deserialized using the save_forecaster and load_forecaster utility functions. These functions streamline the process of saving models to disk and loading them back into memory, ensuring that all internal parameters, custom functions, and window features are properly handled.
Depending on your use case (such as needing to store large NumPy arrays efficiently, embedding custom Python functions, or loading models securely from untrusted sources), skforecast supports multiple serialization engines: joblib, pickle, cloudpickle, and skops.
Libraries and data¶
# Libraries
# ==============================================================================
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from skforecast.datasets import fetch_dataset
from skforecast.recursive import ForecasterRecursive
from skforecast.recursive import ForecasterRecursiveMultiSeries
from skforecast.utils import save_forecaster
from skforecast.utils import load_forecaster
# Download data
# ==============================================================================
data = fetch_dataset(
name="h2o", raw=True, kwargs_read={"names": ["y", "date"], "header": 0},
verbose=False
)
data['date'] = pd.to_datetime(data['date'], format='%Y-%m-%d')
data = data.set_index('date')
data = data.asfreq('MS')
Basic Saving and Loading (joblib backend)¶
# Create and train forecaster
# ==============================================================================
forecaster = ForecasterRecursive(
estimator = RandomForestRegressor(random_state=123),
lags = 5,
forecaster_id = "forecaster_001"
)
forecaster.fit(y=data['y'])
forecaster.predict(steps=3)
2008-07-01 0.714526 2008-08-01 0.789144 2008-09-01 0.818433 Freq: MS, Name: pred, dtype: float64
# Save model
# ==============================================================================
save_forecaster(forecaster, file_name='forecaster_001.joblib', verbose=False, suppress_warnings=False)
# Load model
# ==============================================================================
forecaster_loaded = load_forecaster('forecaster_001.joblib', verbose=True, suppress_warnings=False)
===================
ForecasterRecursive
===================
Estimator: RandomForestRegressor
Lags: [1 2 3 4 5]
Window features: None
Calendar features: None
Window size: 5
Series name: y
Exogenous included: False
Exogenous names: None
Categorical features: auto
Transformer for y: None
Transformer for exog: None
Weight function included: False
Differentiation order: None
Drop NaN from series: False
Training range: [Timestamp('1991-07-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]
Training index type: DatetimeIndex
Training index frequency: <MonthBegin>
Estimator parameters:
{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'max_depth':
None, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None,
'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2,
'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100,
'n_jobs': None, 'oob_score': False, 'random_state': 123, 'verbose': 0,
'warm_start': False}
fit_kwargs: {}
Creation date: 2026-07-07 11:51:15
Last fit date: 2026-07-07 11:51:15
Skforecast version: 0.23.0
Python version: 3.13.13
Forecaster id: forecaster_001
# Forecaster identifier
# ==============================================================================
forecaster.forecaster_id
'forecaster_001'
Serialization backends¶
Both save_forecaster and load_forecaster accept a backend argument to choose the serialization engine. Choosing the right backend depends on your specific needs:
| Backend | Extension | Notes |
|---|---|---|
| joblib (default) | .joblib | Best for most cases. Highly efficient for objects with large NumPy arrays. |
| pickle | .pkl | Standard Python library, requires no extra dependencies. |
| cloudpickle | .cloudpickle | Best for models with custom features. Embeds custom functions (e.g. weight_func) and user-defined classes (e.g. window_features) directly in the file, avoiding the need for separate .py exports. Requires cloudpickle. |
| skops | .skops | Best for security. Secure format that does not execute arbitrary code on load. Recommended when loading files from untrusted sources. Requires skops. |
Note: When backend is not explicitly passed to load_forecaster, it is inferred automatically from the file extension.
API Details¶
For fine-grained control over the saving and loading processes, refer to the parameters below.
The save_forecaster function accepts the following parameters:
forecaster: The forecaster instance to be saved.file_name: The name (or path) of the file where the model will be saved.backend: The serialization engine to use. Defaults to'joblib'. Other options are'pickle','cloudpickle', and'skops'.save_custom_functions: (Default:True) IfTrue, custom functions used in the forecaster (likeweight_func) that are defined in the__main__namespace are exported as standalone.pyfiles. They must be available when loading the model. This parameter is ignored if usingbackend='cloudpickle'.verbose: (Default:False) IfTrue, prints a summary of the saved forecaster.suppress_warnings: (Default:False) IfTrue, suppresses skforecast warnings during the save process.
The load_forecaster function reconstructs the model and accepts these parameters:
file_name: The file name or path to load.backend: The serialization engine. IfNone(default), it is automatically inferred from the file extension.trusted: (Default:False) Specifies which custom types to trust whenbackend='skops'.verbose: (Default:True) IfTrue, prints a summary of the loaded forecaster.suppress_warnings: (Default:False) IfTrue, suppresses skforecast warnings during the load process.
Pickle backend¶
Switching backend is just a matter of passing the backend argument to save_forecaster and load_forecaster. The extension of file_name is adjusted automatically to match the chosen backend, regardless of the extension originally passed.
# Save and load the same forecaster using the pickle backend
# ==============================================================================
save_forecaster(forecaster, file_name='forecaster_001.pkl', backend='pickle', verbose=False)
forecaster_loaded = load_forecaster('forecaster_001.pkl', verbose=False)
forecaster_loaded.predict(steps=3)
2008-07-01 0.714526 2008-08-01 0.789144 2008-09-01 0.818433 Freq: MS, Name: pred, dtype: float64
skops backend¶
The 'skops' backend (skops documentation) stores the forecaster in a format that, unlike pickle, does not execute arbitrary code when the file is read. On load it only reconstructs types you explicitly trust, controlled by the trusted argument of load_forecaster:
trusted=False(default, secure): only skops' built-in safe types are loaded. Because every skforecast forecaster contains types that are not trusted by default, loading raises anUntrustedTypesFoundExceptionlisting them so you can review them before deciding to trust them.trusted=[...]: a list of type names to additionally trust. Obtain the candidate list withskops.io.get_untrusted_types(file=file_name), review it, and pass it.trusted=True: trust every type found in the file. Use this only for files from a source you trust, as it removes skops' security guarantee.
The last_window_ and training_range_ attributes (pandas objects, which skops cannot serialize natively) are decomposed into plain types on save and rebuilt on load automatically.
# Save forecaster using the skops backend
# ==============================================================================
save_forecaster(forecaster, file_name='forecaster_001.skops', backend='skops', verbose=False)
# By default, no custom type is trusted, so loading raises an exception
# ==============================================================================
from skops.io.exceptions import UntrustedTypesFoundException
try:
forecaster_loaded = load_forecaster('forecaster_001.skops', verbose=False)
except UntrustedTypesFoundException as e:
print(e)
Untrusted types found in the file: ['pandas._libs.tslibs.offsets.MonthBegin', 'pandas.core.indexes.datetimes.DatetimeIndex', 'skforecast.preprocessing._preprocessing.QuantileBinner', 'skforecast.recursive._forecaster_recursive.ForecasterRecursive']. skops does not load these types unless you explicitly trust them. To review the full list of untrusted types in the file, run `skops.io.get_untrusted_types(file='forecaster_001.skops')`. If you trust the source of 'forecaster_001.skops', reload with `load_forecaster(..., trusted=True)` to trust them all, or pass the reviewed list via `trusted=[...]`.
# Review the types the file needs to reconstruct the forecaster
# ==============================================================================
from skops.io import get_untrusted_types
untrusted_types = get_untrusted_types(file='forecaster_001.skops')
untrusted_types
['pandas._libs.tslibs.offsets.MonthBegin', 'pandas.core.indexes.datetimes.DatetimeIndex', 'skforecast.preprocessing._preprocessing.QuantileBinner', 'skforecast.recursive._forecaster_recursive.ForecasterRecursive']
# Once reviewed, trust them explicitly to load the forecaster
# ==============================================================================
forecaster_loaded = load_forecaster(
'forecaster_001.skops', trusted=untrusted_types, verbose=False
)
forecaster_loaded.predict(steps=3)
2008-07-01 0.714526 2008-08-01 0.789144 2008-09-01 0.818433 Freq: MS, Name: pred, dtype: float64
⚠ Warning
The 'skops' backend is not available for ForecasterStats, ForecasterRnn, or ForecasterFoundation, whose underlying estimators (statsmodels, Keras, or a foundation model) embed objects that skops cannot serialize. Use 'joblib', 'pickle', or 'cloudpickle' for those forecasters.
Forecaster with Custom Features¶
Sometimes external objects are needed when creating a Forecaster. For example:
Custom class to create window and custom features.
A function to reduce the impact of some dates on the model, Weighted Time Series Forecasting.
For your code to work properly, these functions must be available in the environment where the Forecaster is loaded.
# Custom class to create rolling skewness features
# ==============================================================================
from scipy.stats import skew
class RollingSkewness():
"""
Custom class to create rolling skewness features.
"""
def __init__(self, window_sizes, features_names: list | None = None):
if not isinstance(window_sizes, list):
window_sizes = [window_sizes]
self.window_sizes = window_sizes
self.features_names = features_names if features_names is not None else ['roll_skew']
def transform_batch(self, X: pd.Series) -> pd.DataFrame:
rolling_obj = X.rolling(window=self.window_sizes[0], center=False, closed='left')
rolling_skewness = rolling_obj.skew()
rolling_skewness = pd.DataFrame({self.features_names[0]: rolling_skewness})
return rolling_skewness
def transform(self, X: np.ndarray) -> np.ndarray:
X = X[-self.window_sizes[0]:]
X = X[~np.isnan(X)]
if len(X) > 0:
rolling_skewness = np.array([skew(X, bias=False)])
else:
rolling_skewness = np.array([np.nan])
return rolling_skewness
# Custom function to create weights
# ==============================================================================
def custom_weights(index):
"""
Return 0 if index is between 2004-01-01 and 2005-01-01.
"""
weights = np.where(
(index >= '2004-01-01') & (index <= '2005-01-01'),
0,
1
)
return weights
# Create and train forecaster
# ==============================================================================
window_features = RollingSkewness(window_sizes=3)
forecaster = ForecasterRecursive(
estimator = RandomForestRegressor(random_state=123),
lags = 3,
window_features = window_features,
weight_func = custom_weights,
forecaster_id = "forecaster_custom_features"
)
forecaster.fit(y=data['y'])
⚠ Warning
The save_forecaster function saves custom weight functions that are defined in the current session (the 'main' namespace, for example this notebook) as a module (custom_weights.py), since they cannot be re-imported when the Forecaster is loaded in a different session. Custom weight functions imported from a module are restored automatically and are not saved. The classes used to create the window features are never saved automatically, so you must ensure that these classes are available in the environment where the Forecaster is loaded.
# Save model and custom function
# ==============================================================================
save_forecaster(
forecaster,
file_name = 'forecaster_custom_features.joblib',
save_custom_functions = True,
verbose = False
)
╭───────────────────────────── SaveLoadSkforecastWarning ──────────────────────────────╮ │ Custom function(s) used to create weights are defined in the '__main__' namespace │ │ and have been saved as: 'custom_weights.py'. These files must be imported before │ │ loading the forecaster. │ │ Visit the documentation for more information: │ │ https://skforecast.org/latest/user_guides/save-load-forecaster.html#saving-and-loadi │ │ ng-a-forecaster-model-with-custom-features │ │ │ │ Category : skforecast.exceptions.SaveLoadSkforecastWarning │ │ Location : │ │ /home/ubuntu/miniconda3/envs/skforecast_23_py13/lib/python3.13/site-packages/skforec │ │ ast/utils/utils.py:2804 │ │ Suppress : warnings.simplefilter('ignore', category=SaveLoadSkforecastWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────── SaveLoadSkforecastWarning ──────────────────────────────╮ │ The Forecaster includes custom user-defined classes in the `window_features` │ │ argument. These classes are not saved automatically when saving the Forecaster. │ │ Please ensure you save these classes manually and import them before loading the │ │ Forecaster. │ │ Custom classes: RollingSkewness │ │ Visit the documentation for more information: │ │ https://skforecast.org/latest/user_guides/save-load-forecaster.html#saving-and-loadi │ │ ng-a-forecaster-model-with-custom-features │ │ │ │ Category : skforecast.exceptions.SaveLoadSkforecastWarning │ │ Location : │ │ /home/ubuntu/miniconda3/envs/skforecast_23_py13/lib/python3.13/site-packages/skforec │ │ ast/utils/utils.py:2828 │ │ Suppress : warnings.simplefilter('ignore', category=SaveLoadSkforecastWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
At this point, the RollingSkewness class is manually saved in a file called rolling_skewness.py. This file must be available in the environment where the Forecaster is loaded.
# Load model and custom function
# ==============================================================================
from rolling_skewness import RollingSkewness # This file has to be generated manually
from custom_weights import custom_weights
forecaster_loaded = load_forecaster('forecaster_custom_features.joblib', verbose=True)
===================
ForecasterRecursive
===================
Estimator: RandomForestRegressor
Lags: [1 2 3]
Window features: ['roll_skew']
Calendar features: None
Window size: 3
Series name: y
Exogenous included: False
Exogenous names: None
Categorical features: auto
Transformer for y: None
Transformer for exog: None
Weight function included: True
Differentiation order: None
Drop NaN from series: False
Training range: [Timestamp('1991-07-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]
Training index type: DatetimeIndex
Training index frequency: <MonthBegin>
Estimator parameters:
{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'max_depth':
None, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None,
'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2,
'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100,
'n_jobs': None, 'oob_score': False, 'random_state': 123, 'verbose': 0,
'warm_start': False}
fit_kwargs: {}
Creation date: 2026-07-07 11:51:22
Last fit date: 2026-07-07 11:51:22
Skforecast version: 0.23.0
Python version: 3.13.13
Forecaster id: forecaster_custom_features
# Predict using loaded forecaster
# ==============================================================================
forecaster_loaded.predict(steps=5)
2008-07-01 0.741098 2008-08-01 0.815339 2008-09-01 0.848204 2008-10-01 0.822180 2008-11-01 0.873488 Freq: MS, Name: pred, dtype: float64
Avoiding external files with the cloudpickle backend¶
Unlike joblib, pickle, or skops, the cloudpickle backend embeds the custom RollingSkewness class and the custom_weights function directly in the saved file. No .py files need to be exported or imported, and no SaveLoadSkforecastWarning is raised.
# Save model using the cloudpickle backend: no warning is raised
# ==============================================================================
save_forecaster(
forecaster,
file_name = 'forecaster_custom_features.cloudpickle',
backend = 'cloudpickle',
verbose = False
)
# Simulate a session where the custom class and function were never defined
# ==============================================================================
del RollingSkewness, custom_weights
# Load and predict without RollingSkewness or custom_weights being available
# ==============================================================================
forecaster_loaded = load_forecaster('forecaster_custom_features.cloudpickle', verbose=True)
forecaster_loaded.predict(steps=5)
===================
ForecasterRecursive
===================
Estimator: RandomForestRegressor
Lags: [1 2 3]
Window features: ['roll_skew']
Calendar features: None
Window size: 3
Series name: y
Exogenous included: False
Exogenous names: None
Categorical features: auto
Transformer for y: None
Transformer for exog: None
Weight function included: True
Differentiation order: None
Drop NaN from series: False
Training range: [Timestamp('1991-07-01 00:00:00'), Timestamp('2008-06-01 00:00:00')]
Training index type: DatetimeIndex
Training index frequency: <MonthBegin>
Estimator parameters:
{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'max_depth':
None, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None,
'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2,
'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100,
'n_jobs': None, 'oob_score': False, 'random_state': 123, 'verbose': 0,
'warm_start': False}
fit_kwargs: {}
Creation date: 2026-07-07 11:51:22
Last fit date: 2026-07-07 11:51:22
Skforecast version: 0.23.0
Python version: 3.13.13
Forecaster id: forecaster_custom_features
2008-07-01 0.741098 2008-08-01 0.815339 2008-09-01 0.848204 2008-10-01 0.822180 2008-11-01 0.873488 Freq: MS, Name: pred, dtype: float64
ForecasterRecursiveMultiSeries¶
When using a ForecasterRecursiveMultiSeries, the save_forecaster function saves a separate module for each custom weight function that is defined in the current session (the 'main' namespace). Functions imported from a module are restored automatically and are not saved.
# Data download
# ==============================================================================
data = fetch_dataset(name="items_sales", verbose=False)
data.head()
| item_1 | item_2 | item_3 | |
|---|---|---|---|
| date | |||
| 2012-01-01 | 8.253175 | 21.047727 | 19.429739 |
| 2012-01-02 | 22.777826 | 26.578125 | 28.009863 |
| 2012-01-03 | 27.549099 | 31.751042 | 32.078922 |
| 2012-01-04 | 25.895533 | 24.567708 | 27.252276 |
| 2012-01-05 | 21.379238 | 18.191667 | 20.357737 |
# Custom function to create weights for each item
# ==============================================================================
def custom_weights_item_1(index):
"""
Return 0 if index is between 2012-01-01 and 2012-06-01.
"""
weights = np.where(
(index >= '2012-01-01') & (index <= '2012-06-01'), 0, 1
)
return weights
def custom_weights_item_2(index):
"""
Return 0 if index is between 2012-04-01 and 2013-01-01.
"""
weights = np.where(
(index >= '2012-04-01') & (index <= '2013-01-01'), 0, 1
)
return weights
def custom_weights_item_3(index):
"""
Return 0 if index is between 2012-06-01 and 2013-01-01.
"""
weights = np.where(
(index >= '2012-06-01') & (index <= '2013-01-01'), 0, 1
)
return weights
# Custom class to create rolling skewness features (multi-series)
# ==============================================================================
from scipy.stats import skew
class RollingSkewnessMultiSeries():
"""
Custom class to create rolling skewness features for multiple series.
"""
def __init__(self, window_sizes, features_names: list | None = None):
if not isinstance(window_sizes, list):
window_sizes = [window_sizes]
self.window_sizes = window_sizes
self.features_names = features_names if features_names is not None else ['roll_skew']
def transform_batch(self, X: pd.Series) -> pd.DataFrame:
rolling_obj = X.rolling(window=self.window_sizes[0], center=False, closed='left')
rolling_skewness = rolling_obj.skew()
rolling_skewness = pd.DataFrame({self.features_names[0]: rolling_skewness})
return rolling_skewness
def transform(self, X: np.ndarray) -> np.ndarray:
X_dim = X.ndim
if X_dim == 1:
n_series = 1 # Only one series
X = X.reshape(-1, 1)
else:
n_series = X.shape[1] # Series (levels) to be predicted (present in last_window)
X = X[-self.window_sizes[0]:]
n_stats = 1 # Only skewness is calculated
rolling_skewness = np.full(
shape=(n_series, n_stats), fill_value=np.nan, dtype=float
)
for i in range(n_series):
col = X[:, i]
col = col[~np.isnan(col)]
if len(col) > 0:
rolling_skewness[i, :] = skew(col, bias=False)
if X_dim == 1:
rolling_skewness = rolling_skewness.flatten()
return rolling_skewness
⚠ Warning
When weight_func is a dict and does not contain any of the series, for instance:
# Weights are not included for item_2
weight_func_dict = {
'item_1': custom_weights_item_1,
'item_3': custom_weights_item_3
}
You must create a function that returns all 1's as weights of that series.
def custom_weights_all_1(index):
"""
Return 1 for all elements in the index.
"""
weights = np.ones(len(index))
return weights
# item_2 dummy weights
weight_func_dict = {
'item_1': custom_weights_item_1,
'item_2': custom_weights_all_1,
'item_3': custom_weights_item_3
}
# Create and train ForecasterRecursiveMultiSeries
# ==============================================================================
window_features = RollingSkewnessMultiSeries(window_sizes=3)
weight_func_dict = {
'item_1': custom_weights_item_1,
'item_2': custom_weights_item_2,
'item_3': custom_weights_item_3
}
forecaster = ForecasterRecursiveMultiSeries(
estimator = RandomForestRegressor(random_state=123),
lags = 3,
window_features = window_features,
encoding = 'ordinal',
weight_func = weight_func_dict
)
forecaster.fit(series=data)
forecaster
╭────────────────────────────────── InputTypeWarning ──────────────────────────────────╮ │ Passing a DataFrame (either wide or long format) as `series` requires additional │ │ internal transformations, which can increase computational time. It is recommended │ │ to use a dictionary of pandas Series instead. For more details, see: │ │ https://skforecast.org/latest/user_guides/independent-multi-time-series-forecasting. │ │ html#input-data │ │ │ │ Category : skforecast.exceptions.InputTypeWarning │ │ Location : │ │ /home/ubuntu/miniconda3/envs/skforecast_23_py13/lib/python3.13/site-packages/skforec │ │ ast/utils/utils.py:3388 │ │ Suppress : warnings.simplefilter('ignore', category=InputTypeWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
ForecasterRecursiveMultiSeries
General Information
- Estimator: RandomForestRegressor
- Lags: [1 2 3]
- Window features: ['roll_skew']
- Calendar features: None
- Window size: 3
- Series encoding: ordinal
- Exogenous included: False
- Categorical features: auto
- Weight function included: True
- Series weights: None
- Differentiation order: None
- Drop NaN from series: False
- Creation date: 2026-07-07 11:51:23
- Last fit date: 2026-07-07 11:51:24
- Skforecast version: 0.23.0
- Python version: 3.13.13
- Forecaster id: None
Exogenous Variables
None
Data Transformations
- Transformer for series: None
- Transformer for exog: None
Training Information
- Series names (levels): item_1, item_2, item_3
- Training range: 'item_1': ['2012-01-01', '2015-01-01'], 'item_2': ['2012-01-01', '2015-01-01'], 'item_3': ['2012-01-01', '2015-01-01']
- Training index type: DatetimeIndex
- Training index frequency: D
Estimator Parameters
-
{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'max_depth': None, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100, 'n_jobs': None, 'oob_score': False, 'random_state': 123, 'verbose': 0, 'warm_start': False}
Fit Kwargs
-
{}
# Save model and custom function
# ==============================================================================
save_forecaster(
forecaster,
file_name = 'forecaster_multiseries_custom_features.joblib',
save_custom_functions = True,
verbose = False
)
╭───────────────────────────── SaveLoadSkforecastWarning ──────────────────────────────╮ │ Custom function(s) used to create weights are defined in the '__main__' namespace │ │ and have been saved as: 'custom_weights_item_1.py', 'custom_weights_item_2.py', │ │ 'custom_weights_item_3.py'. These files must be imported before loading the │ │ forecaster. │ │ Visit the documentation for more information: │ │ https://skforecast.org/latest/user_guides/save-load-forecaster.html#saving-and-loadi │ │ ng-a-forecaster-model-with-custom-features │ │ │ │ Category : skforecast.exceptions.SaveLoadSkforecastWarning │ │ Location : │ │ /home/ubuntu/miniconda3/envs/skforecast_23_py13/lib/python3.13/site-packages/skforec │ │ ast/utils/utils.py:2804 │ │ Suppress : warnings.simplefilter('ignore', category=SaveLoadSkforecastWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────── SaveLoadSkforecastWarning ──────────────────────────────╮ │ The Forecaster includes custom user-defined classes in the `window_features` │ │ argument. These classes are not saved automatically when saving the Forecaster. │ │ Please ensure you save these classes manually and import them before loading the │ │ Forecaster. │ │ Custom classes: RollingSkewnessMultiSeries │ │ Visit the documentation for more information: │ │ https://skforecast.org/latest/user_guides/save-load-forecaster.html#saving-and-loadi │ │ ng-a-forecaster-model-with-custom-features │ │ │ │ Category : skforecast.exceptions.SaveLoadSkforecastWarning │ │ Location : │ │ /home/ubuntu/miniconda3/envs/skforecast_23_py13/lib/python3.13/site-packages/skforec │ │ ast/utils/utils.py:2828 │ │ Suppress : warnings.simplefilter('ignore', category=SaveLoadSkforecastWarning) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯
# Load model and custom function
# ==============================================================================
from rolling_skewness import RollingSkewnessMultiSeries # This file has to be generated manually
from custom_weights_item_1 import custom_weights_item_1
from custom_weights_item_2 import custom_weights_item_2
from custom_weights_item_3 import custom_weights_item_3
forecaster_loaded = load_forecaster(
'forecaster_multiseries_custom_features.joblib', verbose=True
)
==============================
ForecasterRecursiveMultiSeries
==============================
Estimator: RandomForestRegressor
Lags: [1 2 3]
Window features: ['roll_skew']
Calendar features: None
Window size: 3
Series encoding: ordinal
Series names (levels): item_1, item_2, item_3
Exogenous included: False
Exogenous names: None
Categorical features: auto
Transformer for series: None
Transformer for exog: None
Weight function included: True
Series weights: None
Differentiation order: None
Drop NaN from series: False
Training range:
'item_1': ['2012-01-01', '2015-01-01'], 'item_2': ['2012-01-01', '2015-01-01'],
'item_3': ['2012-01-01', '2015-01-01']
Training index type: DatetimeIndex
Training index frequency: <Day>
Estimator parameters:
{'bootstrap': True, 'ccp_alpha': 0.0, 'criterion': 'squared_error', 'max_depth':
None, 'max_features': 1.0, 'max_leaf_nodes': None, 'max_samples': None,
'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2,
'min_weight_fraction_leaf': 0.0, 'monotonic_cst': None, 'n_estimators': 100,
'n_jobs': None, 'oob_score': False, 'random_state': 123, 'verbose': 0,
'warm_start': False}
fit_kwargs: {}
Creation date: 2026-07-07 11:51:23
Last fit date: 2026-07-07 11:51:24
Skforecast version: 0.23.0
Python version: 3.13.13
Forecaster id: None
# Predict using loaded forecaster
# ==============================================================================
forecaster_loaded.predict(steps=5, levels=None) # Predict all levels
| level | pred | |
|---|---|---|
| 2015-01-02 | item_1 | 14.983436 |
| 2015-01-02 | item_2 | 17.968657 |
| 2015-01-02 | item_3 | 20.923016 |
| 2015-01-03 | item_1 | 15.192081 |
| 2015-01-03 | item_2 | 17.987674 |
| 2015-01-03 | item_3 | 19.328315 |
| 2015-01-04 | item_1 | 17.895695 |
| 2015-01-04 | item_2 | 21.798395 |
| 2015-01-04 | item_3 | 21.058723 |
| 2015-01-05 | item_1 | 16.063031 |
| 2015-01-05 | item_2 | 23.830917 |
| 2015-01-05 | item_3 | 22.003161 |
| 2015-01-06 | item_1 | 17.759633 |
| 2015-01-06 | item_2 | 21.495810 |
| 2015-01-06 | item_3 | 22.582027 |
Using cloudpickle with ForecasterRecursiveMultiSeries¶
The same pattern applies here: saving with backend='cloudpickle' embeds every function in the weight_func dictionary and any custom window-feature class directly in the file, avoiding the per-item .py exports shown above.