eb-features 0.2.4__tar.gz → 0.2.7__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.
- {eb_features-0.2.4 → eb_features-0.2.7}/LICENSE +1 -1
- {eb_features-0.2.4/src/eb_features.egg-info → eb_features-0.2.7}/PKG-INFO +27 -36
- eb_features-0.2.7/README.md +83 -0
- {eb_features-0.2.4 → eb_features-0.2.7}/pyproject.toml +18 -5
- eb_features-0.2.7/src/eb_features/__init__.py +46 -0
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/__init__.py +25 -3
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/calendar.py +44 -25
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/config.py +20 -13
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/constants.py +11 -15
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/encoders.py +5 -5
- eb_features-0.2.7/src/eb_features/panel/engineering.py +250 -0
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/lags.py +31 -11
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/rolling.py +59 -39
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features/panel/validation.py +65 -11
- eb_features-0.2.7/src/eb_features/py.typed +0 -0
- {eb_features-0.2.4 → eb_features-0.2.7/src/eb_features.egg-info}/PKG-INFO +27 -36
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features.egg-info/SOURCES.txt +1 -0
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features.egg-info/requires.txt +7 -0
- eb_features-0.2.4/README.md +0 -96
- eb_features-0.2.4/src/eb_features/__init__.py +0 -20
- eb_features-0.2.4/src/eb_features/panel/engineering.py +0 -304
- {eb_features-0.2.4 → eb_features-0.2.7}/setup.cfg +0 -0
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features.egg-info/dependency_links.txt +0 -0
- {eb_features-0.2.4 → eb_features-0.2.7}/src/eb_features.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eb-features
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Feature engineering utilities for panel time-series data in the Electric Barometer ecosystem.
|
|
5
5
|
Author-email: "Kyle Corrie (Economistician)" <kcorrie@economistician.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -9,25 +9,29 @@ Project-URL: Repository, https://github.com/Economistician/eb-features
|
|
|
9
9
|
Project-URL: Issues, https://github.com/Economistician/eb-features/issues
|
|
10
10
|
Project-URL: Documentation, https://economistician.github.io/eb-docs/
|
|
11
11
|
Keywords: forecasting,features,feature-engineering,time-series,panel-data,operations-research
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
20
|
Classifier: Operating System :: OS Independent
|
|
22
21
|
Classifier: Topic :: Scientific/Engineering
|
|
23
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.11
|
|
24
23
|
Description-Content-Type: text/markdown
|
|
25
24
|
License-File: LICENSE
|
|
26
25
|
Requires-Dist: numpy>=1.24
|
|
27
26
|
Requires-Dist: pandas>=2.0
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
28
29
|
Provides-Extra: dev
|
|
29
30
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
30
31
|
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
32
|
+
Provides-Extra: all
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == "all"
|
|
34
|
+
Requires-Dist: pytest-cov>=5.0; extra == "all"
|
|
31
35
|
Dynamic: license-file
|
|
32
36
|
|
|
33
37
|
# Electric Barometer · Features (`eb-features`)
|
|
@@ -43,19 +47,7 @@ Feature engineering primitives for panel-based forecasting systems, designed to
|
|
|
43
47
|
|
|
44
48
|
## Overview
|
|
45
49
|
|
|
46
|
-
`eb-features`
|
|
47
|
-
|
|
48
|
-
Within the Electric Barometer ecosystem, `eb-features` serves as the upstream feature construction layer, producing standardized inputs for downstream evaluation and metric components. While designed to integrate seamlessly with Electric Barometer, the package remains framework-agnostic and can be used independently in other forecasting workflows.
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Role in the Electric Barometer Ecosystem
|
|
53
|
-
|
|
54
|
-
`eb-features` defines the feature engineering primitives used throughout the Electric Barometer ecosystem. It is responsible for constructing deterministic, panel-aware input features—such as lags, rolling aggregations, and calendar encodings—that serve as the foundational inputs to forecasting and evaluation workflows.
|
|
55
|
-
|
|
56
|
-
This package focuses exclusively on feature construction and validation. It does not perform model training, forecast generation, metric evaluation, or decision logic. Those responsibilities are handled by downstream layers in the ecosystem that consume engineered features for modeling, selection, and operational assessment.
|
|
57
|
-
|
|
58
|
-
By separating feature semantics from modeling and evaluation concerns, `eb-features` provides a stable, reusable foundation that ensures consistency and reproducibility across forecasting pipelines operating on heterogeneous panel data.
|
|
50
|
+
`eb-features` builds deterministic, panel-aware forecasting features (lags, rolling aggregates, calendar encodings) with explicit entity and time boundaries. It does not train models or evaluate forecasts.
|
|
59
51
|
|
|
60
52
|
---
|
|
61
53
|
|
|
@@ -67,7 +59,7 @@ By separating feature semantics from modeling and evaluation concerns, `eb-featu
|
|
|
67
59
|
pip install eb-features
|
|
68
60
|
```
|
|
69
61
|
|
|
70
|
-
The package supports Python 3.
|
|
62
|
+
The package supports Python 3.11 and later.
|
|
71
63
|
|
|
72
64
|
---
|
|
73
65
|
|
|
@@ -87,34 +79,33 @@ The example below shows how to construct lagged and rolling features for panel d
|
|
|
87
79
|
|
|
88
80
|
```python
|
|
89
81
|
import pandas as pd
|
|
90
|
-
from eb_features
|
|
91
|
-
from eb_features.panel.rolling import add_rolling_features
|
|
82
|
+
from eb_features import add_lag_features, add_rolling_features
|
|
92
83
|
|
|
93
|
-
# Example panel data
|
|
94
84
|
df = pd.DataFrame({
|
|
95
|
-
"entity_id": ["A", "A", "A", "B", "B"],
|
|
96
|
-
"date": pd.
|
|
97
|
-
|
|
85
|
+
"entity_id": ["A", "A", "A", "A", "B", "B", "B", "B"],
|
|
86
|
+
"date": pd.to_datetime(
|
|
87
|
+
["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"] * 2
|
|
88
|
+
),
|
|
89
|
+
"y": [10, 12, 11, 13, 7, 9, 8, 10],
|
|
98
90
|
})
|
|
91
|
+
df = df.sort_values(["entity_id", "date"], kind="mergesort")
|
|
99
92
|
|
|
100
|
-
|
|
101
|
-
df = add_lag_features(
|
|
93
|
+
df, lag_cols = add_lag_features(
|
|
102
94
|
df,
|
|
103
|
-
value_col="y",
|
|
104
|
-
lags=[1, 2],
|
|
105
95
|
entity_col="entity_id",
|
|
106
|
-
|
|
96
|
+
target_col="y",
|
|
97
|
+
lag_steps=[1, 2],
|
|
107
98
|
)
|
|
108
99
|
|
|
109
|
-
|
|
110
|
-
df = add_rolling_features(
|
|
100
|
+
df, roll_cols = add_rolling_features(
|
|
111
101
|
df,
|
|
112
|
-
value_col="y",
|
|
113
|
-
windows=[3],
|
|
114
102
|
entity_col="entity_id",
|
|
115
|
-
|
|
103
|
+
target_col="y",
|
|
104
|
+
rolling_windows=[3],
|
|
105
|
+
rolling_stats=["mean"],
|
|
116
106
|
)
|
|
117
107
|
|
|
108
|
+
print(lag_cols, roll_cols)
|
|
118
109
|
print(df)
|
|
119
110
|
```
|
|
120
111
|
|
|
@@ -122,7 +113,7 @@ print(df)
|
|
|
122
113
|
|
|
123
114
|
## License
|
|
124
115
|
|
|
125
|
-
BSD 3-Clause License.
|
|
126
|
-
©
|
|
116
|
+
BSD 3-Clause License.
|
|
117
|
+
© 2026 Kyle Corrie.
|
|
127
118
|
|
|
128
119
|
<!-- Trusted publishing verification -->
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Electric Barometer · Features (`eb-features`)
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Economistician/eb-features/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
Feature engineering primitives for panel-based forecasting systems, designed to integrate seamlessly with the Electric Barometer ecosystem.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
`eb-features` builds deterministic, panel-aware forecasting features (lags, rolling aggregates, calendar encodings) with explicit entity and time boundaries. It does not train models or evaluate forecasts.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
`eb-features` is distributed as a standard Python package.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install eb-features
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The package supports Python 3.11 and later.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Core Concepts
|
|
31
|
+
|
|
32
|
+
- **Panel-aware feature construction** — Features are constructed with explicit awareness of entity boundaries and temporal ordering, ensuring correctness in multi-entity forecasting settings.
|
|
33
|
+
- **Deterministic transformations** — Feature generation is designed to be reproducible and free of stochastic behavior, supporting auditability and consistent downstream evaluation.
|
|
34
|
+
- **Temporal causality** — All features respect time directionality, preventing information leakage from future observations into historical feature sets.
|
|
35
|
+
- **Rolling and lag semantics** — Common forecasting features such as lags and rolling aggregates are treated as first-class primitives with clear, well-defined behavior.
|
|
36
|
+
- **Validation by construction** — Feature pipelines include explicit checks and constraints to ensure structural validity before model training or evaluation.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Minimal Example
|
|
41
|
+
|
|
42
|
+
The example below shows how to construct lagged and rolling features for panel data while preserving entity boundaries and temporal ordering.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import pandas as pd
|
|
46
|
+
from eb_features import add_lag_features, add_rolling_features
|
|
47
|
+
|
|
48
|
+
df = pd.DataFrame({
|
|
49
|
+
"entity_id": ["A", "A", "A", "A", "B", "B", "B", "B"],
|
|
50
|
+
"date": pd.to_datetime(
|
|
51
|
+
["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04"] * 2
|
|
52
|
+
),
|
|
53
|
+
"y": [10, 12, 11, 13, 7, 9, 8, 10],
|
|
54
|
+
})
|
|
55
|
+
df = df.sort_values(["entity_id", "date"], kind="mergesort")
|
|
56
|
+
|
|
57
|
+
df, lag_cols = add_lag_features(
|
|
58
|
+
df,
|
|
59
|
+
entity_col="entity_id",
|
|
60
|
+
target_col="y",
|
|
61
|
+
lag_steps=[1, 2],
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
df, roll_cols = add_rolling_features(
|
|
65
|
+
df,
|
|
66
|
+
entity_col="entity_id",
|
|
67
|
+
target_col="y",
|
|
68
|
+
rolling_windows=[3],
|
|
69
|
+
rolling_stats=["mean"],
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
print(lag_cols, roll_cols)
|
|
73
|
+
print(df)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
BSD 3-Clause License.
|
|
81
|
+
© 2026 Kyle Corrie.
|
|
82
|
+
|
|
83
|
+
<!-- Trusted publishing verification -->
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
######################################
|
|
4
4
|
[project]
|
|
5
5
|
name = "eb-features"
|
|
6
|
-
version = "0.2.
|
|
6
|
+
version = "0.2.7"
|
|
7
7
|
description = "Feature engineering utilities for panel time-series data in the Electric Barometer ecosystem."
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
11
11
|
license-files = ["LICENSE*"]
|
|
12
12
|
|
|
@@ -17,12 +17,11 @@ authors = [
|
|
|
17
17
|
keywords = ["forecasting", "features", "feature-engineering", "time-series", "panel-data", "operations-research"]
|
|
18
18
|
|
|
19
19
|
classifiers = [
|
|
20
|
-
"Development Status ::
|
|
20
|
+
"Development Status :: 5 - Production/Stable",
|
|
21
21
|
"Intended Audience :: Science/Research",
|
|
22
22
|
"Intended Audience :: Developers",
|
|
23
23
|
"Programming Language :: Python :: 3",
|
|
24
24
|
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
-
"Programming Language :: Python :: 3.10",
|
|
26
25
|
"Programming Language :: Python :: 3.11",
|
|
27
26
|
"Programming Language :: Python :: 3.12",
|
|
28
27
|
"Programming Language :: Python :: 3.13",
|
|
@@ -52,11 +51,22 @@ Documentation = "https://economistician.github.io/eb-docs/"
|
|
|
52
51
|
######################################
|
|
53
52
|
[project.optional-dependencies]
|
|
54
53
|
|
|
54
|
+
test = [
|
|
55
|
+
"pytest>=8.0",
|
|
56
|
+
]
|
|
57
|
+
|
|
55
58
|
dev = [
|
|
56
59
|
"pytest>=8.0",
|
|
57
60
|
"pytest-cov>=5.0",
|
|
58
61
|
]
|
|
59
62
|
|
|
63
|
+
# Standardized anchor to satisfy the "extras: all" CI contract.
|
|
64
|
+
# Aggregates dev and test dependencies for the Hub runner.
|
|
65
|
+
all = [
|
|
66
|
+
"pytest>=8.0",
|
|
67
|
+
"pytest-cov>=5.0",
|
|
68
|
+
]
|
|
69
|
+
|
|
60
70
|
######################################
|
|
61
71
|
# Build system
|
|
62
72
|
######################################
|
|
@@ -73,10 +83,13 @@ package-dir = {"" = "src"}
|
|
|
73
83
|
[tool.setuptools.packages.find]
|
|
74
84
|
where = ["src"]
|
|
75
85
|
|
|
86
|
+
[tool.setuptools.package-data]
|
|
87
|
+
eb_features = ["py.typed"]
|
|
88
|
+
|
|
76
89
|
######################################
|
|
77
90
|
# Pytest configuration
|
|
78
91
|
######################################
|
|
79
92
|
[tool.pytest.ini_options]
|
|
80
93
|
pythonpath = ["src"]
|
|
81
94
|
addopts = "-ra"
|
|
82
|
-
testpaths = ["tests"]
|
|
95
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Feature engineering utilities for the Electric Barometer ecosystem.
|
|
3
|
+
|
|
4
|
+
This package provides modular, domain-agnostic feature engineering components
|
|
5
|
+
used across forecasting, evaluation, and operational modeling workflows.
|
|
6
|
+
|
|
7
|
+
The recommended import path is the package root, for example
|
|
8
|
+
``from eb_features import FeatureConfig, FeatureEngineer, add_lag_features``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
14
|
+
|
|
15
|
+
from eb_features.panel.calendar import add_calendar_features
|
|
16
|
+
from eb_features.panel.engineering import FeatureConfig, FeatureEngineer
|
|
17
|
+
from eb_features.panel.lags import add_lag_features
|
|
18
|
+
from eb_features.panel.rolling import add_rolling_features
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _resolve_version() -> str:
|
|
22
|
+
"""
|
|
23
|
+
Resolve the installed package version.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
str
|
|
28
|
+
Installed version string. If the package is not installed (e.g., running
|
|
29
|
+
from source without installation), returns ``"0.0.0"``.
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
return version("eb-features")
|
|
33
|
+
except PackageNotFoundError:
|
|
34
|
+
return "0.0.0"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__version__ = _resolve_version()
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"FeatureConfig",
|
|
41
|
+
"FeatureEngineer",
|
|
42
|
+
"__version__",
|
|
43
|
+
"add_calendar_features",
|
|
44
|
+
"add_lag_features",
|
|
45
|
+
"add_rolling_features",
|
|
46
|
+
]
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""
|
|
4
2
|
Panel time-series feature engineering.
|
|
5
3
|
|
|
@@ -34,9 +32,33 @@ See Also
|
|
|
34
32
|
- [`eb_features.panel.engineering.FeatureConfig`][eb_features.panel.engineering.FeatureConfig]
|
|
35
33
|
"""
|
|
36
34
|
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
from eb_features.panel.calendar import add_calendar_features
|
|
38
|
+
from eb_features.panel.constants import (
|
|
39
|
+
ALLOWED_CALENDAR_FEATURES,
|
|
40
|
+
ALLOWED_ROLLING_STATS,
|
|
41
|
+
DEFAULT_CALENDAR_FEATURES,
|
|
42
|
+
DEFAULT_LAG_STEPS,
|
|
43
|
+
DEFAULT_ROLLING_STATS,
|
|
44
|
+
DEFAULT_ROLLING_WINDOWS,
|
|
45
|
+
)
|
|
46
|
+
from eb_features.panel.encoders import encode_non_numeric_as_category_codes
|
|
37
47
|
from eb_features.panel.engineering import FeatureConfig, FeatureEngineer
|
|
48
|
+
from eb_features.panel.lags import add_lag_features
|
|
49
|
+
from eb_features.panel.rolling import add_rolling_features
|
|
38
50
|
|
|
39
51
|
__all__ = [
|
|
52
|
+
"ALLOWED_CALENDAR_FEATURES",
|
|
53
|
+
"ALLOWED_ROLLING_STATS",
|
|
54
|
+
"DEFAULT_CALENDAR_FEATURES",
|
|
55
|
+
"DEFAULT_LAG_STEPS",
|
|
56
|
+
"DEFAULT_ROLLING_STATS",
|
|
57
|
+
"DEFAULT_ROLLING_WINDOWS",
|
|
40
58
|
"FeatureConfig",
|
|
41
59
|
"FeatureEngineer",
|
|
42
|
-
|
|
60
|
+
"add_calendar_features",
|
|
61
|
+
"add_lag_features",
|
|
62
|
+
"add_rolling_features",
|
|
63
|
+
"encode_non_numeric_as_category_codes",
|
|
64
|
+
]
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
r"""
|
|
4
2
|
Calendar and time-derived features for panel time series.
|
|
5
3
|
|
|
6
4
|
This module provides utilities to derive calendar/time features from a timestamp column.
|
|
7
|
-
It is designed for *panel* time-series data (entity
|
|
5
|
+
It is designed for *panel* time-series data (entity-by-timestamp) and is typically used
|
|
8
6
|
as part of a broader feature engineering pipeline.
|
|
9
7
|
|
|
10
8
|
Supported base calendar features
|
|
@@ -47,7 +45,9 @@ Notes
|
|
|
47
45
|
representation as stored in the column.
|
|
48
46
|
"""
|
|
49
47
|
|
|
50
|
-
from
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
from collections.abc import Iterable, Sequence
|
|
51
51
|
|
|
52
52
|
import numpy as np
|
|
53
53
|
import pandas as pd
|
|
@@ -58,6 +58,7 @@ from eb_features.panel.constants import (
|
|
|
58
58
|
HOUR_PERIOD,
|
|
59
59
|
WEEKEND_DAYS,
|
|
60
60
|
)
|
|
61
|
+
from eb_features.panel.validation import ensure_entity_time_order, infer_panel_entity_col
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
def add_calendar_features(
|
|
@@ -66,7 +67,7 @@ def add_calendar_features(
|
|
|
66
67
|
timestamp_col: str,
|
|
67
68
|
calendar_features: Sequence[str],
|
|
68
69
|
use_cyclical_time: bool = True,
|
|
69
|
-
) ->
|
|
70
|
+
) -> tuple[pd.DataFrame, list[str], list[str]]:
|
|
70
71
|
r"""
|
|
71
72
|
Add calendar/time-derived features to a DataFrame.
|
|
72
73
|
|
|
@@ -114,34 +115,55 @@ def add_calendar_features(
|
|
|
114
115
|
if timestamp_col not in df.columns:
|
|
115
116
|
raise KeyError(f"Timestamp column {timestamp_col!r} not found in DataFrame.")
|
|
116
117
|
|
|
118
|
+
df_out = ensure_entity_time_order(
|
|
119
|
+
df.copy(),
|
|
120
|
+
entity_col=infer_panel_entity_col(df, timestamp_col=timestamp_col),
|
|
121
|
+
timestamp_col=timestamp_col,
|
|
122
|
+
)
|
|
123
|
+
feature_cols, calendar_cols = _assign_calendar_features(
|
|
124
|
+
df_out,
|
|
125
|
+
timestamp_col=timestamp_col,
|
|
126
|
+
calendar_features=calendar_features,
|
|
127
|
+
use_cyclical_time=use_cyclical_time,
|
|
128
|
+
)
|
|
129
|
+
return df_out, feature_cols, calendar_cols
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _assign_calendar_features(
|
|
133
|
+
df: pd.DataFrame,
|
|
134
|
+
*,
|
|
135
|
+
timestamp_col: str,
|
|
136
|
+
calendar_features: Sequence[str],
|
|
137
|
+
use_cyclical_time: bool,
|
|
138
|
+
) -> tuple[list[str], list[str]]:
|
|
139
|
+
"""Mutate ``df`` by adding calendar (and optional cyclical) columns."""
|
|
117
140
|
_validate_calendar_features(calendar_features)
|
|
118
141
|
|
|
119
|
-
|
|
120
|
-
ts = pd.to_datetime(df_out[timestamp_col])
|
|
142
|
+
ts = pd.to_datetime(df[timestamp_col])
|
|
121
143
|
|
|
122
|
-
calendar_cols:
|
|
123
|
-
feature_cols:
|
|
144
|
+
calendar_cols: list[str] = []
|
|
145
|
+
feature_cols: list[str] = []
|
|
124
146
|
|
|
125
147
|
for name in calendar_features:
|
|
126
148
|
if name == "hour":
|
|
127
149
|
col = "hour"
|
|
128
|
-
|
|
150
|
+
df[col] = ts.dt.hour.astype("int16")
|
|
129
151
|
calendar_cols.append(col)
|
|
130
152
|
elif name == "dow":
|
|
131
153
|
col = "dayofweek"
|
|
132
|
-
|
|
154
|
+
df[col] = ts.dt.dayofweek.astype("int16")
|
|
133
155
|
calendar_cols.append(col)
|
|
134
156
|
elif name == "dom":
|
|
135
157
|
col = "dayofmonth"
|
|
136
|
-
|
|
158
|
+
df[col] = ts.dt.day.astype("int16")
|
|
137
159
|
calendar_cols.append(col)
|
|
138
160
|
elif name == "month":
|
|
139
161
|
col = "month"
|
|
140
|
-
|
|
162
|
+
df[col] = ts.dt.month.astype("int16")
|
|
141
163
|
calendar_cols.append(col)
|
|
142
164
|
elif name == "is_weekend":
|
|
143
165
|
col = "is_weekend"
|
|
144
|
-
|
|
166
|
+
df[col] = ts.dt.dayofweek.isin(WEEKEND_DAYS).astype("int8")
|
|
145
167
|
calendar_cols.append(col)
|
|
146
168
|
else: # pragma: no cover
|
|
147
169
|
# Guarded by _validate_calendar_features, but keep a defensive fallback.
|
|
@@ -151,23 +173,20 @@ def add_calendar_features(
|
|
|
151
173
|
|
|
152
174
|
feature_cols.extend(calendar_cols)
|
|
153
175
|
|
|
154
|
-
# ---------------------------------------------------------------------
|
|
155
|
-
# Optional cyclical encodings
|
|
156
|
-
# ---------------------------------------------------------------------
|
|
157
176
|
if use_cyclical_time:
|
|
158
177
|
if "hour" in calendar_cols:
|
|
159
|
-
hour =
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
hour = df["hour"].astype(float)
|
|
179
|
+
df["hour_sin"] = np.sin(2.0 * np.pi * hour / float(HOUR_PERIOD))
|
|
180
|
+
df["hour_cos"] = np.cos(2.0 * np.pi * hour / float(HOUR_PERIOD))
|
|
162
181
|
feature_cols.extend(["hour_sin", "hour_cos"])
|
|
163
182
|
|
|
164
183
|
if "dayofweek" in calendar_cols:
|
|
165
|
-
dow =
|
|
166
|
-
|
|
167
|
-
|
|
184
|
+
dow = df["dayofweek"].astype(float)
|
|
185
|
+
df["dow_sin"] = np.sin(2.0 * np.pi * dow / float(DOW_PERIOD))
|
|
186
|
+
df["dow_cos"] = np.cos(2.0 * np.pi * dow / float(DOW_PERIOD))
|
|
168
187
|
feature_cols.extend(["dow_sin", "dow_cos"])
|
|
169
188
|
|
|
170
|
-
return
|
|
189
|
+
return feature_cols, calendar_cols
|
|
171
190
|
|
|
172
191
|
|
|
173
192
|
def _validate_calendar_features(calendar_features: Iterable[str]) -> None:
|
|
@@ -190,4 +209,4 @@ def _validate_calendar_features(calendar_features: Iterable[str]) -> None:
|
|
|
190
209
|
raise ValueError(
|
|
191
210
|
"Unsupported calendar feature(s) requested: "
|
|
192
211
|
f"{invalid}. Allowed: {sorted(ALLOWED_CALENDAR_FEATURES)}."
|
|
193
|
-
)
|
|
212
|
+
)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""
|
|
4
2
|
Configuration objects for panel time-series feature engineering.
|
|
5
3
|
|
|
@@ -12,27 +10,36 @@ pipeline. The configuration is designed to be:
|
|
|
12
10
|
|
|
13
11
|
Notes
|
|
14
12
|
-----
|
|
15
|
-
This configuration is consumed by
|
|
16
|
-
and related helper
|
|
13
|
+
This configuration is consumed by
|
|
14
|
+
[`FeatureEngineer`][eb_features.panel.engineering.FeatureEngineer] and related helper
|
|
15
|
+
modules (lags, rolling, calendar, encoders).
|
|
17
16
|
"""
|
|
18
17
|
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from collections.abc import Sequence
|
|
19
21
|
from dataclasses import dataclass, field
|
|
20
|
-
from typing import Optional, Sequence, Tuple
|
|
21
22
|
|
|
22
23
|
# -------------------------------------------------------------------------
|
|
23
24
|
# Public constants (useful for validation, docs, and IDE discoverability)
|
|
24
25
|
# -------------------------------------------------------------------------
|
|
25
26
|
|
|
26
27
|
#: Allowed rolling statistics for rolling window features.
|
|
27
|
-
ALLOWED_ROLLING_STATS:
|
|
28
|
+
ALLOWED_ROLLING_STATS: tuple[str, ...] = ("mean", "std", "min", "max", "sum", "median")
|
|
28
29
|
|
|
29
30
|
#: Allowed calendar feature keys derived from timestamps.
|
|
30
|
-
ALLOWED_CALENDAR_FEATURES:
|
|
31
|
+
ALLOWED_CALENDAR_FEATURES: tuple[str, ...] = (
|
|
32
|
+
"hour",
|
|
33
|
+
"dow",
|
|
34
|
+
"dom",
|
|
35
|
+
"month",
|
|
36
|
+
"is_weekend",
|
|
37
|
+
)
|
|
31
38
|
|
|
32
39
|
|
|
33
40
|
@dataclass(frozen=True)
|
|
34
41
|
class FeatureConfig:
|
|
35
|
-
|
|
42
|
+
"""
|
|
36
43
|
Configuration for panel time-series feature engineering.
|
|
37
44
|
|
|
38
45
|
The feature engineering pipeline assumes a long-form panel DataFrame with an entity
|
|
@@ -77,9 +84,9 @@ class FeatureConfig:
|
|
|
77
84
|
by lags and rolling windows).
|
|
78
85
|
"""
|
|
79
86
|
|
|
80
|
-
lag_steps:
|
|
87
|
+
lag_steps: Sequence[int] | None = field(default_factory=lambda: (1, 2, 24))
|
|
81
88
|
|
|
82
|
-
rolling_windows:
|
|
89
|
+
rolling_windows: Sequence[int] | None = field(default_factory=lambda: (3, 24))
|
|
83
90
|
rolling_stats: Sequence[str] = field(
|
|
84
91
|
default_factory=lambda: ("mean", "std", "min", "max", "sum")
|
|
85
92
|
)
|
|
@@ -89,7 +96,7 @@ class FeatureConfig:
|
|
|
89
96
|
)
|
|
90
97
|
use_cyclical_time: bool = True
|
|
91
98
|
|
|
92
|
-
regressor_cols:
|
|
93
|
-
static_cols:
|
|
99
|
+
regressor_cols: Sequence[str] | None = None
|
|
100
|
+
static_cols: Sequence[str] | None = None
|
|
94
101
|
|
|
95
|
-
dropna: bool = True
|
|
102
|
+
dropna: bool = True
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
r"""
|
|
4
2
|
Shared constants for panel feature engineering.
|
|
5
3
|
|
|
@@ -15,14 +13,14 @@ Notes
|
|
|
15
13
|
used consistently across feature builders.
|
|
16
14
|
"""
|
|
17
15
|
|
|
18
|
-
from
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from typing import Final
|
|
19
19
|
|
|
20
20
|
# -----------------------------------------------------------------------------
|
|
21
21
|
# Allowed feature keys
|
|
22
22
|
# -----------------------------------------------------------------------------
|
|
23
|
-
ALLOWED_ROLLING_STATS:
|
|
24
|
-
{"mean", "std", "min", "max", "sum", "median"}
|
|
25
|
-
)
|
|
23
|
+
ALLOWED_ROLLING_STATS: frozenset[str] = frozenset({"mean", "std", "min", "max", "sum", "median"})
|
|
26
24
|
r"""
|
|
27
25
|
Allowed rolling-window summary statistics.
|
|
28
26
|
|
|
@@ -36,9 +34,7 @@ where ``w`` is the window length (in index steps) and ``stat`` is one of the all
|
|
|
36
34
|
"""
|
|
37
35
|
|
|
38
36
|
|
|
39
|
-
ALLOWED_CALENDAR_FEATURES:
|
|
40
|
-
{"hour", "dow", "dom", "month", "is_weekend"}
|
|
41
|
-
)
|
|
37
|
+
ALLOWED_CALENDAR_FEATURES: frozenset[str] = frozenset({"hour", "dow", "dom", "month", "is_weekend"})
|
|
42
38
|
r"""
|
|
43
39
|
Allowed calendar features derived from the timestamp column.
|
|
44
40
|
|
|
@@ -49,12 +45,12 @@ cyclical encodings (sine/cosine) for periodic components.
|
|
|
49
45
|
# -----------------------------------------------------------------------------
|
|
50
46
|
# Default configuration values (used by FeatureConfig)
|
|
51
47
|
# -----------------------------------------------------------------------------
|
|
52
|
-
DEFAULT_LAG_STEPS: Final[
|
|
48
|
+
DEFAULT_LAG_STEPS: Final[tuple[int, ...]] = (1, 2, 24)
|
|
53
49
|
|
|
54
|
-
DEFAULT_ROLLING_WINDOWS: Final[
|
|
55
|
-
DEFAULT_ROLLING_STATS: Final[
|
|
50
|
+
DEFAULT_ROLLING_WINDOWS: Final[tuple[int, ...]] = (3, 24)
|
|
51
|
+
DEFAULT_ROLLING_STATS: Final[tuple[str, ...]] = ("mean", "std", "min", "max", "sum")
|
|
56
52
|
|
|
57
|
-
DEFAULT_CALENDAR_FEATURES: Final[
|
|
53
|
+
DEFAULT_CALENDAR_FEATURES: Final[tuple[str, ...]] = (
|
|
58
54
|
"hour",
|
|
59
55
|
"dow",
|
|
60
56
|
"month",
|
|
@@ -71,5 +67,5 @@ DOW_PERIOD: Final[int] = 7
|
|
|
71
67
|
"""Period used for cyclical day-of-week encodings."""
|
|
72
68
|
|
|
73
69
|
# pandas dt.dayofweek convention: Monday=0 ... Sunday=6
|
|
74
|
-
WEEKEND_DAYS: Final[
|
|
75
|
-
"""Day-of-week values corresponding to Saturday and Sunday."""
|
|
70
|
+
WEEKEND_DAYS: Final[tuple[int, int]] = (5, 6)
|
|
71
|
+
"""Day-of-week values corresponding to Saturday and Sunday."""
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
"""
|
|
4
2
|
Encoding utilities for panel feature engineering.
|
|
5
3
|
|
|
@@ -24,7 +22,9 @@ consider:
|
|
|
24
22
|
- introducing a fitted encoder with persisted category mappings.
|
|
25
23
|
"""
|
|
26
24
|
|
|
27
|
-
from
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from collections.abc import Iterable
|
|
28
28
|
|
|
29
29
|
import pandas as pd
|
|
30
30
|
from pandas.api.types import is_bool_dtype, is_numeric_dtype
|
|
@@ -53,7 +53,7 @@ def encode_non_numeric_as_category_codes(
|
|
|
53
53
|
-------
|
|
54
54
|
pandas.DataFrame
|
|
55
55
|
Copy of ``feature_frame`` where:
|
|
56
|
-
- boolean columns are converted to {0,1}
|
|
56
|
+
- boolean columns are converted to {0, 1}
|
|
57
57
|
- non-numeric, non-boolean columns are replaced by categorical integer codes
|
|
58
58
|
|
|
59
59
|
Notes
|
|
@@ -88,4 +88,4 @@ def encode_non_numeric_as_category_codes(
|
|
|
88
88
|
cat = pd.Categorical(s, categories=categories, ordered=False)
|
|
89
89
|
df[col] = cat.codes.astype(dtype)
|
|
90
90
|
|
|
91
|
-
return df
|
|
91
|
+
return df
|