eb-features 0.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.
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Kyle Corrie
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.4
2
+ Name: eb-features
3
+ Version: 0.1.0
4
+ Summary: Feature engineering utilities for panel time-series data in the Electric Barometer ecosystem.
5
+ Author-email: "Kyle Corrie (Economistician)" <kcorrie@economistician.com>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/Economistician/eb-features
8
+ Project-URL: Repository, https://github.com/Economistician/eb-features
9
+ Project-URL: Issues, https://github.com/Economistician/eb-features/issues
10
+ Project-URL: Documentation, https://economistician.github.io/eb-docs/
11
+ Keywords: forecasting,features,feature-engineering,time-series,panel-data,operations-research
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: numpy>=1.24
26
+ Requires-Dist: pandas>=2.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Electric barometer Feature Engineering (`eb-features`)
33
+
34
+ **eb-features** is the feature engineering layer of the **Electric Barometer** ecosystem.
35
+
36
+ It provides a structured, opinionated set of **panel-aware feature construction utilities**
37
+ for time-series modeling in operational environments—contexts where *temporal structure*,
38
+ *entity boundaries*, and *leakage safety* matter as much as model choice itself.
39
+
40
+ This package focuses on **deterministic, stateless feature generation** for classical
41
+ supervised learning pipelines, producing clean, model-ready design matrices from
42
+ long-form panel data.
43
+
44
+ ---
45
+
46
+ ## Naming convention
47
+
48
+ Electric Barometer packages follow a consistent naming convention:
49
+
50
+ - **Distribution names** (used with `pip install`) use hyphens
51
+ e.g. `pip install eb-features`
52
+ - **Python import paths** use underscores
53
+ e.g. `import eb_features`
54
+
55
+ This follows standard Python packaging practices and avoids ambiguity between
56
+ package names and module imports.
57
+
58
+ ---
59
+
60
+ ## What this package provides
61
+
62
+ ### Panel-safe lag features
63
+ Lagged versions of the target series constructed **strictly within entity** boundaries.
64
+
65
+ - Configurable lag steps (index-based, frequency-agnostic)
66
+ - Deterministic naming (`lag_1`, `lag_24`, etc.)
67
+ - Explicit handling of missing history
68
+
69
+ ---
70
+
71
+ ### Leakage-aware rolling statistics
72
+ Rolling-window summaries designed for forecasting workflows.
73
+
74
+ - Mean, sum, min, max, std, median
75
+ - Configurable window sizes
76
+ - **Leakage-safe by default** (excludes current target value)
77
+ - Optional early availability via `min_periods`
78
+
79
+ ---
80
+
81
+ ### Calendar and time-derived features
82
+ Calendar attributes derived from timestamp columns.
83
+
84
+ - Hour, day-of-week, day-of-month, month
85
+ - Weekend indicators
86
+ - Optional cyclical encodings (sine/cosine) for periodic components
87
+ - Timezone-aware timestamp support
88
+
89
+ ---
90
+
91
+ ### Passthrough regressors and static features
92
+ Support for mixing engineered temporal features with:
93
+
94
+ - Numeric external regressors
95
+ - Static entity-level metadata
96
+ - Automatic regressor detection when not explicitly specified
97
+
98
+ Non-numeric passthrough columns are encoded using stable, dataset-local
99
+ categorical codes.
100
+
101
+ ---
102
+
103
+ ### Validation and guardrails
104
+ Built-in validation to catch common modeling errors early.
105
+
106
+ - Required-column checks
107
+ - Strict monotonic timestamp enforcement within entity
108
+ - Protection against cross-entity leakage
109
+ - Non-finite value detection before model handoff
110
+
111
+ ---
112
+
113
+ ## Design principles
114
+
115
+ `eb-features` is intentionally:
116
+
117
+ - **Stateless** — no fitted encoders or persisted mappings
118
+ - **Deterministic** — same input + config → same output
119
+ - **Frequency-agnostic** — works with hourly, daily, or irregular data
120
+ - **Panel-aware** — entity boundaries are first-class constraints
121
+
122
+ This makes it suitable for batch modeling, experimentation, and reproducible
123
+ forecast evaluation pipelines.
124
+
125
+ ---
126
+
127
+ ## Documentation structure
128
+
129
+ - **API Reference**
130
+ All feature builders and utilities are documented automatically from
131
+ NumPy-style docstrings using `mkdocstrings`.
132
+
133
+ Conceptual motivation and modeling guidance for these features live in the
134
+ companion repositories:
135
+
136
+ - **eb-metrics** — operationally meaningful forecast metrics
137
+ - **eb-evaluation** — structured forecast evaluation workflows
138
+ - **eb-papers** — formal definitions and technical notes
139
+
140
+ ---
141
+
142
+ ## Intended audience
143
+
144
+ This package is intended for:
145
+
146
+ - data scientists and applied ML practitioners
147
+ - forecasting and demand-planning teams
148
+ - operations and service analytics engineers
149
+ - researchers working with panel time-series data
150
+
151
+ The emphasis throughout is on **correct feature construction under operational
152
+ constraints**, not generic time-series convenience.
153
+
154
+ ---
155
+
156
+ ## Relationship to the Electric Barometer framework
157
+
158
+ `eb-features` provides the **feature engineering layer** of the Electric Barometer
159
+ ecosystem.
160
+
161
+ It is designed to work in concert with:
162
+
163
+ - **eb-metrics** — how forecasts are evaluated
164
+ - **eb-evaluation** — how forecasts are compared and selected
165
+ - **eb-adapters** — how forecasts integrate with external systems
166
+
167
+ Together, these components support a disciplined, end-to-end approach to
168
+ *forecast readiness*—from raw data, to features, to evaluation.
@@ -0,0 +1,137 @@
1
+ # Electric barometer Feature Engineering (`eb-features`)
2
+
3
+ **eb-features** is the feature engineering layer of the **Electric Barometer** ecosystem.
4
+
5
+ It provides a structured, opinionated set of **panel-aware feature construction utilities**
6
+ for time-series modeling in operational environments—contexts where *temporal structure*,
7
+ *entity boundaries*, and *leakage safety* matter as much as model choice itself.
8
+
9
+ This package focuses on **deterministic, stateless feature generation** for classical
10
+ supervised learning pipelines, producing clean, model-ready design matrices from
11
+ long-form panel data.
12
+
13
+ ---
14
+
15
+ ## Naming convention
16
+
17
+ Electric Barometer packages follow a consistent naming convention:
18
+
19
+ - **Distribution names** (used with `pip install`) use hyphens
20
+ e.g. `pip install eb-features`
21
+ - **Python import paths** use underscores
22
+ e.g. `import eb_features`
23
+
24
+ This follows standard Python packaging practices and avoids ambiguity between
25
+ package names and module imports.
26
+
27
+ ---
28
+
29
+ ## What this package provides
30
+
31
+ ### Panel-safe lag features
32
+ Lagged versions of the target series constructed **strictly within entity** boundaries.
33
+
34
+ - Configurable lag steps (index-based, frequency-agnostic)
35
+ - Deterministic naming (`lag_1`, `lag_24`, etc.)
36
+ - Explicit handling of missing history
37
+
38
+ ---
39
+
40
+ ### Leakage-aware rolling statistics
41
+ Rolling-window summaries designed for forecasting workflows.
42
+
43
+ - Mean, sum, min, max, std, median
44
+ - Configurable window sizes
45
+ - **Leakage-safe by default** (excludes current target value)
46
+ - Optional early availability via `min_periods`
47
+
48
+ ---
49
+
50
+ ### Calendar and time-derived features
51
+ Calendar attributes derived from timestamp columns.
52
+
53
+ - Hour, day-of-week, day-of-month, month
54
+ - Weekend indicators
55
+ - Optional cyclical encodings (sine/cosine) for periodic components
56
+ - Timezone-aware timestamp support
57
+
58
+ ---
59
+
60
+ ### Passthrough regressors and static features
61
+ Support for mixing engineered temporal features with:
62
+
63
+ - Numeric external regressors
64
+ - Static entity-level metadata
65
+ - Automatic regressor detection when not explicitly specified
66
+
67
+ Non-numeric passthrough columns are encoded using stable, dataset-local
68
+ categorical codes.
69
+
70
+ ---
71
+
72
+ ### Validation and guardrails
73
+ Built-in validation to catch common modeling errors early.
74
+
75
+ - Required-column checks
76
+ - Strict monotonic timestamp enforcement within entity
77
+ - Protection against cross-entity leakage
78
+ - Non-finite value detection before model handoff
79
+
80
+ ---
81
+
82
+ ## Design principles
83
+
84
+ `eb-features` is intentionally:
85
+
86
+ - **Stateless** — no fitted encoders or persisted mappings
87
+ - **Deterministic** — same input + config → same output
88
+ - **Frequency-agnostic** — works with hourly, daily, or irregular data
89
+ - **Panel-aware** — entity boundaries are first-class constraints
90
+
91
+ This makes it suitable for batch modeling, experimentation, and reproducible
92
+ forecast evaluation pipelines.
93
+
94
+ ---
95
+
96
+ ## Documentation structure
97
+
98
+ - **API Reference**
99
+ All feature builders and utilities are documented automatically from
100
+ NumPy-style docstrings using `mkdocstrings`.
101
+
102
+ Conceptual motivation and modeling guidance for these features live in the
103
+ companion repositories:
104
+
105
+ - **eb-metrics** — operationally meaningful forecast metrics
106
+ - **eb-evaluation** — structured forecast evaluation workflows
107
+ - **eb-papers** — formal definitions and technical notes
108
+
109
+ ---
110
+
111
+ ## Intended audience
112
+
113
+ This package is intended for:
114
+
115
+ - data scientists and applied ML practitioners
116
+ - forecasting and demand-planning teams
117
+ - operations and service analytics engineers
118
+ - researchers working with panel time-series data
119
+
120
+ The emphasis throughout is on **correct feature construction under operational
121
+ constraints**, not generic time-series convenience.
122
+
123
+ ---
124
+
125
+ ## Relationship to the Electric Barometer framework
126
+
127
+ `eb-features` provides the **feature engineering layer** of the Electric Barometer
128
+ ecosystem.
129
+
130
+ It is designed to work in concert with:
131
+
132
+ - **eb-metrics** — how forecasts are evaluated
133
+ - **eb-evaluation** — how forecasts are compared and selected
134
+ - **eb-adapters** — how forecasts integrate with external systems
135
+
136
+ Together, these components support a disciplined, end-to-end approach to
137
+ *forecast readiness*—from raw data, to features, to evaluation.
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "eb-features"
3
+ version = "0.1.0"
4
+ description = "Feature engineering utilities for panel time-series data in the Electric Barometer ecosystem."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "BSD-3-Clause"
8
+ license-files = ["LICENSE*"]
9
+
10
+ authors = [
11
+ { name = "Kyle Corrie (Economistician)", email = "kcorrie@economistician.com" }
12
+ ]
13
+
14
+ dependencies = [
15
+ "numpy>=1.24",
16
+ "pandas>=2.0",
17
+ ]
18
+
19
+ keywords = ["forecasting", "features", "feature-engineering", "time-series", "panel-data", "operations-research"]
20
+
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Science/Research",
24
+ "Intended Audience :: Developers",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.10",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Operating System :: OS Independent",
31
+ "Topic :: Scientific/Engineering",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/Economistician/eb-features"
36
+ Repository = "https://github.com/Economistician/eb-features"
37
+ Issues = "https://github.com/Economistician/eb-features/issues"
38
+ Documentation = "https://economistician.github.io/eb-docs/"
39
+
40
+ [project.optional-dependencies]
41
+ dev = [
42
+ "pytest>=8.0",
43
+ "pytest-cov>=5.0",
44
+ ]
45
+
46
+ [build-system]
47
+ requires = ["setuptools>=64", "wheel"]
48
+ build-backend = "setuptools.build_meta"
49
+
50
+ [tool.setuptools]
51
+ package-dir = {"" = "src"}
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+
56
+ [tool.pytest.ini_options]
57
+ pythonpath = ["src"]
58
+ addopts = "-ra"
59
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Feature engineering utilities for the Electric Barometer ecosystem.
5
+
6
+ This package provides modular, domain-agnostic feature engineering components
7
+ used across forecasting, evaluation, and operational modeling workflows.
8
+
9
+ Subpackages
10
+ -----------
11
+ panel
12
+ Feature engineering utilities for panel (entity Ă— timestamp) time-series data.
13
+
14
+ Notes
15
+ -----
16
+ This package intentionally exposes a small public API. Most functionality is
17
+ accessed through subpackages such as `eb_features.panel`.
18
+ """
19
+
20
+ __all__ = []
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Panel time-series feature engineering.
5
+
6
+ This subpackage provides lightweight, frequency-agnostic feature engineering utilities
7
+ for *panel* time-series data, i.e., data indexed by:
8
+
9
+ - an **entity** identifier (store, SKU, customer, sensor, etc.), and
10
+ - a **timestamp** column
11
+
12
+ The primary public interface is:
13
+
14
+ - [`FeatureConfig`][eb_features.panel.engineering.FeatureConfig]: declarative feature configuration
15
+ - [`FeatureEngineer`][eb_features.panel.engineering.FeatureEngineer]: stateless transformer that
16
+ produces a model-ready ``(X, y, feature_names)`` triple from a long-form DataFrame.
17
+
18
+ Design goals
19
+ ------------
20
+ - **Stateless**: no fit/transform lifecycle; features are generated deterministically from inputs.
21
+ - **Frequency-agnostic**: lags and rolling windows are expressed in index steps, not wall-clock units.
22
+ - **Leakage-aware**: feature construction is intended to use only information available at or
23
+ before prediction time (depending on configuration and implementation details).
24
+
25
+ Notes
26
+ -----
27
+ This subpackage is intentionally conservative in scope. It focuses on producing features suitable
28
+ for classical supervised learning pipelines (tree models, linear models, shallow neural nets) that
29
+ expect a fixed-width design matrix.
30
+
31
+ See Also
32
+ --------
33
+ - [`eb_features.panel.engineering.FeatureEngineer`][eb_features.panel.engineering.FeatureEngineer]
34
+ - [`eb_features.panel.engineering.FeatureConfig`][eb_features.panel.engineering.FeatureConfig]
35
+ """
36
+
37
+ from eb_features.panel.engineering import FeatureConfig, FeatureEngineer
38
+
39
+ __all__ = [
40
+ "FeatureConfig",
41
+ "FeatureEngineer",
42
+ ]
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Calendar and time-derived features for panel time series.
5
+
6
+ This module provides utilities to derive calendar/time features from a timestamp column.
7
+ It is designed for *panel* time-series data (entity Ă— timestamp) and is typically used
8
+ as part of a broader feature engineering pipeline.
9
+
10
+ Supported base calendar features
11
+ --------------------------------
12
+ Given a timestamp column ``timestamp_col``:
13
+
14
+ - ``"hour"``: Hour of day in ``[0, 23]``
15
+ - ``"dow"``: Day of week in ``[0, 6]`` where Monday=0 (pandas convention)
16
+ - ``"dom"``: Day of month in ``[1, 31]``
17
+ - ``"month"``: Month in ``[1, 12]``
18
+ - ``"is_weekend"``: Weekend indicator (Saturday/Sunday) as ``0/1``
19
+
20
+ Optional cyclical encodings
21
+ ---------------------------
22
+ Certain calendar attributes are periodic and can be represented with sine/cosine pairs:
23
+
24
+ - hour (period 24):
25
+
26
+ $$
27
+ \sin\left(2\pi \frac{\mathrm{hour}}{24}\right),\quad
28
+ \cos\left(2\pi \frac{\mathrm{hour}}{24}\right)
29
+ $$
30
+
31
+ - day of week (period 7):
32
+
33
+ $$
34
+ \sin\left(2\pi \frac{\mathrm{dow}}{7}\right),\quad
35
+ \cos\left(2\pi \frac{\mathrm{dow}}{7}\right)
36
+ $$
37
+
38
+ These encodings are added only when:
39
+ - the corresponding base feature (hour or dow) is present, and
40
+ - ``use_cyclical_time=True``.
41
+
42
+ Notes
43
+ -----
44
+ - Feature construction is *stateless*: functions operate only on the provided DataFrame.
45
+ - Time features are derived from ``pandas.to_datetime`` conversion of ``timestamp_col``.
46
+ Timezone-aware timestamps are supported; feature values reflect the timestamp's local
47
+ representation as stored in the column.
48
+ """
49
+
50
+ from typing import Iterable, List, Sequence, Tuple
51
+
52
+ import numpy as np
53
+ import pandas as pd
54
+
55
+ from eb_features.panel.constants import (
56
+ ALLOWED_CALENDAR_FEATURES,
57
+ DOW_PERIOD,
58
+ HOUR_PERIOD,
59
+ WEEKEND_DAYS,
60
+ )
61
+
62
+
63
+ def add_calendar_features(
64
+ df: pd.DataFrame,
65
+ *,
66
+ timestamp_col: str,
67
+ calendar_features: Sequence[str],
68
+ use_cyclical_time: bool = True,
69
+ ) -> Tuple[pd.DataFrame, List[str], List[str]]:
70
+ r"""
71
+ Add calendar/time-derived features to a DataFrame.
72
+
73
+ Parameters
74
+ ----------
75
+ df : pandas.DataFrame
76
+ Input DataFrame containing a timestamp column.
77
+ timestamp_col : str
78
+ Name of the timestamp column.
79
+ calendar_features : Sequence[str]
80
+ Base calendar features to derive. Allowed values are:
81
+ ``{"hour", "dow", "dom", "month", "is_weekend"}``.
82
+ use_cyclical_time : bool, default True
83
+ If True, add sine/cosine encodings for hour and/or day-of-week when those
84
+ base features are included.
85
+
86
+ Returns
87
+ -------
88
+ df_out : pandas.DataFrame
89
+ Copy of ``df`` with the requested calendar features added as columns.
90
+ feature_cols : list[str]
91
+ Names of all features added by this call, including cyclical encodings (if any).
92
+ calendar_cols : list[str]
93
+ Names of base calendar feature columns added (excludes cyclical encodings).
94
+
95
+ Raises
96
+ ------
97
+ KeyError
98
+ If ``timestamp_col`` is not present in ``df``.
99
+ ValueError
100
+ If an unsupported calendar feature is requested.
101
+
102
+ Notes
103
+ -----
104
+ The derived columns are currently named:
105
+
106
+ - ``hour``
107
+ - ``dayofweek`` (for requested ``"dow"``)
108
+ - ``dayofmonth`` (for requested ``"dom"``)
109
+ - ``month``
110
+ - ``is_weekend``
111
+
112
+ This naming is stable and intended to be referenced by downstream steps.
113
+ """
114
+ if timestamp_col not in df.columns:
115
+ raise KeyError(f"Timestamp column {timestamp_col!r} not found in DataFrame.")
116
+
117
+ _validate_calendar_features(calendar_features)
118
+
119
+ df_out = df.copy()
120
+ ts = pd.to_datetime(df_out[timestamp_col])
121
+
122
+ calendar_cols: List[str] = []
123
+ feature_cols: List[str] = []
124
+
125
+ for name in calendar_features:
126
+ if name == "hour":
127
+ col = "hour"
128
+ df_out[col] = ts.dt.hour.astype("int16")
129
+ calendar_cols.append(col)
130
+ elif name == "dow":
131
+ col = "dayofweek"
132
+ df_out[col] = ts.dt.dayofweek.astype("int16")
133
+ calendar_cols.append(col)
134
+ elif name == "dom":
135
+ col = "dayofmonth"
136
+ df_out[col] = ts.dt.day.astype("int16")
137
+ calendar_cols.append(col)
138
+ elif name == "month":
139
+ col = "month"
140
+ df_out[col] = ts.dt.month.astype("int16")
141
+ calendar_cols.append(col)
142
+ elif name == "is_weekend":
143
+ col = "is_weekend"
144
+ df_out[col] = ts.dt.dayofweek.isin(WEEKEND_DAYS).astype("int8")
145
+ calendar_cols.append(col)
146
+ else: # pragma: no cover
147
+ # Guarded by _validate_calendar_features, but keep a defensive fallback.
148
+ raise ValueError(
149
+ f"Unsupported calendar feature {name!r}. Allowed: {sorted(ALLOWED_CALENDAR_FEATURES)}."
150
+ )
151
+
152
+ feature_cols.extend(calendar_cols)
153
+
154
+ # ---------------------------------------------------------------------
155
+ # Optional cyclical encodings
156
+ # ---------------------------------------------------------------------
157
+ if use_cyclical_time:
158
+ if "hour" in calendar_cols:
159
+ hour = df_out["hour"].astype(float)
160
+ df_out["hour_sin"] = np.sin(2.0 * np.pi * hour / float(HOUR_PERIOD))
161
+ df_out["hour_cos"] = np.cos(2.0 * np.pi * hour / float(HOUR_PERIOD))
162
+ feature_cols.extend(["hour_sin", "hour_cos"])
163
+
164
+ if "dayofweek" in calendar_cols:
165
+ dow = df_out["dayofweek"].astype(float)
166
+ df_out["dow_sin"] = np.sin(2.0 * np.pi * dow / float(DOW_PERIOD))
167
+ df_out["dow_cos"] = np.cos(2.0 * np.pi * dow / float(DOW_PERIOD))
168
+ feature_cols.extend(["dow_sin", "dow_cos"])
169
+
170
+ return df_out, feature_cols, calendar_cols
171
+
172
+
173
+ def _validate_calendar_features(calendar_features: Iterable[str]) -> None:
174
+ r"""
175
+ Validate that requested calendar features are supported.
176
+
177
+ Parameters
178
+ ----------
179
+ calendar_features : Iterable[str]
180
+ Candidate calendar feature keys.
181
+
182
+ Raises
183
+ ------
184
+ ValueError
185
+ If any feature key is unsupported.
186
+ """
187
+ requested = list(calendar_features)
188
+ invalid = [f for f in requested if f not in ALLOWED_CALENDAR_FEATURES]
189
+ if invalid:
190
+ raise ValueError(
191
+ "Unsupported calendar feature(s) requested: "
192
+ f"{invalid}. Allowed: {sorted(ALLOWED_CALENDAR_FEATURES)}."
193
+ )