spotforecast2 2.2.2__tar.gz → 2.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/PKG-INFO +1 -1
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/pyproject.toml +1 -1
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/bayesian_search.py +2 -1
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/random_search.py +38 -44
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/spotoptim_search.py +89 -67
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/utils_metrics.py +34 -28
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/models/forecaster_recursive_lgbm_full.py +27 -33
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/models/forecaster_recursive_model_full.py +86 -33
- spotforecast2-2.2.4/src/spotforecast2/models/forecaster_recursive_xgb_full.py +111 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/strategies.py +1 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/outlier_plots.py +59 -52
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/plotter.py +97 -122
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/time_series_visualization.py +165 -125
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/stats/autocorrelation.py +62 -47
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/task_demo.py +34 -16
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/task_n_to_1_with_covariates.py +81 -105
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/task_n_to_1_with_covariates_and_dataframe.py +96 -103
- spotforecast2-2.2.2/src/spotforecast2/models/forecaster_recursive_xgb_full.py +0 -74
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/README.md +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/grid_search.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/models/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/base.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/clean.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/defaults.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/factories.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/lazy.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/multi.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/optuna.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/predict.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/runner.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/search_spaces.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/multitask/spotoptim.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/assets/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/assets/prediction_report.html.j2 +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/distribution.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/plots/spectral.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/py.typed +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/stats/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/task_entsoe.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/task_n_to_1.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/tasks/task_n_to_1_dataframe.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/warnings/__init__.py +0 -0
- {spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/warnings/exceptions.py +0 -0
{spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/bayesian_search.py
RENAMED
|
@@ -33,7 +33,8 @@ from spotforecast2_safe.splitter.utils_common import (
|
|
|
33
33
|
from spotforecast2.model_selection.utils_metrics import (
|
|
34
34
|
_calculate_metrics_one_step_ahead,
|
|
35
35
|
)
|
|
36
|
-
from spotforecast2_safe.forecaster.utils import
|
|
36
|
+
from spotforecast2_safe.forecaster.utils import date_to_index_position
|
|
37
|
+
from spotforecast2_safe.preprocessing.forecaster_config import initialize_lags
|
|
37
38
|
from spotforecast2_safe.exceptions import IgnoredArgumentWarning
|
|
38
39
|
from spotforecast2_safe.exceptions import set_skforecast_warnings
|
|
39
40
|
|
{spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/random_search.py
RENAMED
|
@@ -85,50 +85,44 @@ def random_search_forecaster(
|
|
|
85
85
|
param=value pairs.
|
|
86
86
|
|
|
87
87
|
Examples:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
>>> print(results.shape[0])
|
|
127
|
-
5
|
|
128
|
-
>>> print('estimator__alpha' in results.columns)
|
|
129
|
-
True
|
|
130
|
-
>>> print('mean_squared_error' in results.columns)
|
|
131
|
-
True
|
|
88
|
+
```{python}
|
|
89
|
+
import numpy as np
|
|
90
|
+
import pandas as pd
|
|
91
|
+
from sklearn.linear_model import Ridge
|
|
92
|
+
from scipy.stats import uniform
|
|
93
|
+
from spotforecast2_safe.forecaster.recursive import ForecasterRecursive
|
|
94
|
+
from spotforecast2_safe.splitter import TimeSeriesFold
|
|
95
|
+
from spotforecast2.model_selection.random_search import random_search_forecaster
|
|
96
|
+
|
|
97
|
+
rng = np.random.default_rng(1234)
|
|
98
|
+
y = pd.Series(rng.standard_normal(30), name="y")
|
|
99
|
+
|
|
100
|
+
forecaster = ForecasterRecursive(estimator=Ridge(), lags=2)
|
|
101
|
+
cv = TimeSeriesFold(steps=2, initial_train_size=20, refit=False)
|
|
102
|
+
|
|
103
|
+
param_distributions = {
|
|
104
|
+
"estimator__alpha": uniform(0.1, 10.0),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
results = random_search_forecaster(
|
|
108
|
+
forecaster=forecaster,
|
|
109
|
+
y=y,
|
|
110
|
+
cv=cv,
|
|
111
|
+
param_distributions=param_distributions,
|
|
112
|
+
metric="mean_squared_error",
|
|
113
|
+
n_iter=2,
|
|
114
|
+
random_state=1234,
|
|
115
|
+
return_best=False,
|
|
116
|
+
verbose=False,
|
|
117
|
+
show_progress=False,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
print(results.shape)
|
|
121
|
+
print(results.columns.tolist())
|
|
122
|
+
assert results.shape[0] == 2
|
|
123
|
+
assert "estimator__alpha" in results.columns
|
|
124
|
+
assert "mean_squared_error" in results.columns
|
|
125
|
+
```
|
|
132
126
|
"""
|
|
133
127
|
|
|
134
128
|
param_grid = list(
|
{spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/spotoptim_search.py
RENAMED
|
@@ -7,7 +7,7 @@ Hyperparameter search functions for forecasters using SpotOptim.
|
|
|
7
7
|
This module provides an alternative to Bayesian (Optuna-based) search
|
|
8
8
|
by leveraging the SpotOptim surrogate-model-based optimizer. It
|
|
9
9
|
follows the same interface as
|
|
10
|
-
|
|
10
|
+
`spotforecast2.model_selection.bayesian_search_forecaster()`, so the
|
|
11
11
|
two can be used interchangeably.
|
|
12
12
|
"""
|
|
13
13
|
|
|
@@ -21,6 +21,7 @@ from typing import Any, Callable, Dict
|
|
|
21
21
|
|
|
22
22
|
import numpy as np
|
|
23
23
|
import pandas as pd
|
|
24
|
+
from tqdm.auto import tqdm
|
|
24
25
|
|
|
25
26
|
try:
|
|
26
27
|
from spotoptim import SpotOptim
|
|
@@ -34,7 +35,8 @@ except ImportError:
|
|
|
34
35
|
|
|
35
36
|
from spotforecast2_safe.exceptions import IgnoredArgumentWarning
|
|
36
37
|
from spotforecast2_safe.forecaster.metrics import _get_metric, add_y_train_argument
|
|
37
|
-
from spotforecast2_safe.forecaster.utils import date_to_index_position
|
|
38
|
+
from spotforecast2_safe.forecaster.utils import date_to_index_position
|
|
39
|
+
from spotforecast2_safe.preprocessing.forecaster_config import initialize_lags
|
|
38
40
|
from spotforecast2_safe.splitter.split_ts_cv import TimeSeriesFold
|
|
39
41
|
from spotforecast2_safe.splitter.utils_common import (
|
|
40
42
|
check_backtesting_input,
|
|
@@ -66,33 +68,22 @@ def parse_lags_from_strings(lags_str: str | int | list) -> int | list:
|
|
|
66
68
|
Either an integer or a list of integers representing lags.
|
|
67
69
|
|
|
68
70
|
Examples:
|
|
69
|
-
Basic parsing:
|
|
70
|
-
|
|
71
|
-
>>> from spotforecast2.model_selection.spotoptim_search import (
|
|
72
|
-
... parse_lags_from_strings,
|
|
73
|
-
... )
|
|
74
|
-
>>> parse_lags_from_strings(24)
|
|
75
|
-
24
|
|
76
|
-
>>> parse_lags_from_strings("[1, 2, 3]")
|
|
77
|
-
[1, 2, 3]
|
|
78
|
-
|
|
79
|
-
Visualizing the safety threshold (Example of dynamic documentation):
|
|
80
|
-
|
|
81
71
|
```{python}
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
from spotforecast2.model_selection.spotoptim_search import (
|
|
73
|
+
parse_lags_from_strings,
|
|
74
|
+
)
|
|
84
75
|
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
result_int = parse_lags_from_strings(24)
|
|
77
|
+
print(result_int)
|
|
78
|
+
assert result_int == 24
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
result_list = parse_lags_from_strings("[1, 2, 3]")
|
|
81
|
+
print(result_list)
|
|
82
|
+
assert result_list == [1, 2, 3]
|
|
91
83
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
# plt.show() # Commented for non-interactive environments
|
|
84
|
+
result_passthrough = parse_lags_from_strings([4, 8, 12])
|
|
85
|
+
print(result_passthrough)
|
|
86
|
+
assert result_passthrough == [4, 8, 12]
|
|
96
87
|
```
|
|
97
88
|
"""
|
|
98
89
|
if isinstance(lags_str, (int, list)):
|
|
@@ -129,7 +120,7 @@ def spotoptim_search_forecaster(
|
|
|
129
120
|
"""Hyperparameter optimisation for a Forecaster using SpotOptim.
|
|
130
121
|
|
|
131
122
|
Drop-in alternative to
|
|
132
|
-
|
|
123
|
+
`bayesian_search_forecaster()`
|
|
133
124
|
that uses the SpotOptim surrogate-model-based optimizer instead of
|
|
134
125
|
Optuna's TPE sampler.
|
|
135
126
|
|
|
@@ -139,7 +130,7 @@ def spotoptim_search_forecaster(
|
|
|
139
130
|
cv: Cross-validation strategy — ``TimeSeriesFold`` or
|
|
140
131
|
``OneStepAheadFold``.
|
|
141
132
|
search_space: Hyperparameter search space. Either a
|
|
142
|
-
|
|
133
|
+
`ParameterSet` or a plain
|
|
143
134
|
``dict`` (see examples below).
|
|
144
135
|
metric: Metric name, callable, or list thereof.
|
|
145
136
|
exog: Optional exogenous variable(s).
|
|
@@ -165,9 +156,8 @@ def spotoptim_search_forecaster(
|
|
|
165
156
|
``OneStepAheadFold``.
|
|
166
157
|
|
|
167
158
|
Examples:
|
|
168
|
-
**1 — Dict-based search space (no ParameterSet needed):**
|
|
169
|
-
|
|
170
159
|
```{python}
|
|
160
|
+
# 1 — Dict-based search space (no ParameterSet needed):
|
|
171
161
|
import numpy as np
|
|
172
162
|
import pandas as pd
|
|
173
163
|
from sklearn.linear_model import Ridge
|
|
@@ -209,10 +199,23 @@ def spotoptim_search_forecaster(
|
|
|
209
199
|
print(f"Contains 'alpha': {'alpha' in results.columns}")
|
|
210
200
|
```
|
|
211
201
|
|
|
212
|
-
**2 — ParameterSet-based search space:**
|
|
213
|
-
|
|
214
202
|
```{python}
|
|
203
|
+
# 2 — ParameterSet-based search space:
|
|
204
|
+
import numpy as np
|
|
205
|
+
import pandas as pd
|
|
206
|
+
from sklearn.linear_model import Ridge
|
|
215
207
|
from spotoptim.hyperparameters import ParameterSet
|
|
208
|
+
from spotforecast2_safe.forecaster.recursive import ForecasterRecursive
|
|
209
|
+
from spotforecast2_safe.splitter import TimeSeriesFold
|
|
210
|
+
from spotforecast2.model_selection import spotoptim_search_forecaster
|
|
211
|
+
|
|
212
|
+
np.random.seed(42)
|
|
213
|
+
y = pd.Series(
|
|
214
|
+
np.random.randn(200).cumsum(),
|
|
215
|
+
index=pd.date_range("2022-01-01", periods=200, freq="h"),
|
|
216
|
+
name="load",
|
|
217
|
+
)
|
|
218
|
+
cv = TimeSeriesFold(steps=5, initial_train_size=150, refit=False)
|
|
216
219
|
|
|
217
220
|
ps = ParameterSet()
|
|
218
221
|
_ = ps.add_float("alpha", low=0.01, high=10.0)
|
|
@@ -309,9 +312,8 @@ def spotoptim_objective(
|
|
|
309
312
|
np.ndarray: 1D array of results for the primary metric.
|
|
310
313
|
|
|
311
314
|
Examples:
|
|
312
|
-
Generating textual output of parameter evaluation:
|
|
313
|
-
|
|
314
315
|
```{python}
|
|
316
|
+
# Demonstrate the call structure of spotoptim_objective.
|
|
315
317
|
import numpy as np
|
|
316
318
|
import pandas as pd
|
|
317
319
|
from spotforecast2_safe.splitter import TimeSeriesFold
|
|
@@ -550,9 +552,23 @@ def spotoptim_search(
|
|
|
550
552
|
all_lags: list = []
|
|
551
553
|
all_params: list[dict] = []
|
|
552
554
|
|
|
555
|
+
# Single trial-level progress bar. Each entry in ``X`` is one trial
|
|
556
|
+
# (initial design point or sequential proposal), so we advance by
|
|
557
|
+
# ``len(X)`` per objective call. ``n_trials`` (== SpotOptim's
|
|
558
|
+
# ``max_iter``) is the total budget — it already includes the
|
|
559
|
+
# ``n_initial`` design points, so the total bar length is ``n_trials``.
|
|
560
|
+
# The inner backtesting fold loop is forced silent below to avoid the
|
|
561
|
+
# per-trial bar spam that used to stack dozens of fast "100% 35/35"
|
|
562
|
+
# bars in notebook output.
|
|
563
|
+
trial_bar = (
|
|
564
|
+
tqdm(total=n_trials, desc="SpotOptim trials", leave=True)
|
|
565
|
+
if show_progress
|
|
566
|
+
else None
|
|
567
|
+
)
|
|
568
|
+
|
|
553
569
|
# --- Objective function -----------------------------------------------
|
|
554
570
|
def _objective_wrapper(X: np.ndarray) -> np.ndarray:
|
|
555
|
-
|
|
571
|
+
result = spotoptim_objective(
|
|
556
572
|
X=X,
|
|
557
573
|
forecaster_search=forecaster_search,
|
|
558
574
|
cv_name=cv_name,
|
|
@@ -562,7 +578,7 @@ def spotoptim_search(
|
|
|
562
578
|
exog=exog,
|
|
563
579
|
n_jobs=n_jobs,
|
|
564
580
|
verbose=verbose,
|
|
565
|
-
show_progress=
|
|
581
|
+
show_progress=True,
|
|
566
582
|
suppress_warnings=suppress_warnings,
|
|
567
583
|
var_name=var_name,
|
|
568
584
|
var_type=var_type,
|
|
@@ -571,6 +587,9 @@ def spotoptim_search(
|
|
|
571
587
|
all_lags=all_lags,
|
|
572
588
|
all_params=all_params,
|
|
573
589
|
)
|
|
590
|
+
if trial_bar is not None:
|
|
591
|
+
trial_bar.update(len(X))
|
|
592
|
+
return result
|
|
574
593
|
|
|
575
594
|
# --- Run SpotOptim ----------------------------------------------------
|
|
576
595
|
optimizer = SpotOptim(
|
|
@@ -586,7 +605,11 @@ def spotoptim_search(
|
|
|
586
605
|
**kwargs_spotoptim_,
|
|
587
606
|
)
|
|
588
607
|
|
|
589
|
-
|
|
608
|
+
try:
|
|
609
|
+
optimizer.optimize()
|
|
610
|
+
finally:
|
|
611
|
+
if trial_bar is not None:
|
|
612
|
+
trial_bar.close()
|
|
590
613
|
|
|
591
614
|
# --- Build results DataFrame ------------------------------------------
|
|
592
615
|
lags_list = [
|
|
@@ -652,21 +675,20 @@ def convert_search_space(
|
|
|
652
675
|
- var_trans: List of transformation functions (e.g., log10) or None.
|
|
653
676
|
|
|
654
677
|
Examples:
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
[(0.01, 10.0)]
|
|
666
|
-
|
|
667
|
-
[
|
|
668
|
-
|
|
669
|
-
Converting a complex dictionary search space:
|
|
678
|
+
```{python}
|
|
679
|
+
from spotoptim.hyperparameters import ParameterSet
|
|
680
|
+
from spotforecast2.model_selection.spotoptim_search import (
|
|
681
|
+
convert_search_space,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
ps = ParameterSet()
|
|
685
|
+
_ = ps.add_float("alpha", 0.01, 10.0)
|
|
686
|
+
b, t, n, tr = convert_search_space(ps)
|
|
687
|
+
print(b)
|
|
688
|
+
assert b == [(0.01, 10.0)]
|
|
689
|
+
print(t)
|
|
690
|
+
assert t == ["float"]
|
|
691
|
+
```
|
|
670
692
|
|
|
671
693
|
```{python}
|
|
672
694
|
from spotforecast2.model_selection.spotoptim_search import convert_search_space
|
|
@@ -674,7 +696,7 @@ def convert_search_space(
|
|
|
674
696
|
search_space = {
|
|
675
697
|
"learning_rate": (0.001, 0.1, "log10"),
|
|
676
698
|
"max_depth": (2, 10),
|
|
677
|
-
"model_type": ["RandomForest", "XGBoost"]
|
|
699
|
+
"model_type": ["RandomForest", "XGBoost"],
|
|
678
700
|
}
|
|
679
701
|
|
|
680
702
|
bounds, vt, vn, vtr = convert_search_space(search_space)
|
|
@@ -764,21 +786,21 @@ def array_to_params(
|
|
|
764
786
|
Dictionary mapping parameter names to typed values.
|
|
765
787
|
|
|
766
788
|
Examples:
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
789
|
+
```{python}
|
|
790
|
+
import numpy as np
|
|
791
|
+
from spotforecast2.model_selection.spotoptim_search import (
|
|
792
|
+
array_to_params,
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
result = array_to_params(
|
|
796
|
+
np.array([100.0, 0.05]),
|
|
797
|
+
var_name=["n_estimators", "lr"],
|
|
798
|
+
var_type=["int", "float"],
|
|
799
|
+
bounds=[(50, 200), (0.01, 0.3)],
|
|
800
|
+
)
|
|
801
|
+
print(result)
|
|
802
|
+
assert result == {"n_estimators": 100, "lr": 0.05}
|
|
803
|
+
```
|
|
782
804
|
|
|
783
805
|
```{python}
|
|
784
806
|
import numpy as np
|
{spotforecast2-2.2.2 → spotforecast2-2.2.4}/src/spotforecast2/model_selection/utils_metrics.py
RENAMED
|
@@ -38,34 +38,40 @@ def _calculate_metrics_one_step_ahead(
|
|
|
38
38
|
rolling features.
|
|
39
39
|
|
|
40
40
|
Examples:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
41
|
+
```{python}
|
|
42
|
+
import numpy as np
|
|
43
|
+
import pandas as pd
|
|
44
|
+
from sklearn.linear_model import LinearRegression
|
|
45
|
+
from sklearn.metrics import mean_absolute_error
|
|
46
|
+
from spotforecast2_safe.forecaster.metrics import add_y_train_argument
|
|
47
|
+
from spotforecast2_safe.forecaster.recursive import ForecasterRecursive
|
|
48
|
+
from spotforecast2.model_selection.utils_metrics import (
|
|
49
|
+
_calculate_metrics_one_step_ahead,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Predictors are already extracted lag features; y = x+1 (linear).
|
|
53
|
+
X_train = pd.DataFrame(
|
|
54
|
+
{"lag_2": [1.0, 2.0, 3.0, 4.0, 5.0], "lag_1": [2.0, 3.0, 4.0, 5.0, 6.0]}
|
|
55
|
+
)
|
|
56
|
+
y_train = pd.Series([3.0, 4.0, 5.0, 6.0, 7.0], name="y")
|
|
57
|
+
X_test = pd.DataFrame({"lag_2": [5.0], "lag_1": [6.0]})
|
|
58
|
+
# Deliberately set y_test=9.0 (model predicts 7.0) to get MAE=2.0.
|
|
59
|
+
y_test = pd.Series([9.0], name="y")
|
|
60
|
+
|
|
61
|
+
forecaster = ForecasterRecursive(LinearRegression(), lags=2)
|
|
62
|
+
# Metrics must accept y_train; use add_y_train_argument to wrap sklearn metrics.
|
|
63
|
+
metrics = [add_y_train_argument(mean_absolute_error)]
|
|
64
|
+
result = _calculate_metrics_one_step_ahead(
|
|
65
|
+
forecaster=forecaster,
|
|
66
|
+
metrics=metrics,
|
|
67
|
+
X_train=X_train,
|
|
68
|
+
y_train=y_train,
|
|
69
|
+
X_test=X_test,
|
|
70
|
+
y_test=y_test,
|
|
71
|
+
)
|
|
72
|
+
print(f"MAE: {result[0]:.4f}")
|
|
73
|
+
assert result[0] == 2.0
|
|
74
|
+
```
|
|
69
75
|
"""
|
|
70
76
|
|
|
71
77
|
if type(forecaster).__name__ == "ForecasterDirect":
|
|
@@ -3,20 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
"""LGBM forecaster with real Bayesian tuning and SHAP.
|
|
5
5
|
|
|
6
|
-
This module provides
|
|
6
|
+
This module provides `ForecasterRecursiveLGBMFull`, which combines
|
|
7
7
|
the LightGBM forecaster from ``spotforecast2-safe`` with Bayesian
|
|
8
8
|
hyperparameter optimisation (Optuna) and SHAP-based feature importance
|
|
9
|
-
from
|
|
9
|
+
from `ForecasterRecursiveModelFull`.
|
|
10
10
|
|
|
11
11
|
Examples:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
```{python}
|
|
13
|
+
from spotforecast2.models import ForecasterRecursiveLGBMFull
|
|
14
|
+
|
|
15
|
+
model = ForecasterRecursiveLGBMFull(iteration=0)
|
|
16
|
+
assert model.name == "lgbm"
|
|
17
|
+
assert model.forecaster is not None
|
|
18
|
+
assert model.n_trials == 10
|
|
19
|
+
print(f"name={model.name}, n_trials={model.n_trials}, iteration={model.iteration}")
|
|
20
|
+
```
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
23
|
from __future__ import annotations
|
|
@@ -35,12 +36,12 @@ class ForecasterRecursiveLGBMFull(
|
|
|
35
36
|
"""LGBM forecaster with real Bayesian tuning and SHAP.
|
|
36
37
|
|
|
37
38
|
Inherits the LightGBM forecaster initialisation from
|
|
38
|
-
|
|
39
|
-
(``spotforecast2-safe``) and adds the real
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
`ForecasterRecursiveLGBM`
|
|
40
|
+
(``spotforecast2-safe``) and adds the real `tune()` and
|
|
41
|
+
`get_global_shap_feature_importance()` from
|
|
42
|
+
`ForecasterRecursiveModelFull`.
|
|
42
43
|
|
|
43
|
-
The MRO ensures that
|
|
44
|
+
The MRO ensures that `tune()` and SHAP methods resolve from
|
|
44
45
|
``ForecasterRecursiveModelFull``, while the LightGBM-specific
|
|
45
46
|
``__init__`` (estimator wiring) comes from ``ForecasterRecursiveLGBM``.
|
|
46
47
|
|
|
@@ -51,25 +52,18 @@ class ForecasterRecursiveLGBMFull(
|
|
|
51
52
|
``predict_size``, ``train_size``).
|
|
52
53
|
|
|
53
54
|
Examples:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
>>> model.name
|
|
57
|
-
'lgbm'
|
|
58
|
-
>>> model.forecaster is not None
|
|
59
|
-
True
|
|
60
|
-
>>> model.n_trials
|
|
61
|
-
10
|
|
62
|
-
>>> model.iteration
|
|
63
|
-
0
|
|
55
|
+
```{python}
|
|
56
|
+
from spotforecast2.models import ForecasterRecursiveLGBMFull
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
58
|
+
model = ForecasterRecursiveLGBMFull(iteration=0)
|
|
59
|
+
assert model.name == "lgbm"
|
|
60
|
+
assert model.forecaster is not None
|
|
61
|
+
assert model.n_trials == 10
|
|
62
|
+
assert model.iteration == 0
|
|
63
|
+
print(f"name={model.name}, n_trials={model.n_trials}, iteration={model.iteration}")
|
|
64
|
+
print(f"tune callable: {callable(model.tune)}")
|
|
65
|
+
print(f"shap callable: {callable(model.get_global_shap_feature_importance)}")
|
|
66
|
+
```
|
|
73
67
|
"""
|
|
74
68
|
|
|
75
69
|
def __init__(self, iteration: int, lags: int = 12, **kwargs: Any):
|