openstef-beam 4.0.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_beam-4.0.0/.gitignore +146 -0
- openstef_beam-4.0.0/PKG-INFO +39 -0
- openstef_beam-4.0.0/README.md +7 -0
- openstef_beam-4.0.0/pyproject.toml +57 -0
- openstef_beam-4.0.0/src/openstef_beam/__init__.py +43 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/__init__.py +32 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/analysis_pipeline.py +194 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/models/__init__.py +24 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/models/target_metadata.py +97 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/models/visualization_aggregation.py +61 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/models/visualization_output.py +93 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/__init__.py +27 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/forecast_time_series_plotter.py +683 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/grouped_target_metric_plotter.py +173 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/precision_recall_curve_plotter.py +151 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/quantile_calibration_box_plotter.py +196 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/quantile_probability_plotter.py +153 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/summary_table_plotter.py +80 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/plots/windowed_metric_plotter.py +141 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/__init__.py +33 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/base.py +250 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/grouped_target_metric_visualization.py +351 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/precision_recall_curve_visualization.py +195 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/quantile_calibration_box_visualization.py +240 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/quantile_probability_visualization.py +221 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/summary_table_visualization.py +311 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/timeseries_visualization.py +155 -0
- openstef_beam-4.0.0/src/openstef_beam/analysis/visualizations/windowed_metric_visualization.py +429 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/__init__.py +23 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_callback.py +9 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_event.py +57 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_event_generator.py +207 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_forecaster/__init__.py +25 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_forecaster/dummy_forecaster.py +73 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_forecaster/mixins.py +236 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/backtest_pipeline.py +301 -0
- openstef_beam-4.0.0/src/openstef_beam/backtesting/restricted_horizon_timeseries.py +72 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/__init__.py +55 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/baselines/__init__.py +17 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/baselines/openstef4.py +243 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/benchmark_comparison_pipeline.py +255 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/benchmark_pipeline.py +420 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/benchmarks/__init__.py +14 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/benchmarks/liander2024.py +239 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/callbacks/__init__.py +10 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/callbacks/base.py +230 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/callbacks/strict_execution_callback.py +39 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/models/__init__.py +9 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/models/benchmark_target.py +116 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/storage/__init__.py +16 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/storage/base.py +254 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/storage/local_storage.py +163 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/storage/s3_storage.py +193 -0
- openstef_beam-4.0.0/src/openstef_beam/benchmarking/target_provider.py +412 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/__init__.py +43 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/evaluation_pipeline.py +316 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/metric_providers.py +753 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/models/__init__.py +21 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/models/report.py +150 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/models/subset.py +100 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/models/window.py +132 -0
- openstef_beam-4.0.0/src/openstef_beam/evaluation/window_iterators.py +117 -0
- openstef_beam-4.0.0/src/openstef_beam/metrics/__init__.py +58 -0
- openstef_beam-4.0.0/src/openstef_beam/metrics/metrics_deterministic.py +625 -0
- openstef_beam-4.0.0/src/openstef_beam/metrics/metrics_probabilistic.py +273 -0
- openstef_beam-4.0.0/tests/__init__.py +3 -0
- openstef_beam-4.0.0/tests/unit/__init__.py +3 -0
- openstef_beam-4.0.0/tests/unit/analysis/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_forecast_time_series_plotter.py +467 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_grouped_target_metric_plotter.py +114 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_precision_recall_curve_plotter.py +100 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_quantile_calibration_box_plotter.py +114 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_quantile_probability_plotter.py +124 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_summary_table_plotter.py +25 -0
- openstef_beam-4.0.0/tests/unit/analysis/plots/test_windowed_metric_plotter.py +91 -0
- openstef_beam-4.0.0/tests/unit/analysis/test_analysis_pipeline.py +296 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/conftest.py +194 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_grouped_target_metric_visualization.py +468 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_precision_recall_curve_visualization.py +217 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_quantile_calibration_box_visualization.py +282 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_quantile_probability_visualization.py +241 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_summary_table_visualization.py +186 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_timeseries_visualization.py +162 -0
- openstef_beam-4.0.0/tests/unit/analysis/visualizations/test_windowed_metric_visualization.py +496 -0
- openstef_beam-4.0.0/tests/unit/backtesting/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/backtesting/test_backtest_event_generator.py +220 -0
- openstef_beam-4.0.0/tests/unit/backtesting/test_backtest_pipeline.py +382 -0
- openstef_beam-4.0.0/tests/unit/backtesting/test_batch_prediction.py +212 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/baselines/__init__.py +3 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/baselines/test_openstef4.py +182 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/storage/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/storage/test_local_storage.py +300 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/storage/test_s3_storage.py +287 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/test_benchmark_pipeline.py +443 -0
- openstef_beam-4.0.0/tests/unit/benchmarking/test_target_provider.py +158 -0
- openstef_beam-4.0.0/tests/unit/evaluation/__init__.py +3 -0
- openstef_beam-4.0.0/tests/unit/evaluation/models/__init__.py +3 -0
- openstef_beam-4.0.0/tests/unit/evaluation/models/test_window.py +72 -0
- openstef_beam-4.0.0/tests/unit/evaluation/test_evaluation_pipeline.py +153 -0
- openstef_beam-4.0.0/tests/unit/evaluation/test_metric_provider.py +123 -0
- openstef_beam-4.0.0/tests/unit/evaluation/test_window_iterators.py +87 -0
- openstef_beam-4.0.0/tests/unit/metrics/__init__.py +0 -0
- openstef_beam-4.0.0/tests/unit/metrics/test_metrics_deterministic.py +590 -0
- openstef_beam-4.0.0/tests/unit/metrics/test_metrics_probabilistic.py +186 -0
- openstef_beam-4.0.0/tests/utils/__init__.py +0 -0
- openstef_beam-4.0.0/tests/utils/mocks.py +109 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <openstef@lfenergy.org> # noqa E501>
|
|
2
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
3
|
+
|
|
4
|
+
# Core
|
|
5
|
+
config.user.yaml
|
|
6
|
+
git-template
|
|
7
|
+
.DS_Store
|
|
8
|
+
tmp
|
|
9
|
+
|
|
10
|
+
# Python bytecode
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
|
|
15
|
+
# C extensions
|
|
16
|
+
*.so
|
|
17
|
+
|
|
18
|
+
# Packaging and build artifacts
|
|
19
|
+
.Python
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
downloads/
|
|
23
|
+
wheels/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
sdist/
|
|
26
|
+
.eggs/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
*.egg
|
|
29
|
+
.installed.cfg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# uv
|
|
33
|
+
.uv/
|
|
34
|
+
|
|
35
|
+
# Ruff
|
|
36
|
+
.ruff_cache/
|
|
37
|
+
|
|
38
|
+
# Pyright
|
|
39
|
+
.pyright/
|
|
40
|
+
# pyright-report/
|
|
41
|
+
|
|
42
|
+
# Test, coverage, tox
|
|
43
|
+
.pytest_cache/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
htmlcov/
|
|
47
|
+
.cover/
|
|
48
|
+
cover/
|
|
49
|
+
pytest-report.xml
|
|
50
|
+
.tox/
|
|
51
|
+
.nox/
|
|
52
|
+
coverage.xml
|
|
53
|
+
|
|
54
|
+
# Hypothesis
|
|
55
|
+
.hypothesis/
|
|
56
|
+
|
|
57
|
+
# Cython
|
|
58
|
+
cython_debug/
|
|
59
|
+
|
|
60
|
+
# Type checkers (misc)
|
|
61
|
+
.mypy_cache/
|
|
62
|
+
.dmypy.json
|
|
63
|
+
dmypy.json
|
|
64
|
+
.pytype/
|
|
65
|
+
.pyre/
|
|
66
|
+
|
|
67
|
+
# Sphinx
|
|
68
|
+
docs/_build/
|
|
69
|
+
docs/source/api/generated/
|
|
70
|
+
docs/source/tutorials/
|
|
71
|
+
docs/source/benchmarks/
|
|
72
|
+
docs/source/user_guide/**/quick_start_tutorial.py
|
|
73
|
+
docs/source/user_guide/**/feature_engineering_tutorial.py
|
|
74
|
+
docs/source/user_guide/**/datasets_tutorial.py
|
|
75
|
+
docs/source/user_guide/**/backtesting_tutorial.py
|
|
76
|
+
|
|
77
|
+
# docs/_doctrees/
|
|
78
|
+
# docs/_static_gen/
|
|
79
|
+
|
|
80
|
+
# Editors: JetBrains
|
|
81
|
+
.idea/
|
|
82
|
+
|
|
83
|
+
# Editors: VS Code (allow shared configs)
|
|
84
|
+
.vscode/*
|
|
85
|
+
|
|
86
|
+
# Jupyter / IPython
|
|
87
|
+
.ipynb_checkpoints
|
|
88
|
+
profile_default/
|
|
89
|
+
ipython_config.py
|
|
90
|
+
|
|
91
|
+
# Spyder / Rope
|
|
92
|
+
.spyderproject
|
|
93
|
+
.spyproject
|
|
94
|
+
.ropeproject
|
|
95
|
+
|
|
96
|
+
# Environments
|
|
97
|
+
.env
|
|
98
|
+
.venv
|
|
99
|
+
env/
|
|
100
|
+
venv/
|
|
101
|
+
ENV/
|
|
102
|
+
env.bak/
|
|
103
|
+
venv.bak/
|
|
104
|
+
|
|
105
|
+
# PEP 582
|
|
106
|
+
__pypackages__/
|
|
107
|
+
|
|
108
|
+
# web frameworks / services
|
|
109
|
+
*.sqlite
|
|
110
|
+
*.sqlite3
|
|
111
|
+
*.log
|
|
112
|
+
instance/
|
|
113
|
+
.webassets-cache
|
|
114
|
+
celerybeat-schedule
|
|
115
|
+
celerybeat.pid
|
|
116
|
+
*.sage.py
|
|
117
|
+
tmp/
|
|
118
|
+
|
|
119
|
+
# PyInstaller
|
|
120
|
+
*.manifest
|
|
121
|
+
*.spec
|
|
122
|
+
|
|
123
|
+
# Project outputs
|
|
124
|
+
output/
|
|
125
|
+
prof/
|
|
126
|
+
certificates/
|
|
127
|
+
|
|
128
|
+
# Output artifacts
|
|
129
|
+
*.html
|
|
130
|
+
*.pkl
|
|
131
|
+
|
|
132
|
+
# Benchmark outputs
|
|
133
|
+
benchmark_results*/
|
|
134
|
+
|
|
135
|
+
# Local dataset files
|
|
136
|
+
liander_dataset/
|
|
137
|
+
|
|
138
|
+
# Mlflow
|
|
139
|
+
/mlflow
|
|
140
|
+
/mlflow_artifacts_local
|
|
141
|
+
|
|
142
|
+
.github/instructions
|
|
143
|
+
|
|
144
|
+
# Jupyter notebook cache (myst-nb execution outputs)
|
|
145
|
+
.jupyter_cache/
|
|
146
|
+
docs/build.zip
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openstef-beam
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: Backtesting, Evaluation, Analysis and Metrics (BEAM) library for OpenSTEF
|
|
5
|
+
Project-URL: Documentation, https://openstef.github.io/openstef/index.html
|
|
6
|
+
Project-URL: Homepage, https://lfenergy.org/projects/openstef/
|
|
7
|
+
Project-URL: Issues, https://github.com/OpenSTEF/openstef/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/OpenSTEF/openstef
|
|
9
|
+
Author-email: "Alliander N.V" <openstef@lfenergy.org>
|
|
10
|
+
License-Expression: MPL-2.0
|
|
11
|
+
Keywords: energy,forecasting,machinelearning
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: <4.0,>=3.12
|
|
19
|
+
Requires-Dist: openstef-core<5,>=4.0.0.dev0
|
|
20
|
+
Requires-Dist: plotly>=6.3
|
|
21
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
22
|
+
Requires-Dist: scoringrules>=0.8
|
|
23
|
+
Requires-Dist: tqdm>=4.67.1
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: openstef-meta<5,>=4.0.0.dev0; extra == 'all'
|
|
26
|
+
Requires-Dist: openstef-models<5,>=4.0.0.dev0; extra == 'all'
|
|
27
|
+
Requires-Dist: s3fs>=2025.5.1; extra == 'all'
|
|
28
|
+
Provides-Extra: baselines
|
|
29
|
+
Requires-Dist: openstef-meta<5,>=4.0.0.dev0; extra == 'baselines'
|
|
30
|
+
Requires-Dist: openstef-models<5,>=4.0.0.dev0; extra == 'baselines'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
<!--
|
|
34
|
+
SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
35
|
+
|
|
36
|
+
SPDX-License-Identifier: MPL-2.0
|
|
37
|
+
-->
|
|
38
|
+
|
|
39
|
+
# openstef-beam
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
[build-system]
|
|
6
|
+
build-backend = "hatchling.build"
|
|
7
|
+
|
|
8
|
+
requires = [ "hatchling" ]
|
|
9
|
+
|
|
10
|
+
[project]
|
|
11
|
+
name = "openstef-beam"
|
|
12
|
+
version = "4.0.0"
|
|
13
|
+
description = "Backtesting, Evaluation, Analysis and Metrics (BEAM) library for OpenSTEF"
|
|
14
|
+
readme = "README.md"
|
|
15
|
+
keywords = [ "energy", "forecasting", "machinelearning" ]
|
|
16
|
+
license = "MPL-2.0"
|
|
17
|
+
authors = [
|
|
18
|
+
{ name = "Alliander N.V", email = "openstef@lfenergy.org" },
|
|
19
|
+
]
|
|
20
|
+
requires-python = ">=3.12,<4.0"
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 5 - Production/Stable",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
dependencies = [
|
|
31
|
+
"openstef-core>=4.0.0.dev0,<5",
|
|
32
|
+
"plotly>=6.3",
|
|
33
|
+
"pyyaml>=6.0.2",
|
|
34
|
+
"scoringrules>=0.8",
|
|
35
|
+
"tqdm>=4.67.1",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
optional-dependencies.all = [
|
|
39
|
+
"openstef-beam[baselines]",
|
|
40
|
+
"s3fs>=2025.5.1",
|
|
41
|
+
]
|
|
42
|
+
optional-dependencies.baselines = [
|
|
43
|
+
"openstef-meta>=4.0.0.dev0,<5",
|
|
44
|
+
"openstef-models>=4.0.0.dev0,<5",
|
|
45
|
+
]
|
|
46
|
+
urls.Documentation = "https://openstef.github.io/openstef/index.html"
|
|
47
|
+
urls.Homepage = "https://lfenergy.org/projects/openstef/"
|
|
48
|
+
urls.Issues = "https://github.com/OpenSTEF/openstef/issues"
|
|
49
|
+
urls.Repository = "https://github.com/OpenSTEF/openstef"
|
|
50
|
+
|
|
51
|
+
[tool.hatch.build.targets.wheel]
|
|
52
|
+
packages = [ "src/openstef_beam" ]
|
|
53
|
+
|
|
54
|
+
[tool.uv.sources]
|
|
55
|
+
openstef-core = { workspace = true }
|
|
56
|
+
openstef-models = { workspace = true }
|
|
57
|
+
openstef-meta = { workspace = true }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""OpenSTEF BEAM: Backtesting, Evaluation, Analysis and Metrics framework.
|
|
6
|
+
|
|
7
|
+
BEAM provides all the tools to test energy forecasting models under realistic conditions.
|
|
8
|
+
Instead of simple validation that can mislead, BEAM simulates real-world scenarios using
|
|
9
|
+
versioned data - ensuring models only use information available at prediction time.
|
|
10
|
+
|
|
11
|
+
Key features:
|
|
12
|
+
|
|
13
|
+
**Real-world simulation**: Uses versioned data to prevent data leakage. Models are
|
|
14
|
+
retrained periodically and can only access historical information, just like in production.
|
|
15
|
+
|
|
16
|
+
**Flexible integration**: Plug in your own forecasting models, create custom metrics,
|
|
17
|
+
design specific visualizations, and select relevant lead times for your use case.
|
|
18
|
+
|
|
19
|
+
**Complete workflow**: Backtesting → Evaluation → Analysis → Benchmarking. From testing
|
|
20
|
+
individual models to comparing multiple approaches across different energy targets.
|
|
21
|
+
|
|
22
|
+
**Lead time analysis**: Evaluate how forecast quality changes from 1-hour to 48-hour
|
|
23
|
+
predictions, critical for operational planning in energy systems.
|
|
24
|
+
|
|
25
|
+
Use BEAM to:
|
|
26
|
+
|
|
27
|
+
- Test models under realistic operational constraints
|
|
28
|
+
- Compare forecasting approaches with versioned data integrity
|
|
29
|
+
- Generate flexible reports tailored to your metrics and visualizations
|
|
30
|
+
- Ensure evaluation results match real-world deployment performance
|
|
31
|
+
|
|
32
|
+
BEAM's versioned data approach and flexible architecture make it the reliable choice
|
|
33
|
+
for energy forecasting model validation that translates to production success.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
|
|
38
|
+
# Set up logging configuration
|
|
39
|
+
root_logger = logging.getLogger(name=__name__)
|
|
40
|
+
if not root_logger.handlers:
|
|
41
|
+
root_logger.addHandler(logging.NullHandler())
|
|
42
|
+
|
|
43
|
+
__all__ = []
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""Turns evaluation results into visualizations and reports for decision making.
|
|
6
|
+
|
|
7
|
+
Numbers and metrics are hard to interpret. This module creates charts, plots, and
|
|
8
|
+
summary reports that help you understand model performance and make decisions about
|
|
9
|
+
which models to deploy. It automatically generates the visualizations most relevant
|
|
10
|
+
for energy forecasting operations.
|
|
11
|
+
|
|
12
|
+
What you get:
|
|
13
|
+
|
|
14
|
+
- Performance charts: See how models compare across different metrics
|
|
15
|
+
- Time series plots: Visualize predictions vs actual load over time
|
|
16
|
+
- Error analysis: Understand when and why models make mistakes
|
|
17
|
+
- Comparison reports: Side-by-side model performance analysis
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from openstef_beam.analysis import plots, visualizations
|
|
21
|
+
from openstef_beam.analysis.analysis_pipeline import AnalysisConfig, AnalysisPipeline
|
|
22
|
+
from openstef_beam.analysis.models import AnalysisOutput, AnalysisScope, VisualizationOutput
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"AnalysisConfig",
|
|
26
|
+
"AnalysisOutput",
|
|
27
|
+
"AnalysisPipeline",
|
|
28
|
+
"AnalysisScope",
|
|
29
|
+
"VisualizationOutput",
|
|
30
|
+
"plots",
|
|
31
|
+
"visualizations",
|
|
32
|
+
]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""Analysis pipeline for generating visualizations from evaluation reports.
|
|
6
|
+
|
|
7
|
+
This module provides the core pipeline that orchestrates visualization generation
|
|
8
|
+
from evaluation reports at different aggregation levels. It integrates with the
|
|
9
|
+
benchmarking framework to provide consistent analysis outputs across benchmark runs.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from collections import defaultdict
|
|
13
|
+
from collections.abc import Sequence
|
|
14
|
+
|
|
15
|
+
from pydantic import Field
|
|
16
|
+
|
|
17
|
+
from openstef_beam.analysis.models import (
|
|
18
|
+
AnalysisAggregation,
|
|
19
|
+
AnalysisOutput,
|
|
20
|
+
AnalysisScope,
|
|
21
|
+
GroupName,
|
|
22
|
+
TargetMetadata,
|
|
23
|
+
VisualizationOutput,
|
|
24
|
+
)
|
|
25
|
+
from openstef_beam.analysis.visualizations import ReportTuple, VisualizationProvider
|
|
26
|
+
from openstef_beam.evaluation import EvaluationReport
|
|
27
|
+
from openstef_beam.evaluation.models import Filtering
|
|
28
|
+
from openstef_core.base_model import BaseConfig
|
|
29
|
+
from openstef_core.utils.itertools import groupby
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AnalysisConfig(BaseConfig):
|
|
33
|
+
"""Configuration for the analytics pipeline."""
|
|
34
|
+
|
|
35
|
+
visualization_providers: list[VisualizationProvider] = Field(
|
|
36
|
+
default=[], description="List of visualization providers to use for generating analysis outputs"
|
|
37
|
+
)
|
|
38
|
+
filterings: list[Filtering] | None = Field(
|
|
39
|
+
default=None,
|
|
40
|
+
description="When set, only include these filterings (e.g. LeadTime, AvailableAt) in analysis. "
|
|
41
|
+
"None means use all filterings found in the evaluation data.",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AnalysisPipeline:
|
|
46
|
+
"""Orchestrates the generation of visualizations from evaluation reports.
|
|
47
|
+
|
|
48
|
+
The pipeline processes evaluation reports at different aggregation levels:
|
|
49
|
+
|
|
50
|
+
- Individual targets: Creates detailed visualizations for single targets
|
|
51
|
+
- Multiple targets: Creates comparative visualizations across target groups
|
|
52
|
+
|
|
53
|
+
It integrates with the benchmarking framework by being called from BenchmarkPipeline
|
|
54
|
+
after evaluation is complete, ensuring consistent visualization generation across
|
|
55
|
+
all benchmark runs.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
config: AnalysisConfig,
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Initialize the analysis pipeline with configuration.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
config: Analysis configuration containing visualization providers.
|
|
66
|
+
"""
|
|
67
|
+
super().__init__()
|
|
68
|
+
self.config = config
|
|
69
|
+
|
|
70
|
+
def _group_by_filtering(
|
|
71
|
+
self,
|
|
72
|
+
reports: Sequence[tuple[TargetMetadata, EvaluationReport]],
|
|
73
|
+
) -> dict[Filtering, list[ReportTuple]]:
|
|
74
|
+
"""Group reports by their lead time filtering conditions.
|
|
75
|
+
|
|
76
|
+
Organizes evaluation reports based on their lead time criteria (e.g.,
|
|
77
|
+
1-hour ahead vs 24-hour ahead forecasts), enabling comparison of model
|
|
78
|
+
performance across different forecasting horizons.
|
|
79
|
+
|
|
80
|
+
When ``config.filterings`` is set, only subsets matching those filterings are included.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Dictionary mapping lead time filtering conditions to lists of report tuples.
|
|
84
|
+
"""
|
|
85
|
+
allowed = set(self.config.filterings) if self.config.filterings is not None else None
|
|
86
|
+
return groupby(
|
|
87
|
+
(subset.filtering, (base_metadata.with_filtering(subset.filtering), subset))
|
|
88
|
+
for base_metadata, report in reports
|
|
89
|
+
for subset in report.subset_reports
|
|
90
|
+
if allowed is None or subset.filtering in allowed
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def run_for_subsets(
|
|
94
|
+
self,
|
|
95
|
+
reports: list[ReportTuple],
|
|
96
|
+
aggregation: AnalysisAggregation,
|
|
97
|
+
) -> list[VisualizationOutput]:
|
|
98
|
+
"""Generate visualizations for a set of evaluation reports at a specific aggregation level.
|
|
99
|
+
|
|
100
|
+
Processes the provided evaluation reports through all configured visualization
|
|
101
|
+
providers that support the requested aggregation level. Only providers that
|
|
102
|
+
declare support for the aggregation are used.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
reports: List of (metadata, evaluation_subset_report) tuples to visualize.
|
|
106
|
+
The metadata provides context about the target and run, while the
|
|
107
|
+
evaluation report contains the metrics and predictions to visualize.
|
|
108
|
+
aggregation: The aggregation level determining how reports are grouped
|
|
109
|
+
and compared in visualizations.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
List of visualization outputs from all applicable providers. Empty if
|
|
113
|
+
no providers support the requested aggregation level.
|
|
114
|
+
"""
|
|
115
|
+
return [
|
|
116
|
+
provider.create(reports=reports, aggregation=aggregation)
|
|
117
|
+
for provider in self.config.visualization_providers
|
|
118
|
+
if aggregation in provider.supported_aggregations
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
def run_for_reports(
|
|
122
|
+
self,
|
|
123
|
+
reports: Sequence[tuple[TargetMetadata, EvaluationReport]],
|
|
124
|
+
scope: AnalysisScope,
|
|
125
|
+
) -> AnalysisOutput:
|
|
126
|
+
"""Generate visualizations for evaluation reports within a specific scope.
|
|
127
|
+
|
|
128
|
+
Groups reports by lead time filtering conditions and generates visualizations
|
|
129
|
+
for each group using all configured visualization providers that support the
|
|
130
|
+
scope's aggregation level. This enables comparing model performance across
|
|
131
|
+
different forecasting horizons (e.g., short-term vs long-term predictions).
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
reports: List of (metadata, evaluation_report) tuples to visualize.
|
|
135
|
+
scope: Analysis scope defining aggregation level and context.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Analysis output containing all generated visualizations grouped by
|
|
139
|
+
lead time filtering conditions.
|
|
140
|
+
"""
|
|
141
|
+
grouped = self._group_by_filtering(reports)
|
|
142
|
+
|
|
143
|
+
result: dict[Filtering, list[VisualizationOutput]] = defaultdict(list)
|
|
144
|
+
for filtering, subset_reports in grouped.items():
|
|
145
|
+
visualizations = self.run_for_subsets(
|
|
146
|
+
reports=subset_reports,
|
|
147
|
+
aggregation=scope.aggregation,
|
|
148
|
+
)
|
|
149
|
+
result[filtering].extend(visualizations)
|
|
150
|
+
|
|
151
|
+
return AnalysisOutput(
|
|
152
|
+
scope=scope,
|
|
153
|
+
visualizations=result,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def run_for_groups(
|
|
157
|
+
self,
|
|
158
|
+
reports: Sequence[tuple[TargetMetadata, EvaluationReport]],
|
|
159
|
+
scope: AnalysisScope,
|
|
160
|
+
) -> dict[GroupName, AnalysisOutput]:
|
|
161
|
+
"""Generate visualizations for multiple target groups at a specific aggregation level.
|
|
162
|
+
|
|
163
|
+
This method processes all evaluation reports, grouping them by their target
|
|
164
|
+
group names and generating visualizations for each group.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
reports: List of (metadata, evaluation_report) tuples to visualize.
|
|
168
|
+
The metadata provides context about the target and run, while the
|
|
169
|
+
evaluation report contains the metrics and predictions to visualize.
|
|
170
|
+
scope: The analytics scope defining how reports are grouped and aggregated.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Dictionary mapping group names to their corresponding AnalyticsOutput.
|
|
174
|
+
"""
|
|
175
|
+
reports_by_group: dict[GroupName, list[tuple[TargetMetadata, EvaluationReport]]] = groupby(
|
|
176
|
+
(report[0].group_name, report) for report in reports
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
result: dict[GroupName, AnalysisOutput] = {}
|
|
180
|
+
for group_name, group_reports in reports_by_group.items():
|
|
181
|
+
if not group_reports:
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
result[group_name] = self.run_for_reports(
|
|
185
|
+
reports=group_reports,
|
|
186
|
+
scope=AnalysisScope(
|
|
187
|
+
aggregation=scope.aggregation,
|
|
188
|
+
target_name=scope.target_name,
|
|
189
|
+
run_name=scope.run_name,
|
|
190
|
+
group_name=group_name,
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return result
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""Data models for analysis pipeline components.
|
|
6
|
+
|
|
7
|
+
This package provides data models and type definitions for the analysis pipeline,
|
|
8
|
+
including aggregation types, output structures, and metadata containers.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from openstef_beam.analysis.models.target_metadata import GroupName, RunName, TargetMetadata, TargetName
|
|
12
|
+
from openstef_beam.analysis.models.visualization_aggregation import AnalysisAggregation
|
|
13
|
+
from openstef_beam.analysis.models.visualization_output import AnalysisOutput, AnalysisScope, VisualizationOutput
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"AnalysisAggregation",
|
|
17
|
+
"AnalysisOutput",
|
|
18
|
+
"AnalysisScope",
|
|
19
|
+
"GroupName",
|
|
20
|
+
"RunName",
|
|
21
|
+
"TargetMetadata",
|
|
22
|
+
"TargetName",
|
|
23
|
+
"VisualizationOutput",
|
|
24
|
+
]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
|
|
5
|
+
"""Target metadata models for analysis pipeline.
|
|
6
|
+
|
|
7
|
+
This module defines the metadata structure that carries target information
|
|
8
|
+
through the analysis pipeline, including grouping and filtering context.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Self
|
|
12
|
+
|
|
13
|
+
from pydantic import Field, model_validator
|
|
14
|
+
|
|
15
|
+
from openstef_beam.evaluation.models import Filtering
|
|
16
|
+
from openstef_core.base_model import BaseModel
|
|
17
|
+
|
|
18
|
+
type TargetName = str
|
|
19
|
+
type GroupName = str
|
|
20
|
+
type RunName = str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TargetMetadata(BaseModel):
|
|
24
|
+
"""Metadata for a forecasting target in the analysis pipeline.
|
|
25
|
+
|
|
26
|
+
Contains essential information about a target including its grouping context
|
|
27
|
+
and lead time filtering criteria. Lead time filtering determines which
|
|
28
|
+
predictions are included based on how far ahead they were made (e.g.,
|
|
29
|
+
1-hour ahead vs 24-hour ahead forecasts).
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
ValueError: When limit constraints are not met (either 'limit' alone or both
|
|
33
|
+
'upper_limit' and 'lower_limit' must be specified).
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
name: TargetName = Field(description="Name of the target")
|
|
37
|
+
group_name: GroupName = Field(description="Name of the group this target belongs to, used for grouping in reports")
|
|
38
|
+
filtering: Filtering | None = Field(
|
|
39
|
+
description="Lead time filtering criteria - either AvailableAt (data availability time) "
|
|
40
|
+
"or LeadTime (forecast horizon)"
|
|
41
|
+
)
|
|
42
|
+
limit: float | None = Field(default=None, description="Capacity limit of the target in appropriate units")
|
|
43
|
+
upper_limit: float | None = Field(
|
|
44
|
+
default=None, description="Upper capacity limit of the target in appropriate units"
|
|
45
|
+
)
|
|
46
|
+
lower_limit: float | None = Field(
|
|
47
|
+
default=None, description="Lower capacity limit of the target in appropriate units"
|
|
48
|
+
)
|
|
49
|
+
run_name: RunName = Field(description="Name of the run associated with this target")
|
|
50
|
+
|
|
51
|
+
@model_validator(mode="after")
|
|
52
|
+
def validate_limits(self) -> "TargetMetadata":
|
|
53
|
+
"""Validate that either limit or both upper_limit and lower_limit are provided.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
The validated TargetMetadata instance.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
ValueError: If neither limit nor (upper_limit and lower_limit) are provided,
|
|
60
|
+
or if both limit and (upper_limit or lower_limit) are provided.
|
|
61
|
+
"""
|
|
62
|
+
has_limit = self.limit is not None
|
|
63
|
+
has_upper = self.upper_limit is not None
|
|
64
|
+
has_lower = self.lower_limit is not None
|
|
65
|
+
|
|
66
|
+
if has_limit and (has_upper or has_lower):
|
|
67
|
+
raise ValueError(
|
|
68
|
+
"Cannot specify both 'limit' and 'upper_limit'/'lower_limit'. "
|
|
69
|
+
"Use either 'limit' alone or both 'upper_limit' and 'lower_limit'."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if not has_limit and not (has_upper and has_lower):
|
|
73
|
+
raise ValueError("Must specify either 'limit' or both 'upper_limit' and 'lower_limit'.")
|
|
74
|
+
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def with_filtering(self, filtering: Filtering) -> Self:
|
|
78
|
+
"""Returns a copy of the target metadata with different lead time filtering applied.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
filtering: New lead time filtering criteria to apply
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
New TargetMetadata instance with updated lead time filtering
|
|
85
|
+
"""
|
|
86
|
+
return type(self)(
|
|
87
|
+
name=self.name,
|
|
88
|
+
group_name=self.group_name,
|
|
89
|
+
filtering=filtering,
|
|
90
|
+
limit=self.limit,
|
|
91
|
+
upper_limit=self.upper_limit,
|
|
92
|
+
lower_limit=self.lower_limit,
|
|
93
|
+
run_name=self.run_name,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
__all__ = ["GroupName", "RunName", "TargetMetadata", "TargetName"]
|