from spotforecast2_safe.data.fetch_data import fetch_data, get_package_data_home
df = fetch_data(filename=str(get_package_data_home() / "demo10.csv"))spotforecast2 multi-task-target demo
Vorbereitung
Bereitstellen des DataFrames df:
Ausschalten der Warnungen:
import warnings
warnings.filterwarnings("ignore")Konfiguration der Parameter und des Cache-Verzeichnisses. Seit dem Refactor 05-2026 lebt jeder Pipeline-Parameter auf einem ConfigMulti (oder ConfigEntsoe) und wird direkt an MultiTask übergeben.
import tempfile
import pandas as pd
from spotforecast2.multitask.multi import MultiTask
from spotforecast2_safe.configurator.config_multi import ConfigMulti
CACHE_HOME = tempfile.mkdtemp()
PROJECT_NAME = "test_001"bounds und agg_weights sind domänenspezifische Kalibrierungen, die der Anwender explizit bereitstellen muss. bounds=None bedeutet kein Clipping; agg_weights=None bedeutet gleichmäßige 1/n-Gewichtung. Für den anonymisierten demo10-Datensatz werden datengetriebene Quantilgrenzen verwendet, da absolute Voreinstellungen je nach Skalierung 20–98 % der Werte als Ausreißer markieren würden.
# Aggregationsgewichte: ein Wert pro Zielspalte (positional); negatives
# Vorzeichen invertiert eine Reihe vor der Aggregation.
# demo10 hat 11 Zielspalten — für andere Datensätze die Länge anpassen.
agg_weights = [1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, -1.0, 1.0]
# Datengetriebene Ausreißergrenzen (0,1 %- / 99,9 %-Quantil je Spalte).
bounds = [
(float(df[c].quantile(0.001)), float(df[c].quantile(0.999)))
for c in df.columns
]Fail-safe Verhalten bei Open-Meteo-Ausfällen
config.on_weather_failure steuert, wie BaseTask.build_exogenous_features auf Open-Meteo-Ausfälle reagiert (z. B. Rate-Limit, 5xx-Antworten, nicht erreichbares Archiv):
"raise"(Default): EineWeatherFetchError-Exception bricht den Lauf ab. Das ist das sichere Standardverhalten — Pipeline und Modellartefakte bleiben konsistent."skip": Der Fehler wird gefangen, eine WARNING wird im Per-Run-Log vermerkt, und die Pipeline läuft ohne Wetterfeatures weiter (config.use_exogenous_features=Truereicht weiter mit Kalender-, Tag- Nacht-, und Feiertagsmerkmalen). Modelle werden nur auf den verbleibenden Kovariaten trainiert.
config_skip = ConfigMulti(
bounds=bounds,
agg_weights=agg_weights,
train_size=pd.Timedelta(days=365),
on_weather_failure="skip",
)
print(config_skip.on_weather_failure)skip
on_weather_failure="skip" mit use_exogenous_features=True kann zu einer Form-Inkonsistenz zwischen Trainings- und Vorhersagezeitpunkt führen: wenn Open-Meteo beim Training erreichbar ist, aber bei der Vorhersage nicht (oder umgekehrt), trainiert das Modell mit Wetter-Spalten und sieht bei predict plötzlich weniger Spalten — LightGBM (und andere Regressor-Backends) verweigern den Aufruf mit The number of features in data (51) is not the same as it was in training data (36).
Für Produktivpfade gibt es zwei sichere Optionen:
- Strikt (
on_weather_failure="raise", Default): Ausfälle stoppen die Pipeline; trainierte Modelle und Vorhersageläufe sehen immer dieselben Spalten. Empfohlen, wenn der Datenanbieter zuverlässig ist und ein unterbrochener Lauf akzeptabel ist. - Vollständig wetterfrei (
use_exogenous_features=Falseoderon_weather_failure="skip"mit beiden Schritten unter derselben Policy): Modell und Prognose werden konsistent ohne Wetter trainiert.
on_weather_failure="skip" ist ideal für isolierte, einmalige Prognosen (z. B. ein interaktives Notebook), in denen kein zuvor trainiertes Modell geladen wird.
Production
Optuna
Tunen der Hyperparameter (wöchentlich):
cfg_optuna = ConfigMulti(
bounds=bounds,
agg_weights=agg_weights,
train_size=pd.Timedelta(days=365),
delta_val=pd.Timedelta(days=7 * 10),
n_trials_optuna=2,
use_exogenous_features=False,
)
cfg_optuna.data_frame_name = PROJECT_NAME
mt_optuna = MultiTask(cfg_optuna, task="optuna", cache_home=CACHE_HOME, log_level=40)
mt_optuna.prepare_data(demo_data=df)
mt_optuna.detect_outliers()
mt_optuna.impute()
mt_optuna.build_exogenous_features()
result_optuna = mt_optuna.run(show=False)
forecast_optuna = result_optuna["future_pred"].to_frame("forecast")
print(forecast_optuna) forecast
2021-12-24 22:00:00+00:00 20532.616843
2021-12-24 23:00:00+00:00 18484.325220
2021-12-25 00:00:00+00:00 18290.980401
2021-12-25 01:00:00+00:00 17950.581132
2021-12-25 02:00:00+00:00 17964.726295
2021-12-25 03:00:00+00:00 17420.335463
2021-12-25 04:00:00+00:00 17505.920060
2021-12-25 05:00:00+00:00 20410.638489
2021-12-25 06:00:00+00:00 21294.183191
2021-12-25 07:00:00+00:00 22532.192199
2021-12-25 08:00:00+00:00 23501.487070
2021-12-25 09:00:00+00:00 23851.439854
2021-12-25 10:00:00+00:00 23890.930737
2021-12-25 11:00:00+00:00 23676.621622
2021-12-25 12:00:00+00:00 23686.748348
2021-12-25 13:00:00+00:00 23505.294413
2021-12-25 14:00:00+00:00 23126.756619
2021-12-25 15:00:00+00:00 23194.048540
2021-12-25 16:00:00+00:00 22744.574090
2021-12-25 17:00:00+00:00 22060.087413
2021-12-25 18:00:00+00:00 22276.042505
2021-12-25 19:00:00+00:00 22808.132984
2021-12-25 20:00:00+00:00 22528.450983
2021-12-25 21:00:00+00:00 23064.591862
Lazy
Model-Fit und Prognose mit den besten Hyperparametern aus Optuna, ohne erneutes Tuning (täglich oder stündlich). Wurde die Optuna-Phase noch nicht ausgeführt, wird lazy automatisch mit den Standardparametern gestartet.
cfg_lazy = ConfigMulti(
bounds=bounds,
agg_weights=agg_weights,
train_size=pd.Timedelta(days=365),
delta_val=pd.Timedelta(days=7 * 10),
use_exogenous_features=False,
)
cfg_lazy.data_frame_name = PROJECT_NAME
mt_lazy = MultiTask(cfg_lazy, task="lazy", cache_home=CACHE_HOME, log_level=40)
mt_lazy.prepare_data(demo_data=df)
mt_lazy.detect_outliers()
mt_lazy.impute()
mt_lazy.build_exogenous_features()
result_lazy = mt_lazy.run(show=False)
forecast_lazy = result_lazy["future_pred"].to_frame("forecast")
print(forecast_lazy) forecast
2021-12-24 22:00:00+00:00 20532.616843
2021-12-24 23:00:00+00:00 18484.325220
2021-12-25 00:00:00+00:00 18290.980401
2021-12-25 01:00:00+00:00 17950.581132
2021-12-25 02:00:00+00:00 17964.726295
2021-12-25 03:00:00+00:00 17420.335463
2021-12-25 04:00:00+00:00 17505.920060
2021-12-25 05:00:00+00:00 20410.638489
2021-12-25 06:00:00+00:00 21294.183191
2021-12-25 07:00:00+00:00 22532.192199
2021-12-25 08:00:00+00:00 23501.487070
2021-12-25 09:00:00+00:00 23851.439854
2021-12-25 10:00:00+00:00 23890.930737
2021-12-25 11:00:00+00:00 23676.621622
2021-12-25 12:00:00+00:00 23686.748348
2021-12-25 13:00:00+00:00 23505.294413
2021-12-25 14:00:00+00:00 23126.756619
2021-12-25 15:00:00+00:00 23194.048540
2021-12-25 16:00:00+00:00 22744.574090
2021-12-25 17:00:00+00:00 22060.087413
2021-12-25 18:00:00+00:00 22276.042505
2021-12-25 19:00:00+00:00 22808.132984
2021-12-25 20:00:00+00:00 22528.450983
2021-12-25 21:00:00+00:00 23064.591862
Defaults
Model-Fit mit den Werks-Default-Parametern des Forecaster-Factories. Im Gegensatz zu lazy werden zwischengespeicherte Tuning-Ergebnisse nicht gelesen — der deterministische Baseline-Pfad (entspricht “Approach 2: Training without Tuning” in der ENTSO-E-Publikation).
cfg_defaults = ConfigMulti(
bounds=bounds,
agg_weights=agg_weights,
train_size=pd.Timedelta(days=365),
delta_val=pd.Timedelta(days=7 * 10),
use_exogenous_features=False,
)
cfg_defaults.data_frame_name = PROJECT_NAME
mt_defaults = MultiTask(
cfg_defaults, task="defaults", cache_home=CACHE_HOME, log_level=40
)
mt_defaults.prepare_data(demo_data=df)
mt_defaults.detect_outliers()
mt_defaults.impute()
mt_defaults.build_exogenous_features()
result_defaults = mt_defaults.run(show=False)
forecast_defaults = result_defaults["future_pred"].to_frame("forecast")
print(forecast_defaults) forecast
2021-12-24 22:00:00+00:00 19784.786913
2021-12-24 23:00:00+00:00 17353.805168
2021-12-25 00:00:00+00:00 12854.053604
2021-12-25 01:00:00+00:00 10898.861621
2021-12-25 02:00:00+00:00 8658.511175
2021-12-25 03:00:00+00:00 8521.080382
2021-12-25 04:00:00+00:00 9705.074603
2021-12-25 05:00:00+00:00 9952.963326
2021-12-25 06:00:00+00:00 16263.598353
2021-12-25 07:00:00+00:00 24437.188460
2021-12-25 08:00:00+00:00 28052.624598
2021-12-25 09:00:00+00:00 30806.464406
2021-12-25 10:00:00+00:00 30609.574705
2021-12-25 11:00:00+00:00 30002.255197
2021-12-25 12:00:00+00:00 29274.890808
2021-12-25 13:00:00+00:00 28891.842562
2021-12-25 14:00:00+00:00 27900.053069
2021-12-25 15:00:00+00:00 27991.913999
2021-12-25 16:00:00+00:00 24929.225126
2021-12-25 17:00:00+00:00 27267.002484
2021-12-25 18:00:00+00:00 25899.305595
2021-12-25 19:00:00+00:00 25384.064327
2021-12-25 20:00:00+00:00 25844.921890
2021-12-25 21:00:00+00:00 22053.735204
Zusätzliche Tasks
Predict
Nur Prognose, ohne Tuning oder Fit. Die Schritte optuna oder lazy müssen bereits einmal gelaufen sein, damit die Ergebnisse im Cache vorliegen.
cfg_predict = ConfigMulti(
bounds=bounds,
agg_weights=agg_weights,
use_exogenous_features=False,
)
cfg_predict.data_frame_name = PROJECT_NAME
mt_predict = MultiTask(
cfg_predict, task="predict", cache_home=CACHE_HOME, log_level=40
)
mt_predict.prepare_data(demo_data=df)
mt_predict.detect_outliers()
mt_predict.impute()
mt_predict.build_exogenous_features()
result_predict = mt_predict.run(show=False)
forecast_predict = result_predict["future_pred"].to_frame("forecast")
print(forecast_predict) forecast
2021-12-24 22:00:00+00:00 20532.616843
2021-12-24 23:00:00+00:00 18484.325220
2021-12-25 00:00:00+00:00 18290.980401
2021-12-25 01:00:00+00:00 17950.581132
2021-12-25 02:00:00+00:00 17964.726295
2021-12-25 03:00:00+00:00 17420.335463
2021-12-25 04:00:00+00:00 17505.920060
2021-12-25 05:00:00+00:00 20410.638489
2021-12-25 06:00:00+00:00 21294.183191
2021-12-25 07:00:00+00:00 22532.192199
2021-12-25 08:00:00+00:00 23501.487070
2021-12-25 09:00:00+00:00 23851.439854
2021-12-25 10:00:00+00:00 23890.930737
2021-12-25 11:00:00+00:00 23676.621622
2021-12-25 12:00:00+00:00 23686.748348
2021-12-25 13:00:00+00:00 23505.294413
2021-12-25 14:00:00+00:00 23126.756619
2021-12-25 15:00:00+00:00 23194.048540
2021-12-25 16:00:00+00:00 22744.574090
2021-12-25 17:00:00+00:00 22060.087413
2021-12-25 18:00:00+00:00 22276.042505
2021-12-25 19:00:00+00:00 22808.132984
2021-12-25 20:00:00+00:00 22528.450983
2021-12-25 21:00:00+00:00 23064.591862
Clean
Löschen der Cache-Daten für das Projekt. Mit dry_run=True wird nur angezeigt, welche Dateien gelöscht würden, ohne sie tatsächlich zu entfernen.
cfg_clean_dry = ConfigMulti()
cfg_clean_dry.data_frame_name = PROJECT_NAME
mt_clean_dry = MultiTask(
cfg_clean_dry, task="clean", cache_home=CACHE_HOME, dry_run=True, log_level=40
)
mt_clean_dry.run()[clean] Dry run — would delete: /tmp/tmpxauvfgid
Would remove: logging
Would remove: models
Would remove: tuning_results
{'status': 'dry_run',
'cache_dir': PosixPath('/tmp/tmpxauvfgid'),
'deleted_items': ['logging', 'models', 'tuning_results']}
cfg_clean = ConfigMulti()
cfg_clean.data_frame_name = PROJECT_NAME
mt_clean = MultiTask(
cfg_clean, task="clean", cache_home=CACHE_HOME, dry_run=False, log_level=40
)
mt_clean.run()[clean] Cache removed successfully: /tmp/tmpxauvfgid
{'status': 'success',
'cache_dir': PosixPath('/tmp/tmpxauvfgid'),
'deleted_items': ['logging', 'models', 'tuning_results']}
Dokumentation der Klasse MultiTask
Die vollständige API-Referenz mit allen Parametern, Pipeline-Methoden und weiteren Beispielen: multitask.MultiTask.
Von MultiTask aufgerufene Funktionen
Aufgelistet werden nur Funktionen und Methoden aus den Paketen spotforecast2 und spotforecast2_safe. Aufrufe in Drittbibliotheken (pandas, numpy, scikit-learn, lightgbm, plotly, joblib, astral, …) sind ausgespart. Das Beispiel folgt dem Pfad task="lazy" (siehe Abschnitt Lazy oben).
Einstiegspunkt
spotforecast2.multitask.multi.MultiTask.__init__spotforecast2_safe.data.fetch_data.get_cache_home(fallscache_homeim Konstruktor übergeben wird)spotforecast2_safe.configurator.config_multi.ConfigMulti(nur fallsconfig is None)config.set_params(**overrides)(sofern Overrides übergeben wurden)spotforecast2.multitask.base.BaseTask.__init__config.set_params(**overrides)(Overrides ausMultiTask)spotforecast2.multitask.base.BaseTask._attach_file_handlerspotforecast2_safe.data.fetch_data.get_cache_home
Phase 1 — prepare_data
spotforecast2.multitask.base.BaseTask.prepare_dataconfig.data_loader(config)(falls weder DataFrame nochdemo_dataübergeben wurde — Hook für ENTSO-E-artige Datenakquisition)spotforecast2_safe.preprocessing.curate_data.reset_indexspotforecast2_safe.preprocessing.curate_data.agg_and_resample_dataspotforecast2_safe.preprocessing.curate_data.basic_ts_checksspotforecast2_safe.preprocessing.curate_data.get_start_end
Phase 2 — detect_outliers
spotforecast2.multitask.base.BaseTask.detect_outliersspotforecast2_safe.preprocessing.outlier.manual_outlier_removalspotforecast2_safe.preprocessing.outlier.get_outliers
Phase 2b — plot_with_outliers (nur falls explizit aufgerufen)
spotforecast2.multitask.base.BaseTask.plot_with_outliersspotforecast2.plots.plotter.plot_with_outliers
Phase 3 — impute
spotforecast2.multitask.base.BaseTask.imputespotforecast2_safe.preprocessing.imputation.apply_imputationspotforecast2_safe.preprocessing.imputation.WeightFunction(beiimputation_method="weighted")
Phase 4 — build_exogenous_features
spotforecast2.multitask.base.BaseTask.build_exogenous_featuresspotforecast2_safe.weather.get_weather_featuresspotforecast2_safe.calendar.get_calendar_featuresspotforecast2_safe.calendar.get_day_night_featuresspotforecast2_safe.calendar.get_holiday_featuresspotforecast2_safe.manager.features.apply_cyclical_encodingspotforecast2_safe.manager.features.create_interaction_featuresspotforecast2_safe.manager.features.select_exogenous_featuresspotforecast2_safe.manager.features.merge_data_and_covariates
Phase 5 — run (Dispatch auf LazyTask via TrainingStrategy)
Seit ADR-001 (Refactor 05-2026) läuft die eigentliche Trainings- und Vorhersageschleife zentral in BaseTask._run_strategy. Die execute_*-Funktionen sind zu dünnen Wrappern geschrumpft, die nur noch die passende Strategie wählen.
spotforecast2.multitask.multi.MultiTask.runspotforecast2.multitask.lazy.LazyTask.runspotforecast2.multitask.lazy.execute_lazyspotforecast2.multitask.strategies.LazyStrategy.__init__spotforecast2.multitask.base.BaseTask._run_strategyspotforecast2.multitask.base.BaseTask._ensure_pipeline_ready- Für jedes Target in
config.targets:spotforecast2.multitask.base.BaseTask._get_target_dataspotforecast2_safe.manager.features.get_target_data
spotforecast2.multitask.base.BaseTask.create_forecasterconfig.forecaster_factory(config, weight_func=..., target=...)(Hook) oder Default:spotforecast2.multitask.factories.default_lgbm_forecaster_factoryspotforecast2_safe.forecaster.recursive.ForecasterRecursive.__init__spotforecast2_safe.preprocessing.RollingFeatures
spotforecast2.multitask.strategies.LazyStrategy.prepare_forecasterspotforecast2.multitask.base.BaseTask.load_tuning_results(fallsuse_tuned_params=Trueund Cache vorhanden)
spotforecast2.multitask.base.BaseTask._train_and_predict_targetspotforecast2_safe.forecaster.recursive.ForecasterRecursive.fitspotforecast2_safe.manager.predictor.build_prediction_packagespotforecast2_safe.forecaster.recursive.ForecasterRecursive.create_train_X_yspotforecast2_safe.forecaster.recursive.ForecasterRecursive.predict
spotforecast2.multitask.base.BaseTask._show_prediction_figure(fallsshow=True)spotforecast2.plots.plotter.make_plot(save=False)
spotforecast2.multitask.base.BaseTask.save_models(fallsauto_save_models=True)spotforecast2.multitask.base.BaseTask._aggregate_and_show- Single-Target-Schnellpfad: das einzige Per-Target-Paket wird unverändert zurückgegeben (kein zusätzlicher Aggregations-Plot).
- Multi-Target-Pfad:
spotforecast2.multitask.base.agg_predictorspotforecast2_safe.processing.agg_predict.agg_predict
spotforecast2.plots.plotter.make_plot(save=False, fallsshow=True)
Für task="optuna" bzw. task="spotoptim" ändert sich nur die Strategie: OptunaStrategy.prepare_forecaster ruft bayesian_search_forecaster mit der Default-Suchraum-Funktion aus spotforecast2.multitask.search_spaces, SpotOptimStrategy.prepare_forecaster analog spotoptim_search_forecaster. Beide speichern die gefundenen besten Parameter über BaseTask.save_tuning_results und liefern einen frisch instanzierten, parametrisierten Forecaster an _train_and_predict_target zurück.
Für task="defaults" ist DefaultsStrategy.prepare_forecaster ein No-Op: der vom Factory erzeugte Forecaster wird unverändert an _train_and_predict_target weitergereicht, ohne den Tuning-Cache zu konsultieren.