openstef-models 4.0.0.dev1__tar.gz → 4.1.0__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.
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/.gitignore +26 -6
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/PKG-INFO +11 -7
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/README.md +2 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/pyproject.toml +22 -14
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/explainability/__init__.py +5 -3
- openstef_models-4.1.0/src/openstef_models/explainability/mixins.py +126 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/explainability/plotters/__init__.py +3 -1
- openstef_models-4.1.0/src/openstef_models/explainability/plotters/contributions_plotter.py +228 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/explainability/plotters/feature_importance_plotter.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/integrations/__init__.py +3 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/integrations/joblib/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/integrations/joblib/joblib_model_serializer.py +4 -4
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/integrations/mlflow/__init__.py +8 -3
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/integrations/mlflow/mlflow_storage.py +118 -14
- openstef_models-4.1.0/src/openstef_models/integrations/mlflow/mlflow_storage_callback.py +383 -0
- openstef_models-4.1.0/src/openstef_models/integrations/optuna/__init__.py +23 -0
- openstef_models-4.1.0/src/openstef_models/integrations/optuna/tuner.py +355 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/mixins/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/mixins/callbacks.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/mixins/model_serializer.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/__init__.py +3 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting/component_splitter.py +2 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting/constant_component_splitter.py +5 -4
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting/linear_component_splitter.py +8 -14
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting/linear_component_splitter_model/linear_component_splitter_model.z.license +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting_model.py +2 -2
- openstef_models-4.1.0/src/openstef_models/models/forecasting/__init__.py +9 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/forecasting/base_case_forecaster.py +47 -69
- openstef_models-4.0.0.dev1/src/openstef_models/models/forecasting/constant_median_forecaster.py → openstef_models-4.1.0/src/openstef_models/models/forecasting/constant_quantile_forecaster.py +47 -63
- openstef_models-4.1.0/src/openstef_models/models/forecasting/flatliner_forecaster.py +111 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/forecasting/forecaster.py +40 -104
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/forecasting/gblinear_forecaster.py +112 -101
- openstef_models-4.1.0/src/openstef_models/models/forecasting/lgbm_forecaster.py +344 -0
- openstef_models-4.1.0/src/openstef_models/models/forecasting/lgbmlinear_forecaster.py +345 -0
- openstef_models-4.1.0/src/openstef_models/models/forecasting/median_forecaster.py +320 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/forecasting/xgboost_forecaster.py +132 -139
- openstef_models-4.1.0/src/openstef_models/models/forecasting_model.py +678 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/presets/__init__.py +6 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/presets/forecasting_workflow.py +283 -77
- openstef_models-4.1.0/src/openstef_models/testing.py +98 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/energy_domain/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/energy_domain/wind_power_feature_adder.py +15 -15
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/__init__.py +12 -4
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/dimensionality_reducer.py +4 -3
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/empty_feature_remover.py +3 -3
- openstef_models-4.1.0/src/openstef_models/transforms/general/flagger.py +97 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/imputer.py +62 -60
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/nan_dropper.py +3 -3
- openstef_models-4.1.0/src/openstef_models/transforms/general/outlier_handler.py +178 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/sample_weighter.py +134 -33
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/general/scaler.py +3 -2
- openstef_models-4.1.0/src/openstef_models/transforms/general/selector.py +83 -0
- openstef_models-4.1.0/src/openstef_models/transforms/general/shifter.py +130 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/postprocessing/__init__.py +3 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/postprocessing/confidence_interval_applicator.py +35 -21
- openstef_models-4.1.0/src/openstef_models/transforms/postprocessing/isotonic_quantile_calibrator.py +229 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/postprocessing/quantile_sorter.py +2 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/time_domain/__init__.py +5 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/time_domain/cyclic_features_adder.py +3 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/time_domain/datetime_features_adder.py +17 -10
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/time_domain/holiday_features_adder.py +35 -12
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/time_domain/lags_adder.py +109 -39
- openstef_models-4.1.0/src/openstef_models/transforms/time_domain/rolling_aggregates_adder.py +166 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/time_domain/versioned_lags_adder.py +12 -6
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/validation/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/validation/completeness_checker.py +19 -19
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/validation/flatline_checker.py +28 -25
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/validation/input_consistency_checker.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/weather_domain/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/weather_domain/atmosphere_derived_features_adder.py +2 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/weather_domain/daylight_feature_adder.py +2 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/transforms/weather_domain/radiation_derived_features_adder.py +17 -16
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/utils/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/utils/data_split.py +12 -6
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/utils/evaluation_functions.py +3 -3
- openstef_models-4.1.0/src/openstef_models/utils/feature_selection.py +214 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/utils/loss_functions.py +16 -14
- openstef_models-4.1.0/src/openstef_models/utils/multi_quantile_regressor.py +157 -0
- openstef_models-4.1.0/src/openstef_models/utils/xgboost.py +45 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/workflows/__init__.py +1 -1
- openstef_models-4.1.0/src/openstef_models/workflows/callbacks/__init__.py +13 -0
- openstef_models-4.1.0/src/openstef_models/workflows/callbacks/data_save.py +108 -0
- openstef_models-4.1.0/src/openstef_models/workflows/callbacks/model_performance_callback.py +92 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/workflows/custom_component_split_workflow.py +4 -4
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/workflows/custom_forecasting_workflow.py +88 -17
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/integration/test_integration.py +1 -1
- openstef_models-4.1.0/tests/unit/explainability/plotters/test_contributions_plotter.py +219 -0
- {openstef_models-4.0.0.dev1/tests/unit/integrations/joblib → openstef_models-4.1.0/tests/unit/integrations}/__init__.py +1 -1
- {openstef_models-4.0.0.dev1/tests/unit/integrations → openstef_models-4.1.0/tests/unit/integrations/joblib}/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/integrations/joblib/test_joblib_model_serializer.py +1 -1
- {openstef_models-4.0.0.dev1/tests/unit/models/component_splitting → openstef_models-4.1.0/tests/unit/integrations/mlflow}/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/integrations/mlflow/test_mlflow_storage.py +76 -11
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/integrations/mlflow/test_mlflow_storage_callback.py +53 -19
- openstef_models-4.1.0/tests/unit/integrations/optuna/test_tuner.py +306 -0
- {openstef_models-4.0.0.dev1/tests/unit/utils → openstef_models-4.1.0/tests/unit/models/component_splitting}/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/models/component_splitting/test_constant_component_splitter.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/models/component_splitting/test_linear_component_splitter.py +7 -7
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/models/forecasting/conftest.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/models/forecasting/test_base_case_forecaster.py +41 -29
- openstef_models-4.1.0/tests/unit/models/forecasting/test_constant_quantile_forecaster.py +135 -0
- openstef_models-4.1.0/tests/unit/models/forecasting/test_flatliner_forecaster.py +66 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/models/forecasting/test_gblinear_forecaster.py +39 -14
- openstef_models-4.1.0/tests/unit/models/forecasting/test_lgbm_forecaster.py +173 -0
- openstef_models-4.1.0/tests/unit/models/forecasting/test_lgbmlinear_forecaster.py +147 -0
- openstef_models-4.1.0/tests/unit/models/forecasting/test_median_forecaster.py +460 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/models/forecasting/test_xgboost_forecaster.py +41 -16
- openstef_models-4.1.0/tests/unit/models/test_forecasting_model.py +460 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/test_example.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/energy_domain/test_wind_power_feature_adder.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/general/test_dimensionality_reducer.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/general/test_empty_feature_remover.py +1 -1
- openstef_models-4.1.0/tests/unit/transforms/general/test_flagger.py +61 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/general/test_imputer.py +2 -2
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/general/test_nan_dropper.py +1 -1
- openstef_models-4.1.0/tests/unit/transforms/general/test_outlier_handler.py +235 -0
- openstef_models-4.1.0/tests/unit/transforms/general/test_sample_weighter.py +280 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/general/test_scaler.py +2 -2
- openstef_models-4.1.0/tests/unit/transforms/general/test_selector.py +82 -0
- openstef_models-4.1.0/tests/unit/transforms/general/test_shifter.py +174 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/postprocessing/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/postprocessing/test_confidence_interval_applicator.py +43 -1
- openstef_models-4.1.0/tests/unit/transforms/postprocessing/test_isotonic_quantile_calibrator.py +200 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/postprocessing/test_quantile_sorter.py +1 -1
- openstef_models-4.1.0/tests/unit/transforms/time_domain/__init__.py +0 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/time_domain/test_cyclic_features_adder.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/time_domain/test_datetime_features_adder.py +30 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/time_domain/test_holiday_features_adder.py +36 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/time_domain/test_lags_adder.py +186 -15
- openstef_models-4.1.0/tests/unit/transforms/time_domain/test_rolling_aggregates_adder.py +284 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/time_domain/test_versioned_lags_adder.py +54 -44
- openstef_models-4.1.0/tests/unit/transforms/validation/__init__.py +0 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/validation/test_completeness_checker.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/validation/test_flatline_checker.py +20 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/validation/test_input_consistency_checker.py +1 -1
- openstef_models-4.1.0/tests/unit/transforms/weather_domain/__init__.py +0 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/weather_domain/test_atmosphere_derived_features_adder.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/weather_domain/test_daylight_feature_adder.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/transforms/weather_domain/test_radiation_derived_featuers_adder.py +8 -31
- {openstef_models-4.0.0.dev1/tests/unit/integrations/mlflow → openstef_models-4.1.0/tests/unit/utils}/__init__.py +1 -1
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/utils/test_data_split.py +2 -2
- openstef_models-4.1.0/tests/unit/utils/test_feature_selection.py +125 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/utils/test_loss_functions.py +69 -6
- openstef_models-4.1.0/tests/unit/utils/test_multi_quantile_regressor.py +118 -0
- openstef_models-4.1.0/tests/unit/workflows/__init__.py +0 -0
- openstef_models-4.1.0/tests/unit/workflows/callbacks/__init__.py +0 -0
- openstef_models-4.1.0/tests/unit/workflows/callbacks/test_model_performance_callback.py +89 -0
- openstef_models-4.1.0/tests/unit/workflows/test_custom_forecasting_workflow.py +134 -0
- openstef_models-4.0.0.dev1/src/openstef_models/explainability/mixins.py +0 -58
- openstef_models-4.0.0.dev1/src/openstef_models/integrations/mlflow/mlflow_storage_callback.py +0 -239
- openstef_models-4.0.0.dev1/src/openstef_models/models/forecasting/__init__.py +0 -26
- openstef_models-4.0.0.dev1/src/openstef_models/models/forecasting/flatliner_forecaster.py +0 -104
- openstef_models-4.0.0.dev1/src/openstef_models/models/forecasting_model.py +0 -389
- openstef_models-4.0.0.dev1/src/openstef_models/transforms/general/clipper.py +0 -121
- openstef_models-4.0.0.dev1/src/openstef_models/transforms/time_domain/rolling_aggregates_adder.py +0 -126
- openstef_models-4.0.0.dev1/src/openstef_models/utils/feature_selection.py +0 -100
- openstef_models-4.0.0.dev1/tests/unit/models/forecasting/test_constant_median_forecaster.py +0 -92
- openstef_models-4.0.0.dev1/tests/unit/models/forecasting/test_flatliner_forecaster.py +0 -32
- openstef_models-4.0.0.dev1/tests/unit/models/test_forecasting_model.py +0 -241
- openstef_models-4.0.0.dev1/tests/unit/transforms/general/test_clipper.py +0 -116
- openstef_models-4.0.0.dev1/tests/unit/transforms/general/test_sample_weighter.py +0 -97
- openstef_models-4.0.0.dev1/tests/unit/transforms/time_domain/test_rolling_aggregates_adder.py +0 -126
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/models/component_splitting/linear_component_splitter_model/linear_component_splitter_model.z +0 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/integration/__init__.py +0 -0
- {openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/tests/unit/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/models → openstef_models-4.1.0/tests/unit/explainability}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/models/forecasting → openstef_models-4.1.0/tests/unit/explainability/plotters}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/transforms → openstef_models-4.1.0/tests/unit/integrations/optuna}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/transforms/energy_domain → openstef_models-4.1.0/tests/unit/models}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/transforms/general → openstef_models-4.1.0/tests/unit/models/forecasting}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/transforms/time_domain → openstef_models-4.1.0/tests/unit/transforms}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/transforms/validation → openstef_models-4.1.0/tests/unit/transforms/energy_domain}/__init__.py +0 -0
- {openstef_models-4.0.0.dev1/tests/unit/transforms/weather_domain → openstef_models-4.1.0/tests/unit/transforms/general}/__init__.py +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <openstef@lfenergy.org> # noqa E501>
|
|
2
2
|
# SPDX-License-Identifier: MPL-2.0
|
|
3
3
|
|
|
4
4
|
# Core
|
|
@@ -35,10 +35,6 @@ MANIFEST
|
|
|
35
35
|
# Ruff
|
|
36
36
|
.ruff_cache/
|
|
37
37
|
|
|
38
|
-
# Pyright
|
|
39
|
-
.pyright/
|
|
40
|
-
# pyright-report/
|
|
41
|
-
|
|
42
38
|
# Test, coverage, tox
|
|
43
39
|
.pytest_cache/
|
|
44
40
|
.coverage
|
|
@@ -67,6 +63,14 @@ dmypy.json
|
|
|
67
63
|
# Sphinx
|
|
68
64
|
docs/_build/
|
|
69
65
|
docs/source/api/generated/
|
|
66
|
+
docs/source/tutorials/
|
|
67
|
+
docs/source/benchmarks/
|
|
68
|
+
# Community health files materialized from OpenSTEF/.github at build time
|
|
69
|
+
docs/source/contribute/_community/
|
|
70
|
+
docs/source/user_guide/**/quick_start_tutorial.py
|
|
71
|
+
docs/source/user_guide/**/feature_engineering_tutorial.py
|
|
72
|
+
docs/source/user_guide/**/datasets_tutorial.py
|
|
73
|
+
docs/source/user_guide/**/backtesting_tutorial.py
|
|
70
74
|
|
|
71
75
|
# docs/_doctrees/
|
|
72
76
|
# docs/_static_gen/
|
|
@@ -124,4 +128,20 @@ certificates/
|
|
|
124
128
|
*.pkl
|
|
125
129
|
|
|
126
130
|
# Benchmark outputs
|
|
127
|
-
benchmark_results*/
|
|
131
|
+
benchmark_results*/
|
|
132
|
+
|
|
133
|
+
# Local dataset files
|
|
134
|
+
liander_dataset/
|
|
135
|
+
|
|
136
|
+
# Deployment example run artifacts (MLflow store, forecasts, dataset, Celery/Airflow state)
|
|
137
|
+
openstef_deployment_runs/
|
|
138
|
+
|
|
139
|
+
# Mlflow
|
|
140
|
+
/mlflow
|
|
141
|
+
/mlflow_artifacts_local
|
|
142
|
+
|
|
143
|
+
.github/instructions
|
|
144
|
+
|
|
145
|
+
# Jupyter notebook cache (myst-nb execution outputs)
|
|
146
|
+
.jupyter_cache/
|
|
147
|
+
docs/build.zip
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openstef-models
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.0
|
|
4
4
|
Summary: Core models for OpenSTEF
|
|
5
5
|
Project-URL: Documentation, https://openstef.github.io/openstef/index.html
|
|
6
6
|
Project-URL: Homepage, https://lfenergy.org/projects/openstef/
|
|
7
7
|
Project-URL: Issues, https://github.com/OpenSTEF/openstef/issues
|
|
8
8
|
Project-URL: Repository, https://github.com/OpenSTEF/openstef
|
|
9
|
-
Author-email: "Alliander N.V" <
|
|
9
|
+
Author-email: "Alliander N.V" <openstef@lfenergy.org>
|
|
10
10
|
License-Expression: MPL-2.0
|
|
11
11
|
Keywords: energy,forecasting,machinelearning
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -18,12 +18,16 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
18
18
|
Requires-Python: <4.0,>=3.12
|
|
19
19
|
Requires-Dist: holidays>=0.79
|
|
20
20
|
Requires-Dist: mlflow-skinny<4,>=3
|
|
21
|
-
Requires-Dist: openstef-beam<5,>=4
|
|
22
|
-
Requires-Dist: openstef-core<5,>=4
|
|
21
|
+
Requires-Dist: openstef-beam<5,>=4
|
|
22
|
+
Requires-Dist: openstef-core<5,>=4
|
|
23
23
|
Requires-Dist: pvlib>=0.13
|
|
24
24
|
Requires-Dist: pycountry>=24.6.1
|
|
25
|
-
Requires-Dist: scikit-learn<
|
|
25
|
+
Requires-Dist: scikit-learn<1.8,>=1.7.1
|
|
26
26
|
Requires-Dist: scipy<2,>=1.16.3
|
|
27
|
+
Provides-Extra: lgbm
|
|
28
|
+
Requires-Dist: lightgbm>=4.6; extra == 'lgbm'
|
|
29
|
+
Provides-Extra: tuning
|
|
30
|
+
Requires-Dist: optuna>=4.7; extra == 'tuning'
|
|
27
31
|
Provides-Extra: xgb-cpu
|
|
28
32
|
Requires-Dist: xgboost-cpu<4,>=3; (sys_platform == 'linux' or sys_platform == 'win32') and extra == 'xgb-cpu'
|
|
29
33
|
Requires-Dist: xgboost<4,>=3; (sys_platform == 'darwin') and extra == 'xgb-cpu'
|
|
@@ -32,9 +36,9 @@ Requires-Dist: xgboost<4,>=3; extra == 'xgb-gpu'
|
|
|
32
36
|
Description-Content-Type: text/markdown
|
|
33
37
|
|
|
34
38
|
<!--
|
|
35
|
-
SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
39
|
+
SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
36
40
|
|
|
37
41
|
SPDX-License-Identifier: MPL-2.0
|
|
38
42
|
-->
|
|
39
43
|
|
|
40
|
-
# openstef-model
|
|
44
|
+
# openstef-model
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
2
|
+
SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
3
3
|
|
|
4
4
|
SPDX-License-Identifier: MPL-2.0
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
|
-
# openstef-model
|
|
7
|
+
# openstef-model
|
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
|
|
5
4
|
[build-system]
|
|
6
5
|
build-backend = "hatchling.build"
|
|
7
|
-
|
|
8
6
|
requires = [ "hatchling" ]
|
|
9
7
|
|
|
10
8
|
[project]
|
|
11
9
|
name = "openstef-models"
|
|
12
|
-
version = "4.
|
|
10
|
+
version = "4.1.0"
|
|
13
11
|
description = "Core models for OpenSTEF"
|
|
14
12
|
readme = "README.md"
|
|
15
13
|
keywords = [ "energy", "forecasting", "machinelearning" ]
|
|
16
14
|
license = "MPL-2.0"
|
|
17
15
|
authors = [
|
|
18
|
-
{ name = "Alliander N.V", email = "
|
|
16
|
+
{ name = "Alliander N.V", email = "openstef@lfenergy.org" },
|
|
19
17
|
]
|
|
20
18
|
requires-python = ">=3.12,<4.0"
|
|
21
19
|
classifiers = [
|
|
@@ -26,29 +24,39 @@ classifiers = [
|
|
|
26
24
|
"Programming Language :: Python :: 3.13",
|
|
27
25
|
"Programming Language :: Python :: 3.14",
|
|
28
26
|
]
|
|
29
|
-
|
|
30
27
|
dependencies = [
|
|
31
28
|
"holidays>=0.79",
|
|
32
29
|
"mlflow-skinny>=3,<4",
|
|
33
|
-
"openstef-beam>=4
|
|
34
|
-
"openstef-core>=4
|
|
30
|
+
"openstef-beam>=4,<5",
|
|
31
|
+
"openstef-core>=4,<5",
|
|
35
32
|
"pvlib>=0.13",
|
|
36
33
|
"pycountry>=24.6.1",
|
|
37
|
-
"scikit-learn>=1.7.1,<
|
|
34
|
+
"scikit-learn>=1.7.1,<1.8",
|
|
38
35
|
"scipy>=1.16.3,<2",
|
|
39
36
|
]
|
|
40
|
-
|
|
37
|
+
optional-dependencies.lgbm = [
|
|
38
|
+
"lightgbm>=4.6",
|
|
39
|
+
]
|
|
40
|
+
optional-dependencies.tuning = [ "optuna>=4.7" ]
|
|
41
41
|
optional-dependencies.xgb-cpu = [
|
|
42
42
|
"xgboost>=3,<4; sys_platform=='darwin'",
|
|
43
43
|
"xgboost-cpu>=3,<4; sys_platform=='linux' or sys_platform=='win32'",
|
|
44
44
|
]
|
|
45
|
-
|
|
46
45
|
optional-dependencies.xgb-gpu = [ "xgboost>=3,<4" ]
|
|
47
|
-
|
|
48
46
|
urls.Documentation = "https://openstef.github.io/openstef/index.html"
|
|
49
47
|
urls.Homepage = "https://lfenergy.org/projects/openstef/"
|
|
50
48
|
urls.Issues = "https://github.com/OpenSTEF/openstef/issues"
|
|
51
49
|
urls.Repository = "https://github.com/OpenSTEF/openstef"
|
|
52
50
|
|
|
53
|
-
[tool.hatch
|
|
54
|
-
packages = [ "src/openstef_models" ]
|
|
51
|
+
[tool.hatch]
|
|
52
|
+
build.targets.wheel.packages = [ "src/openstef_models" ]
|
|
53
|
+
|
|
54
|
+
[tool.uv]
|
|
55
|
+
# xgb-cpu installs xgboost-cpu (Linux/Windows) and xgb-gpu installs the full
|
|
56
|
+
# xgboost; both provide the same import, so exactly one may be installed.
|
|
57
|
+
conflicts = [
|
|
58
|
+
[
|
|
59
|
+
{ extra = "xgb-cpu" },
|
|
60
|
+
{ extra = "xgb-gpu" },
|
|
61
|
+
],
|
|
62
|
+
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
"""Core models for OpenSTEF."""
|
{openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/explainability/__init__.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
Tools for feature importance, attribution and model interpretation.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from .mixins import ExplainableForecaster
|
|
11
|
-
from .plotters import FeatureImportancePlotter
|
|
10
|
+
from .mixins import ContributionsMixin, ExplainableForecaster
|
|
11
|
+
from .plotters import ContributionsPlotter, FeatureImportancePlotter
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
|
+
"ContributionsMixin",
|
|
15
|
+
"ContributionsPlotter",
|
|
14
16
|
"ExplainableForecaster",
|
|
15
17
|
"FeatureImportancePlotter",
|
|
16
18
|
]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""Mixins for adding explainability features to forecasting models.
|
|
6
|
+
|
|
7
|
+
Provides base classes that enable models to expose feature importance scores
|
|
8
|
+
and generate visualization plots.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
from typing import Any, Literal
|
|
13
|
+
|
|
14
|
+
import pandas as pd
|
|
15
|
+
import plotly.graph_objects as go
|
|
16
|
+
|
|
17
|
+
from openstef_core.datasets import ForecastInputDataset, TimeSeriesDataset
|
|
18
|
+
from openstef_core.types import Q, Quantile
|
|
19
|
+
from openstef_models.explainability.plotters.contributions_plotter import ContributionsPlotter
|
|
20
|
+
from openstef_models.explainability.plotters.feature_importance_plotter import FeatureImportancePlotter
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ExplainableForecaster(ABC):
|
|
24
|
+
"""Mixin for forecasters that can explain feature importance.
|
|
25
|
+
|
|
26
|
+
Provides a standardized interface for accessing and visualizing feature
|
|
27
|
+
importance scores across different forecasting models.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def feature_importances(self) -> pd.DataFrame:
|
|
33
|
+
"""Get feature importance scores for this model.
|
|
34
|
+
|
|
35
|
+
Returns DataFrame with feature names as index and quantiles as columns.
|
|
36
|
+
Each quantile represents the importance distribution across multiple
|
|
37
|
+
model training runs or folds.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
DataFrame with feature names as index and quantile columns.
|
|
41
|
+
Values represent normalized importance scores summing to 1.0.
|
|
42
|
+
|
|
43
|
+
Note:
|
|
44
|
+
The returned DataFrame must have feature names as index and quantile
|
|
45
|
+
columns in format 'quantile_PXX' (e.g., 'quantile_P50', 'quantile_P95').
|
|
46
|
+
All quantile values must be between 0 and 1.
|
|
47
|
+
"""
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
def plot_feature_importances(self, quantile: Quantile = Q(0.5)) -> go.Figure:
|
|
51
|
+
"""Create interactive treemap visualization of feature importances.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
quantile: Which quantile of importance scores to display.
|
|
55
|
+
Defaults to median (0.5).
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Plotly Figure containing treemap with feature importance scores.
|
|
59
|
+
Color intensity indicates relative importance of each feature.
|
|
60
|
+
"""
|
|
61
|
+
return FeatureImportancePlotter().plot(scores=self.feature_importances, quantile=quantile)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ContributionsMixin(ABC):
|
|
65
|
+
"""Mixin for forecasters that can explain per-sample feature contributions.
|
|
66
|
+
|
|
67
|
+
Unlike ``ExplainableForecaster`` which provides aggregate feature importance,
|
|
68
|
+
this mixin provides per-sample decomposition of predictions — i.e., how
|
|
69
|
+
much each feature contributed to the prediction for each individual sample.
|
|
70
|
+
|
|
71
|
+
For tree-based models (XGBoost), this corresponds to SHAP TreeExplainer values.
|
|
72
|
+
For linear models (GBLinear), this is the coefficient x feature value decomposition.
|
|
73
|
+
For ensembles, this shows each base model's contribution weight.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def predict_contributions(self, data: ForecastInputDataset) -> TimeSeriesDataset:
|
|
78
|
+
"""Compute per-sample feature contributions for the given input data.
|
|
79
|
+
|
|
80
|
+
Returns a TimeSeriesDataset where columns are feature names (or model
|
|
81
|
+
names for ensemble contributions) and rows correspond to the same time
|
|
82
|
+
index as the input. Values represent the additive contribution of each
|
|
83
|
+
feature to the prediction at that timestep.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
data: Preprocessed input data (same format as ``predict()`` takes).
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
TimeSeriesDataset with feature contributions. Columns are features,
|
|
90
|
+
rows are timesteps. A ``bias`` column may be included for the
|
|
91
|
+
model intercept/base value.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def plot_contributions(
|
|
95
|
+
self,
|
|
96
|
+
data: ForecastInputDataset,
|
|
97
|
+
kind: Literal["heatmap", "waterfall", "bar"] = "heatmap",
|
|
98
|
+
**kwargs: Any,
|
|
99
|
+
) -> go.Figure:
|
|
100
|
+
"""Plot per-sample feature contributions.
|
|
101
|
+
|
|
102
|
+
Calls ``predict_contributions()`` and visualizes the result using the
|
|
103
|
+
requested chart type.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
data: Preprocessed input data.
|
|
107
|
+
kind: Chart type — ``"heatmap"``, ``"waterfall"``, or ``"bar"``.
|
|
108
|
+
**kwargs: Forwarded to the corresponding plotter method
|
|
109
|
+
(e.g. ``top_n``, ``timestep``).
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
Plotly Figure.
|
|
113
|
+
|
|
114
|
+
Raises:
|
|
115
|
+
ValueError: If *kind* is not one of the supported chart types.
|
|
116
|
+
"""
|
|
117
|
+
contributions = self.predict_contributions(data)
|
|
118
|
+
plotters = {
|
|
119
|
+
"heatmap": ContributionsPlotter.plot_heatmap,
|
|
120
|
+
"waterfall": ContributionsPlotter.plot_waterfall,
|
|
121
|
+
"bar": ContributionsPlotter.plot_bar,
|
|
122
|
+
}
|
|
123
|
+
if kind not in plotters:
|
|
124
|
+
msg = f"Unknown plot kind {kind!r}. Choose from {list(plotters)}"
|
|
125
|
+
raise ValueError(msg)
|
|
126
|
+
return plotters[kind](contributions=contributions, **kwargs)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
|
|
@@ -8,8 +8,10 @@ Provides plotters for creating interactive visualizations of feature importance
|
|
|
8
8
|
scores and other model explanation outputs.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
+
from .contributions_plotter import ContributionsPlotter
|
|
11
12
|
from .feature_importance_plotter import FeatureImportancePlotter
|
|
12
13
|
|
|
13
14
|
__all__ = [
|
|
15
|
+
"ContributionsPlotter",
|
|
14
16
|
"FeatureImportancePlotter",
|
|
15
17
|
]
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""Visualizations for per-sample feature contributions (SHAP values)."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
import plotly.graph_objects as go
|
|
12
|
+
from plotly.subplots import make_subplots
|
|
13
|
+
|
|
14
|
+
from openstef_core.datasets import TimeSeriesDataset # noqa: TC001
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
import pandas as pd
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ContributionsPlotter:
|
|
21
|
+
"""Visualizations for per-timestep feature contributions."""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def plot_heatmap(
|
|
25
|
+
contributions: TimeSeriesDataset,
|
|
26
|
+
top_n: int = 10,
|
|
27
|
+
target_column: str = "load",
|
|
28
|
+
bias_column: str = "bias",
|
|
29
|
+
*,
|
|
30
|
+
show_prediction: bool = True,
|
|
31
|
+
) -> go.Figure:
|
|
32
|
+
"""Create an interactive heatmap of feature contributions over time.
|
|
33
|
+
|
|
34
|
+
X-axis is the prediction datetime, Y-axis shows feature names ranked by mean absolute contribution
|
|
35
|
+
(most important at top). Color ranges from blue (negative) through white (zero) to red (positive).
|
|
36
|
+
When ``show_prediction`` is True a line plot of the model prediction (sum of contributions + bias)
|
|
37
|
+
is shown above the heatmap.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
contributions: Output of ``predict_contributions()``.
|
|
41
|
+
top_n: Number of top features to show (ranked by mean absolute contribution).
|
|
42
|
+
target_column: Name of the target column to exclude. Default "load".
|
|
43
|
+
bias_column: Name of the bias column. Default "bias".
|
|
44
|
+
show_prediction: If True, add a prediction line subplot above the heatmap. Default True.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Plotly Figure with a diverging heatmap centered at zero (and optional prediction line).
|
|
48
|
+
"""
|
|
49
|
+
bias = contributions.data[bias_column] if bias_column in contributions.data.columns else None
|
|
50
|
+
cols_to_drop = [c for c in [target_column, bias_column] if c in contributions.data.columns]
|
|
51
|
+
df = contributions.data.drop(columns=cols_to_drop)
|
|
52
|
+
ranked: list[str] = df.abs().mean().sort_values(ascending=False).head(top_n).index.tolist()
|
|
53
|
+
|
|
54
|
+
# Most-important feature at top of Y-axis
|
|
55
|
+
y_labels = list(reversed(ranked))
|
|
56
|
+
|
|
57
|
+
heatmap = go.Heatmap(
|
|
58
|
+
z=df[y_labels].T.values,
|
|
59
|
+
x=df.index,
|
|
60
|
+
y=y_labels,
|
|
61
|
+
colorscale="RdBu_r",
|
|
62
|
+
zmid=0,
|
|
63
|
+
colorbar={"title": "Contribution"},
|
|
64
|
+
showlegend=False,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if show_prediction:
|
|
68
|
+
prediction = df.sum(axis=1)
|
|
69
|
+
if bias is not None:
|
|
70
|
+
prediction += bias
|
|
71
|
+
|
|
72
|
+
fig = make_subplots(
|
|
73
|
+
rows=2,
|
|
74
|
+
cols=1,
|
|
75
|
+
shared_xaxes=True,
|
|
76
|
+
row_heights=[0.2, 0.8],
|
|
77
|
+
vertical_spacing=0.03,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
fig.add_trace(
|
|
81
|
+
go.Scatter(
|
|
82
|
+
x=df.index,
|
|
83
|
+
y=prediction,
|
|
84
|
+
mode="lines",
|
|
85
|
+
name="Prediction",
|
|
86
|
+
line={"color": "black", "width": 1.5},
|
|
87
|
+
showlegend=False,
|
|
88
|
+
),
|
|
89
|
+
row=1,
|
|
90
|
+
col=1,
|
|
91
|
+
)
|
|
92
|
+
fig.add_trace(heatmap, row=2, col=1)
|
|
93
|
+
|
|
94
|
+
fig.update_layout(
|
|
95
|
+
yaxis_title="Prediction",
|
|
96
|
+
yaxis2_title="Feature",
|
|
97
|
+
xaxis2_title="Time",
|
|
98
|
+
margin={"t": 30, "r": 10, "b": 40, "l": 120},
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
fig = go.Figure(
|
|
102
|
+
data=heatmap,
|
|
103
|
+
layout={
|
|
104
|
+
"xaxis_title": "Time",
|
|
105
|
+
"yaxis_title": "Feature",
|
|
106
|
+
"margin": {"t": 30, "r": 10, "b": 40, "l": 120},
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return fig
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def plot_waterfall(
|
|
114
|
+
contributions: TimeSeriesDataset,
|
|
115
|
+
timestep: int = 0,
|
|
116
|
+
top_n: int = 10,
|
|
117
|
+
target_column: str = "load",
|
|
118
|
+
bias_column: str = "bias",
|
|
119
|
+
) -> go.Figure:
|
|
120
|
+
"""Create a waterfall chart decomposing a single timestep's prediction.
|
|
121
|
+
|
|
122
|
+
Shows how the bias (base value) is pushed up or down by each feature's
|
|
123
|
+
contribution to arrive at the final prediction.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
contributions: Output of ``predict_contributions()``.
|
|
127
|
+
timestep: Row index (0-based) of the timestep to explain.
|
|
128
|
+
top_n: Number of top features to show. Remaining features are
|
|
129
|
+
aggregated into an "other" bar.
|
|
130
|
+
target_column: Name of the target column to exclude. Default "load".
|
|
131
|
+
bias_column: Name of the bias column used as base value. Default "bias".
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Plotly Figure with waterfall chart.
|
|
135
|
+
"""
|
|
136
|
+
bias = contributions.data[bias_column] if bias_column in contributions.data.columns else None
|
|
137
|
+
cols_to_drop = [c for c in [target_column, bias_column] if c in contributions.data.columns]
|
|
138
|
+
df = contributions.data.drop(columns=cols_to_drop)
|
|
139
|
+
row = df.iloc[timestep]
|
|
140
|
+
base_value = float(bias.iloc[timestep]) if bias is not None else 0.0
|
|
141
|
+
|
|
142
|
+
# Rank by |contribution| for this specific timestep
|
|
143
|
+
abs_sorted = row.abs().sort_values(ascending=False)
|
|
144
|
+
top = abs_sorted.head(top_n).index.tolist()
|
|
145
|
+
remaining = [c for c in abs_sorted.index if c not in top]
|
|
146
|
+
|
|
147
|
+
names: list[str] = [bias_column]
|
|
148
|
+
values: list[float] = [base_value]
|
|
149
|
+
measures: list[str] = ["absolute"]
|
|
150
|
+
|
|
151
|
+
for feat in top:
|
|
152
|
+
names.append(feat)
|
|
153
|
+
values.append(float(row[feat]))
|
|
154
|
+
measures.append("relative")
|
|
155
|
+
|
|
156
|
+
if len(remaining) > 0:
|
|
157
|
+
other_sum = float(row[remaining].sum())
|
|
158
|
+
names.append(f"other ({len(remaining)})")
|
|
159
|
+
values.append(other_sum)
|
|
160
|
+
measures.append("relative")
|
|
161
|
+
|
|
162
|
+
names.append("Prediction")
|
|
163
|
+
values.append(base_value + float(row.sum()))
|
|
164
|
+
measures.append("total")
|
|
165
|
+
|
|
166
|
+
timestamp = contributions.data.index[timestep]
|
|
167
|
+
return go.Figure(
|
|
168
|
+
go.Waterfall(
|
|
169
|
+
x=names,
|
|
170
|
+
y=values,
|
|
171
|
+
measure=measures,
|
|
172
|
+
connector={"line": {"color": "grey", "width": 0.5}},
|
|
173
|
+
increasing={"marker": {"color": "#ff4136"}},
|
|
174
|
+
decreasing={"marker": {"color": "#0074d9"}},
|
|
175
|
+
totals={"marker": {"color": "#2ecc40"}},
|
|
176
|
+
textposition="outside",
|
|
177
|
+
text=[f"{v:+.4f}" if m == "relative" else f"{v:.4f}" for v, m in zip(values, measures, strict=True)],
|
|
178
|
+
),
|
|
179
|
+
layout={
|
|
180
|
+
"title": f"Contributions at {timestamp}",
|
|
181
|
+
"yaxis_title": "Contribution",
|
|
182
|
+
"margin": {"t": 50, "r": 10, "b": 40, "l": 60},
|
|
183
|
+
"showlegend": False,
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def plot_bar(
|
|
189
|
+
contributions: TimeSeriesDataset,
|
|
190
|
+
top_n: int = 10,
|
|
191
|
+
target_column: str = "load",
|
|
192
|
+
bias_column: str = "bias",
|
|
193
|
+
) -> go.Figure:
|
|
194
|
+
"""Create a horizontal bar chart of mean absolute contributions per feature.
|
|
195
|
+
|
|
196
|
+
Features are ranked from most to least important (top to bottom).
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
contributions: Output of ``predict_contributions()``.
|
|
200
|
+
top_n: Number of top features to show.
|
|
201
|
+
target_column: Name of the target column to exclude. Default "load".
|
|
202
|
+
bias_column: Name of the bias column to exclude. Default "bias".
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Plotly Figure with horizontal bar chart.
|
|
206
|
+
"""
|
|
207
|
+
cols_to_drop = [c for c in [target_column, bias_column] if c in contributions.data.columns]
|
|
208
|
+
df = contributions.data.drop(columns=cols_to_drop)
|
|
209
|
+
mean_abs: pd.Series = df.abs().mean().sort_values(ascending=False).head(top_n)
|
|
210
|
+
|
|
211
|
+
# Reverse for plotly (bottom-to-top rendering)
|
|
212
|
+
mean_abs = mean_abs.iloc[::-1]
|
|
213
|
+
|
|
214
|
+
return go.Figure(
|
|
215
|
+
go.Bar(
|
|
216
|
+
x=mean_abs.values,
|
|
217
|
+
y=mean_abs.index.tolist(),
|
|
218
|
+
orientation="h",
|
|
219
|
+
marker_color="#1f77b4",
|
|
220
|
+
hovertemplate="<b>%{y}</b><br>mean |SHAP|: %{x:.4f}<extra></extra>",
|
|
221
|
+
),
|
|
222
|
+
layout={
|
|
223
|
+
"xaxis_title": "mean |SHAP value|",
|
|
224
|
+
"yaxis_title": "Feature",
|
|
225
|
+
"margin": {"t": 30, "r": 10, "b": 40, "l": 120},
|
|
226
|
+
"showlegend": False,
|
|
227
|
+
},
|
|
228
|
+
)
|
{openstef_models-4.0.0.dev1 → openstef_models-4.1.0}/src/openstef_models/integrations/__init__.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
|
|
@@ -8,3 +8,5 @@ Contains implementations for callbacks and storage systems that hook into and
|
|
|
8
8
|
extend OpenSTEF functionality by integrating with external systems such as
|
|
9
9
|
monitoring tools, databases, cloud storage, and custom processing pipelines.
|
|
10
10
|
"""
|
|
11
|
+
|
|
12
|
+
__all__ = ["joblib", "mlflow", "optuna"]
|
|
@@ -6,7 +6,7 @@ the local filesystem, making it suitable for development, testing, and
|
|
|
6
6
|
single-machine deployments.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
9
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
10
10
|
#
|
|
11
11
|
# SPDX-License-Identifier: MPL-2.0
|
|
12
12
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
"""Local model storage implementation using joblib serialization.
|
|
@@ -45,7 +45,7 @@ class JoblibModelSerializer(ModelSerializer):
|
|
|
45
45
|
- Load operations fail with ModelNotFoundError if model file doesn't exist
|
|
46
46
|
|
|
47
47
|
Example:
|
|
48
|
-
Basic usage with model persistence
|
|
48
|
+
Basic usage with model persistence
|
|
49
49
|
|
|
50
50
|
>>> from pathlib import Path
|
|
51
51
|
>>> from openstef_models.models.forecasting_model import ForecastingModel
|
|
@@ -58,11 +58,11 @@ class JoblibModelSerializer(ModelSerializer):
|
|
|
58
58
|
|
|
59
59
|
@override
|
|
60
60
|
def serialize(self, model: object, file: BinaryIO) -> None:
|
|
61
|
-
joblib.dump(model, file)
|
|
61
|
+
joblib.dump(model, file)
|
|
62
62
|
|
|
63
63
|
@override
|
|
64
64
|
def deserialize(self, file: BinaryIO) -> object:
|
|
65
|
-
return joblib.load(file)
|
|
65
|
+
return joblib.load(file)
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
__all__ = ["JoblibModelSerializer"]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
2
|
#
|
|
3
3
|
# SPDX-License-Identifier: MPL-2.0
|
|
4
4
|
|
|
@@ -16,6 +16,11 @@ Note:
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from .mlflow_storage import MLFlowStorage
|
|
19
|
-
from .mlflow_storage_callback import
|
|
19
|
+
from .mlflow_storage_callback import (
|
|
20
|
+
MLFlowStorageCallback,
|
|
21
|
+
)
|
|
20
22
|
|
|
21
|
-
__all__ = [
|
|
23
|
+
__all__ = [
|
|
24
|
+
"MLFlowStorage",
|
|
25
|
+
"MLFlowStorageCallback",
|
|
26
|
+
]
|