openstef-core 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.
Files changed (51) hide show
  1. openstef_core-4.0.0/.gitignore +146 -0
  2. openstef_core-4.0.0/PKG-INFO +35 -0
  3. openstef_core-4.0.0/README.md +7 -0
  4. openstef_core-4.0.0/pyproject.toml +49 -0
  5. openstef_core-4.0.0/src/openstef_core/__init__.py +4 -0
  6. openstef_core-4.0.0/src/openstef_core/base_model.py +205 -0
  7. openstef_core-4.0.0/src/openstef_core/constants.py +10 -0
  8. openstef_core-4.0.0/src/openstef_core/datasets/__init__.py +37 -0
  9. openstef_core-4.0.0/src/openstef_core/datasets/mixins.py +221 -0
  10. openstef_core-4.0.0/src/openstef_core/datasets/timeseries_dataset.py +521 -0
  11. openstef_core-4.0.0/src/openstef_core/datasets/validated_datasets.py +616 -0
  12. openstef_core-4.0.0/src/openstef_core/datasets/validation.py +150 -0
  13. openstef_core-4.0.0/src/openstef_core/datasets/versioned_timeseries_dataset.py +338 -0
  14. openstef_core-4.0.0/src/openstef_core/exceptions.py +217 -0
  15. openstef_core-4.0.0/src/openstef_core/mixins/__init__.py +24 -0
  16. openstef_core-4.0.0/src/openstef_core/mixins/param_ranges.py +131 -0
  17. openstef_core-4.0.0/src/openstef_core/mixins/predictor.py +307 -0
  18. openstef_core-4.0.0/src/openstef_core/mixins/stateful.py +132 -0
  19. openstef_core-4.0.0/src/openstef_core/mixins/transform.py +184 -0
  20. openstef_core-4.0.0/src/openstef_core/testing.py +306 -0
  21. openstef_core-4.0.0/src/openstef_core/transforms/__init__.py +17 -0
  22. openstef_core-4.0.0/src/openstef_core/transforms/dataset_transforms.py +82 -0
  23. openstef_core-4.0.0/src/openstef_core/types.py +453 -0
  24. openstef_core-4.0.0/src/openstef_core/utils/__init__.py +34 -0
  25. openstef_core-4.0.0/src/openstef_core/utils/datetime.py +109 -0
  26. openstef_core-4.0.0/src/openstef_core/utils/invariants.py +31 -0
  27. openstef_core-4.0.0/src/openstef_core/utils/itertools.py +108 -0
  28. openstef_core-4.0.0/src/openstef_core/utils/multiprocessing.py +77 -0
  29. openstef_core-4.0.0/src/openstef_core/utils/pandas.py +105 -0
  30. openstef_core-4.0.0/src/openstef_core/utils/pydantic.py +59 -0
  31. openstef_core-4.0.0/tests/__init__.py +3 -0
  32. openstef_core-4.0.0/tests/unit/__init__.py +0 -0
  33. openstef_core-4.0.0/tests/unit/datasets/__init__.py +0 -0
  34. openstef_core-4.0.0/tests/unit/datasets/test_mixins.py +55 -0
  35. openstef_core-4.0.0/tests/unit/datasets/test_timeseries_dataset.py +337 -0
  36. openstef_core-4.0.0/tests/unit/datasets/test_validation.py +160 -0
  37. openstef_core-4.0.0/tests/unit/datasets/test_versioned_timeseries_dataset.py +345 -0
  38. openstef_core-4.0.0/tests/unit/datasets/utils.py +28 -0
  39. openstef_core-4.0.0/tests/unit/mixins/__init__.py +0 -0
  40. openstef_core-4.0.0/tests/unit/mixins/test_stateful.py +118 -0
  41. openstef_core-4.0.0/tests/unit/mixins/test_transform.py +132 -0
  42. openstef_core-4.0.0/tests/unit/test_base_model.py +66 -0
  43. openstef_core-4.0.0/tests/unit/test_hyperparams_tuning.py +115 -0
  44. openstef_core-4.0.0/tests/unit/test_param_ranges.py +112 -0
  45. openstef_core-4.0.0/tests/unit/test_types.py +456 -0
  46. openstef_core-4.0.0/tests/unit/transforms/__init__.py +0 -0
  47. openstef_core-4.0.0/tests/unit/utils/__init__.py +0 -0
  48. openstef_core-4.0.0/tests/unit/utils/test_datetime.py +159 -0
  49. openstef_core-4.0.0/tests/unit/utils/test_itertools.py +100 -0
  50. openstef_core-4.0.0/tests/unit/utils/test_multiprocessing.py +51 -0
  51. openstef_core-4.0.0/tests/unit/utils/test_pandas.py +56 -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,35 @@
1
+ Metadata-Version: 2.4
2
+ Name: openstef-core
3
+ Version: 4.0.0
4
+ Summary: Core functionality for OpenSTEF, a framework for short-term energy forecasting.
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: joblib<2,>=1
20
+ Requires-Dist: numpy<3,>=2.3.2
21
+ Requires-Dist: pandas<3,>=2.3.1
22
+ Requires-Dist: pyarrow>=21
23
+ Requires-Dist: pydantic-extra-types<3,>=2.10.5
24
+ Requires-Dist: pydantic<3,>=2.12.4
25
+ Provides-Extra: benchmark
26
+ Requires-Dist: huggingface-hub>=1.2.2; extra == 'benchmark'
27
+ Description-Content-Type: text/markdown
28
+
29
+ <!--
30
+ SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
31
+
32
+ SPDX-License-Identifier: MPL-2.0
33
+ -->
34
+
35
+ # openstef-core
@@ -0,0 +1,7 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
3
+
4
+ SPDX-License-Identifier: MPL-2.0
5
+ -->
6
+
7
+ # openstef-core
@@ -0,0 +1,49 @@
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-core"
12
+ version = "4.0.0"
13
+ description = "Core functionality for OpenSTEF, a framework for short-term energy forecasting."
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
+ "joblib>=1,<2",
32
+ "numpy>=2.3.2,<3",
33
+ "pandas>=2.3.1,<3",
34
+ "pyarrow>=21",
35
+ "pydantic>=2.12.4,<3",
36
+ "pydantic-extra-types>=2.10.5,<3",
37
+ ]
38
+
39
+ optional-dependencies.benchmark = [
40
+ "huggingface-hub>=1.2.2",
41
+ ]
42
+
43
+ urls.Documentation = "https://openstef.github.io/openstef/index.html"
44
+ urls.Homepage = "https://lfenergy.org/projects/openstef/"
45
+ urls.Issues = "https://github.com/OpenSTEF/openstef/issues"
46
+ urls.Repository = "https://github.com/OpenSTEF/openstef"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = [ "src/openstef_core" ]
@@ -0,0 +1,4 @@
1
+ # SPDX-FileCopyrightText: 2017-2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ """Core functionality for OpenSTEF, a framework for short-term energy forecasting."""
@@ -0,0 +1,205 @@
1
+ # SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """Configuration utilities for OpenSTEF Beam.
6
+
7
+ This module provides a `BaseConfig` class extending Pydantic's `BaseModel`
8
+ with convenience helpers for reading from and writing to YAML files. It also
9
+ exposes two helper functions `write_yaml_config` and `read_yaml_config` that
10
+ operate on arbitrary config instances or Pydantic models / adapters.
11
+ """
12
+
13
+ from pathlib import Path
14
+ from typing import Annotated, Any, Self
15
+
16
+ import yaml
17
+ from pydantic import BaseModel as PydanticBaseModel
18
+ from pydantic import (
19
+ BeforeValidator,
20
+ ConfigDict,
21
+ GetCoreSchemaHandler,
22
+ GetJsonSchemaHandler,
23
+ TypeAdapter,
24
+ ValidationInfo,
25
+ )
26
+ from pydantic_core import core_schema
27
+
28
+
29
+ class BaseModel(PydanticBaseModel):
30
+ """Base model class for OpenSTEF components."""
31
+
32
+ model_config = ConfigDict(protected_namespaces=(), arbitrary_types_allowed=True, ser_json_inf_nan="null")
33
+
34
+
35
+ class BaseConfig(PydanticBaseModel):
36
+ """Base configuration model.
37
+
38
+ It configures Pydantic model for safe YAML serialization / deserialization.
39
+ """
40
+
41
+ model_config = ConfigDict(
42
+ protected_namespaces=(),
43
+ extra="ignore",
44
+ arbitrary_types_allowed=False,
45
+ )
46
+
47
+ @classmethod
48
+ def read_yaml(cls, path: Path) -> Self:
49
+ """Create an instance from a YAML file.
50
+
51
+ Args:
52
+ path: Path to the YAML file to read.
53
+
54
+ Returns:
55
+ An instance of the config class populated with the file contents.
56
+ """
57
+ return read_yaml_config(path, class_type=cls)
58
+
59
+ def write_yaml(self, path: Path) -> None:
60
+ """Write this configuration to a YAML file.
61
+
62
+ Args:
63
+ path: Destination path for the YAML file (will be overwritten).
64
+ """
65
+ write_yaml_config(self, path)
66
+
67
+
68
+ def write_yaml_config(config: BaseConfig, path: Path) -> None:
69
+ """Write the config to a YAML file.
70
+
71
+ Args:
72
+ config: The configuration object to serialize.
73
+ path: Destination path for the YAML file (will be overwritten).
74
+
75
+ Example:
76
+ >>> from pathlib import Path
77
+ >>> from pydantic import BaseModel
78
+ >>> class MyConfig(BaseModel):
79
+ ... foo: int
80
+ >>> cfg = MyConfig(foo=123)
81
+ >>> write_yaml_config(cfg, Path("/tmp/test.yaml"))
82
+ """
83
+ with path.open("w", encoding="utf-8") as f:
84
+ yaml.dump(config.model_dump(mode="json"), f, allow_unicode=True)
85
+
86
+
87
+ def read_yaml_config[T: BaseConfig, U](path: Path, class_type: type[T] | TypeAdapter[U]) -> T | U:
88
+ """Read a configuration object from a YAML file.
89
+
90
+ This function supports two kinds of targets:
91
+
92
+ * A subclass of `BaseConfig`, in which case Pydantic's `model_validate` is used.
93
+ * A `TypeAdapter` instance for more advanced / non-`BaseModel` schema validation.
94
+
95
+ Args:
96
+ path: Path to the YAML file to read.
97
+ class_type: The target type (a `BaseConfig` subclass) or a `TypeAdapter`.
98
+
99
+ Returns:
100
+ A validated configuration instance (either ``T`` or ``U`` depending on
101
+ the provided ``class_type``).
102
+ """
103
+ with path.open("r", encoding="utf-8") as f:
104
+ data = yaml.safe_load(f)
105
+
106
+ if isinstance(class_type, TypeAdapter):
107
+ return class_type.validate_python(data)
108
+
109
+ return class_type.model_validate(data)
110
+
111
+
112
+ class PydanticStringPrimitive:
113
+ """Base class for Pydantic-compatible types with string serialization."""
114
+
115
+ def __str__(self) -> str:
116
+ """Convert to string representation."""
117
+ raise NotImplementedError("Subclasses must implement __str__")
118
+
119
+ @classmethod
120
+ def from_string(cls, s: str) -> Self:
121
+ """Create an instance from string representation."""
122
+ raise NotImplementedError("Subclasses must implement from_string")
123
+
124
+ @classmethod
125
+ def validate(cls, v: Any, _info: ValidationInfo | None = None) -> Self: # noqa: ANN401
126
+ """Validate and convert input to this type.
127
+
128
+ Args:
129
+ v: Input value to validate.
130
+ _info: Additional validation info (unused).
131
+
132
+ Returns:
133
+ Validated instance of this type.
134
+
135
+ Raises:
136
+ ValueError: If input cannot be converted to this type.
137
+ """
138
+ if isinstance(v, cls):
139
+ return v
140
+ if isinstance(v, str):
141
+ return cls.from_string(v)
142
+
143
+ # Subclasses should handle their specific types
144
+ error_message = f"Cannot convert {v} to {cls.__name__}"
145
+ raise ValueError(error_message)
146
+
147
+ @classmethod
148
+ def __get_pydantic_core_schema__(
149
+ cls, _source_type: type[Any], _handler: GetCoreSchemaHandler
150
+ ) -> core_schema.CoreSchema:
151
+ """Define Pydantic validation and serialization behavior.
152
+
153
+ Returns:
154
+ Core schema for Pydantic validation and serialization.
155
+ """
156
+ return core_schema.with_info_plain_validator_function(
157
+ function=cls.validate, serialization=core_schema.plain_serializer_function_ser_schema(cls.__str__)
158
+ )
159
+
160
+ @classmethod
161
+ def __get_pydantic_json_schema__( # noqa: PLW3201
162
+ cls,
163
+ _schema: core_schema.CoreSchema,
164
+ handler: GetJsonSchemaHandler,
165
+ ) -> dict[str, Any]:
166
+ """Generate JSON schema for OpenAPI / FastAPI compatibility.
167
+
168
+ All string-primitive types serialise as plain strings.
169
+
170
+ Returns:
171
+ JSON schema describing the type as a string.
172
+ """
173
+ return {"type": "string"}
174
+
175
+ def __eq__(self, other: object) -> bool:
176
+ """Check equality based on string representation.
177
+
178
+ Returns:
179
+ True if both objects have the same string representation, False otherwise.
180
+ """
181
+ if not isinstance(other, self.__class__):
182
+ return NotImplemented
183
+ return str(self) == str(other)
184
+
185
+ def __hash__(self) -> int:
186
+ """Return hash based on string representation."""
187
+ return hash(str(self))
188
+
189
+
190
+ def _convert_none_to_nan(v: float | None) -> float:
191
+ if v is None:
192
+ return float("nan")
193
+ return v
194
+
195
+
196
+ FloatOrNan = Annotated[float, BeforeValidator(_convert_none_to_nan)]
197
+
198
+ __all__ = [
199
+ "BaseConfig",
200
+ "BaseModel",
201
+ "FloatOrNan",
202
+ "PydanticStringPrimitive",
203
+ "read_yaml_config",
204
+ "write_yaml_config",
205
+ ]
@@ -0,0 +1,10 @@
1
+ # SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """Shared constants for the openstef_core package."""
6
+
7
+ LIANDER_DATASET_REPO_ID = "OpenSTEF/liander2024-energy-forecasting-benchmark"
8
+
9
+
10
+ __all__ = ["LIANDER_DATASET_REPO_ID"]
@@ -0,0 +1,37 @@
1
+ # SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <openstef@lfenergy.org>
2
+ #
3
+ # SPDX-License-Identifier: MPL-2.0
4
+
5
+ """Time series datasets and versioned data access.
6
+
7
+ This module provides core data structures for handling time series data in OpenSTEF forecasting.
8
+ It includes both simple time series datasets and versioned datasets that track data availability
9
+ over time, enabling realistic backtesting and training and forecasting.
10
+
11
+ The module supports:
12
+
13
+ - Regular time series with consistent sampling intervals
14
+ - Versioned time series that track when data became available
15
+ - Validated datasets with domain-specific constraints
16
+ - Data transformations and validation utilities
17
+ - Feature concatenation and horizon restriction operations
18
+ """
19
+
20
+ from openstef_core.datasets.timeseries_dataset import TimeSeriesDataset, validate_horizons_present
21
+ from openstef_core.datasets.validated_datasets import (
22
+ EnergyComponentDataset,
23
+ EnsembleForecastDataset,
24
+ ForecastDataset,
25
+ ForecastInputDataset,
26
+ )
27
+ from openstef_core.datasets.versioned_timeseries_dataset import VersionedTimeSeriesDataset
28
+
29
+ __all__ = [
30
+ "EnergyComponentDataset",
31
+ "EnsembleForecastDataset",
32
+ "ForecastDataset",
33
+ "ForecastInputDataset",
34
+ "TimeSeriesDataset",
35
+ "VersionedTimeSeriesDataset",
36
+ "validate_horizons_present",
37
+ ]