temporalcv 1.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.
- temporalcv-1.0.0/.gitignore +64 -0
- temporalcv-1.0.0/LICENSE +21 -0
- temporalcv-1.0.0/PKG-INFO +408 -0
- temporalcv-1.0.0/README.md +340 -0
- temporalcv-1.0.0/pyproject.toml +174 -0
- temporalcv-1.0.0/src/temporalcv/__init__.py +473 -0
- temporalcv-1.0.0/src/temporalcv/bagging/__init__.py +215 -0
- temporalcv-1.0.0/src/temporalcv/bagging/base.py +397 -0
- temporalcv-1.0.0/src/temporalcv/bagging/strategies/__init__.py +17 -0
- temporalcv-1.0.0/src/temporalcv/bagging/strategies/block_bootstrap.py +150 -0
- temporalcv-1.0.0/src/temporalcv/bagging/strategies/feature_bagging.py +171 -0
- temporalcv-1.0.0/src/temporalcv/bagging/strategies/residual_bootstrap.py +311 -0
- temporalcv-1.0.0/src/temporalcv/bagging/strategies/stationary_bootstrap.py +184 -0
- temporalcv-1.0.0/src/temporalcv/benchmarks/__init__.py +99 -0
- temporalcv-1.0.0/src/temporalcv/benchmarks/base.py +412 -0
- temporalcv-1.0.0/src/temporalcv/benchmarks/fred.py +233 -0
- temporalcv-1.0.0/src/temporalcv/benchmarks/gluonts.py +208 -0
- temporalcv-1.0.0/src/temporalcv/benchmarks/m5.py +165 -0
- temporalcv-1.0.0/src/temporalcv/benchmarks/monash.py +258 -0
- temporalcv-1.0.0/src/temporalcv/changepoint.py +614 -0
- temporalcv-1.0.0/src/temporalcv/compare/__init__.py +117 -0
- temporalcv-1.0.0/src/temporalcv/compare/adapters/__init__.py +49 -0
- temporalcv-1.0.0/src/temporalcv/compare/adapters/multi_series.py +302 -0
- temporalcv-1.0.0/src/temporalcv/compare/adapters/statsforecast_adapter.py +216 -0
- temporalcv-1.0.0/src/temporalcv/compare/base.py +571 -0
- temporalcv-1.0.0/src/temporalcv/compare/docs.py +370 -0
- temporalcv-1.0.0/src/temporalcv/compare/results.py +366 -0
- temporalcv-1.0.0/src/temporalcv/compare/runner.py +499 -0
- temporalcv-1.0.0/src/temporalcv/conformal.py +1561 -0
- temporalcv-1.0.0/src/temporalcv/cv.py +1945 -0
- temporalcv-1.0.0/src/temporalcv/cv_financial.py +703 -0
- temporalcv-1.0.0/src/temporalcv/diagnostics/__init__.py +23 -0
- temporalcv-1.0.0/src/temporalcv/diagnostics/influence.py +240 -0
- temporalcv-1.0.0/src/temporalcv/diagnostics/sensitivity.py +250 -0
- temporalcv-1.0.0/src/temporalcv/gates.py +1790 -0
- temporalcv-1.0.0/src/temporalcv/guardrails.py +637 -0
- temporalcv-1.0.0/src/temporalcv/inference/__init__.py +41 -0
- temporalcv-1.0.0/src/temporalcv/inference/block_bootstrap_ci.py +472 -0
- temporalcv-1.0.0/src/temporalcv/inference/wild_bootstrap.py +334 -0
- temporalcv-1.0.0/src/temporalcv/lag_selection.py +407 -0
- temporalcv-1.0.0/src/temporalcv/metrics/__init__.py +152 -0
- temporalcv-1.0.0/src/temporalcv/metrics/asymmetric.py +537 -0
- temporalcv-1.0.0/src/temporalcv/metrics/core.py +587 -0
- temporalcv-1.0.0/src/temporalcv/metrics/event.py +718 -0
- temporalcv-1.0.0/src/temporalcv/metrics/financial.py +607 -0
- temporalcv-1.0.0/src/temporalcv/metrics/quantile.py +485 -0
- temporalcv-1.0.0/src/temporalcv/metrics/volatility_weighted.py +665 -0
- temporalcv-1.0.0/src/temporalcv/persistence.py +722 -0
- temporalcv-1.0.0/src/temporalcv/py.typed +2 -0
- temporalcv-1.0.0/src/temporalcv/regimes.py +578 -0
- temporalcv-1.0.0/src/temporalcv/stationarity.py +478 -0
- temporalcv-1.0.0/src/temporalcv/statistical_tests.py +3217 -0
- temporalcv-1.0.0/src/temporalcv/validators/__init__.py +55 -0
- temporalcv-1.0.0/src/temporalcv/validators/theoretical.py +503 -0
- temporalcv-1.0.0/tests/__init__.py +1 -0
- temporalcv-1.0.0/tests/adversarial/__init__.py +0 -0
- temporalcv-1.0.0/tests/anti_patterns/__init__.py +13 -0
- temporalcv-1.0.0/tests/anti_patterns/test_boundary_violations.py +235 -0
- temporalcv-1.0.0/tests/anti_patterns/test_lag_leakage.py +229 -0
- temporalcv-1.0.0/tests/benchmarks/__init__.py +5 -0
- temporalcv-1.0.0/tests/benchmarks/test_cv_benchmarks.py +282 -0
- temporalcv-1.0.0/tests/benchmarks/test_gate_benchmarks.py +260 -0
- temporalcv-1.0.0/tests/benchmarks/test_metric_benchmarks.py +308 -0
- temporalcv-1.0.0/tests/conftest.py +337 -0
- temporalcv-1.0.0/tests/fixtures/golden_reference.json +106 -0
- temporalcv-1.0.0/tests/integration/__init__.py +8 -0
- temporalcv-1.0.0/tests/integration/test_e2e_benchmark.py +583 -0
- temporalcv-1.0.0/tests/integration/test_full_workflow.py +304 -0
- temporalcv-1.0.0/tests/known_answer/__init__.py +0 -0
- temporalcv-1.0.0/tests/monte_carlo/__init__.py +1 -0
- temporalcv-1.0.0/tests/monte_carlo/test_conformal_coverage.py +240 -0
- temporalcv-1.0.0/tests/monte_carlo/test_dm_coverage.py +218 -0
- temporalcv-1.0.0/tests/monte_carlo/test_wild_bootstrap_coverage.py +238 -0
- temporalcv-1.0.0/tests/property/__init__.py +4 -0
- temporalcv-1.0.0/tests/property/test_cv_invariants.py +262 -0
- temporalcv-1.0.0/tests/property/test_financial_cv_invariants.py +321 -0
- temporalcv-1.0.0/tests/property/test_gate_invariants.py +210 -0
- temporalcv-1.0.0/tests/property/test_metric_invariants.py +337 -0
- temporalcv-1.0.0/tests/property/test_stationarity_invariants.py +205 -0
- temporalcv-1.0.0/tests/reproducibility/__init__.py +5 -0
- temporalcv-1.0.0/tests/reproducibility/test_seed_determinism.py +317 -0
- temporalcv-1.0.0/tests/test_bagging.py +689 -0
- temporalcv-1.0.0/tests/test_benchmarks.py +520 -0
- temporalcv-1.0.0/tests/test_block_bootstrap_ci.py +463 -0
- temporalcv-1.0.0/tests/test_changepoint.py +548 -0
- temporalcv-1.0.0/tests/test_compare.py +697 -0
- temporalcv-1.0.0/tests/test_conformal.py +1321 -0
- temporalcv-1.0.0/tests/test_cross_fit.py +277 -0
- temporalcv-1.0.0/tests/test_cv.py +1450 -0
- temporalcv-1.0.0/tests/test_cv_financial.py +399 -0
- temporalcv-1.0.0/tests/test_edge_cases.py +476 -0
- temporalcv-1.0.0/tests/test_gates.py +904 -0
- temporalcv-1.0.0/tests/test_gates_residual.py +397 -0
- temporalcv-1.0.0/tests/test_gates_theoretical.py +438 -0
- temporalcv-1.0.0/tests/test_golden_reference.py +154 -0
- temporalcv-1.0.0/tests/test_guardrails.py +496 -0
- temporalcv-1.0.0/tests/test_influence.py +272 -0
- temporalcv-1.0.0/tests/test_integration.py +392 -0
- temporalcv-1.0.0/tests/test_lag_selection.py +448 -0
- temporalcv-1.0.0/tests/test_metrics_asymmetric.py +616 -0
- temporalcv-1.0.0/tests/test_metrics_core.py +341 -0
- temporalcv-1.0.0/tests/test_metrics_event.py +651 -0
- temporalcv-1.0.0/tests/test_metrics_financial.py +628 -0
- temporalcv-1.0.0/tests/test_metrics_quantile.py +658 -0
- temporalcv-1.0.0/tests/test_metrics_volatility.py +574 -0
- temporalcv-1.0.0/tests/test_persistence.py +501 -0
- temporalcv-1.0.0/tests/test_regimes.py +599 -0
- temporalcv-1.0.0/tests/test_residual_bootstrap.py +430 -0
- temporalcv-1.0.0/tests/test_sensitivity.py +384 -0
- temporalcv-1.0.0/tests/test_stationarity.py +518 -0
- temporalcv-1.0.0/tests/test_statistical_tests.py +3572 -0
- temporalcv-1.0.0/tests/test_stratified_reports.py +414 -0
- temporalcv-1.0.0/tests/test_theoretical_bounds.py +379 -0
- temporalcv-1.0.0/tests/test_wild_bootstrap.py +244 -0
- temporalcv-1.0.0/tests/validation/__init__.py +9 -0
- temporalcv-1.0.0/tests/validation/test_residual_diagnostics.py +310 -0
- temporalcv-1.0.0/tests/validation/test_shuffled_target.py +305 -0
- temporalcv-1.0.0/tests/validation/test_synthetic_ar1.py +280 -0
- temporalcv-1.0.0/tests/validation/test_theoretical_bounds.py +329 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
.env
|
|
26
|
+
.venv
|
|
27
|
+
env/
|
|
28
|
+
venv/
|
|
29
|
+
ENV/
|
|
30
|
+
|
|
31
|
+
# pytest / coverage
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
.hypothesis/
|
|
34
|
+
.coverage
|
|
35
|
+
.coverage.*
|
|
36
|
+
htmlcov/
|
|
37
|
+
.tox/
|
|
38
|
+
.nox/
|
|
39
|
+
|
|
40
|
+
# Sphinx build output
|
|
41
|
+
docs/_build/
|
|
42
|
+
|
|
43
|
+
# mypy
|
|
44
|
+
.mypy_cache/
|
|
45
|
+
.dmypy.json
|
|
46
|
+
dmypy.json
|
|
47
|
+
|
|
48
|
+
# IDEs
|
|
49
|
+
.idea/
|
|
50
|
+
.vscode/
|
|
51
|
+
*.swp
|
|
52
|
+
*.swo
|
|
53
|
+
*~
|
|
54
|
+
|
|
55
|
+
# Jupyter
|
|
56
|
+
.ipynb_checkpoints/
|
|
57
|
+
|
|
58
|
+
# OS
|
|
59
|
+
.DS_Store
|
|
60
|
+
Thumbs.db
|
|
61
|
+
|
|
62
|
+
# Benchmark data and intermediate files
|
|
63
|
+
None/
|
|
64
|
+
.tracking/
|
temporalcv-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Brandon Behring
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: temporalcv
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Temporal cross-validation with leakage protection for time-series ML
|
|
5
|
+
Project-URL: Homepage, https://github.com/brandonmbehring-dev/temporalcv
|
|
6
|
+
Project-URL: Documentation, https://github.com/brandonmbehring-dev/temporalcv#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/brandonmbehring-dev/temporalcv
|
|
8
|
+
Project-URL: Issues, https://github.com/brandonmbehring-dev/temporalcv/issues
|
|
9
|
+
Author: Brandon Behring
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: cross-validation,forecasting,leakage-detection,machine-learning,temporal-validation,time-series,walk-forward
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: numpy>=1.21
|
|
28
|
+
Requires-Dist: scikit-learn>=1.0
|
|
29
|
+
Requires-Dist: scipy>=1.7
|
|
30
|
+
Requires-Dist: statsmodels>=0.13
|
|
31
|
+
Provides-Extra: all
|
|
32
|
+
Requires-Dist: datasetsforecast>=0.0.8; extra == 'all'
|
|
33
|
+
Requires-Dist: fredapi>=0.5; extra == 'all'
|
|
34
|
+
Requires-Dist: gluonts>=0.13; extra == 'all'
|
|
35
|
+
Requires-Dist: pandas>=1.3; extra == 'all'
|
|
36
|
+
Requires-Dist: statsforecast>=1.5; extra == 'all'
|
|
37
|
+
Provides-Extra: benchmarks
|
|
38
|
+
Requires-Dist: datasetsforecast>=0.0.8; extra == 'benchmarks'
|
|
39
|
+
Requires-Dist: fredapi>=0.5; extra == 'benchmarks'
|
|
40
|
+
Provides-Extra: changepoint
|
|
41
|
+
Requires-Dist: ruptures>=1.1; extra == 'changepoint'
|
|
42
|
+
Provides-Extra: compare
|
|
43
|
+
Requires-Dist: statsforecast>=1.5; extra == 'compare'
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: hypothesis>=6.0; extra == 'dev'
|
|
46
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: pip-audit>=2.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: pre-commit>=3.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: pytest-benchmark>=4.0; extra == 'dev'
|
|
50
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
51
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
52
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
53
|
+
Provides-Extra: docs
|
|
54
|
+
Requires-Dist: myst-parser>=2.0.0; extra == 'docs'
|
|
55
|
+
Requires-Dist: sphinx-autodoc-typehints>=1.25; extra == 'docs'
|
|
56
|
+
Requires-Dist: sphinx-copybutton>=0.5.2; extra == 'docs'
|
|
57
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == 'docs'
|
|
58
|
+
Requires-Dist: sphinx>=7.2; extra == 'docs'
|
|
59
|
+
Provides-Extra: fred
|
|
60
|
+
Requires-Dist: fredapi>=0.5; extra == 'fred'
|
|
61
|
+
Provides-Extra: gluonts
|
|
62
|
+
Requires-Dist: gluonts>=0.13; extra == 'gluonts'
|
|
63
|
+
Provides-Extra: monash
|
|
64
|
+
Requires-Dist: datasetsforecast>=0.0.8; extra == 'monash'
|
|
65
|
+
Provides-Extra: pandas
|
|
66
|
+
Requires-Dist: pandas>=1.3; extra == 'pandas'
|
|
67
|
+
Description-Content-Type: text/markdown
|
|
68
|
+
|
|
69
|
+
# temporalcv
|
|
70
|
+
|
|
71
|
+
**Temporal cross-validation with leakage protection for time-series ML.**
|
|
72
|
+
|
|
73
|
+
[](https://github.com/brandonmbehring-dev/temporalcv/actions)
|
|
74
|
+
[](https://pypi.org/project/temporalcv/)
|
|
75
|
+
[](https://pypi.org/project/temporalcv/)
|
|
76
|
+
[](https://colab.research.google.com/github/brandonmbehring-dev/temporalcv/blob/main/notebooks/demo.ipynb)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Why temporalcv?
|
|
81
|
+
|
|
82
|
+
Time-series ML has a leakage problem. Standard cross-validation doesn't respect temporal order, and even "proper" walk-forward implementations often miss subtle bugs:
|
|
83
|
+
|
|
84
|
+
- **Lag features computed on full series** (leaks future information)
|
|
85
|
+
- **No gap between train and test** (target leaks into features)
|
|
86
|
+
- **Thresholds computed on full series** (future information in classification)
|
|
87
|
+
|
|
88
|
+
temporalcv provides **validation gates** that catch these bugs before they corrupt your results.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Architecture
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
96
|
+
│ VALIDATION PIPELINE │
|
|
97
|
+
├─────────────────────────────────────────────────────────────────────────┤
|
|
98
|
+
│ │
|
|
99
|
+
│ Data + Model │
|
|
100
|
+
│ │ │
|
|
101
|
+
│ ▼ │
|
|
102
|
+
│ ┌──────────────────────────────────────────────────────────────┐ │
|
|
103
|
+
│ │ VALIDATION GATES │ │
|
|
104
|
+
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
|
|
105
|
+
│ │ │ Shuffled │ │ Temporal │ │ Suspicious │ │ │
|
|
106
|
+
│ │ │ Target Test │ │ Boundary │ │ Improvement │ │ │
|
|
107
|
+
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
|
|
108
|
+
│ │ │ │ │ │ │
|
|
109
|
+
│ │ └─────────────────┼─────────────────┘ │ │
|
|
110
|
+
│ │ ▼ │ │
|
|
111
|
+
│ │ ┌───────────────────────┐ │ │
|
|
112
|
+
│ │ │ HALT / WARN / PASS │ │ │
|
|
113
|
+
│ │ └───────────────────────┘ │ │
|
|
114
|
+
│ └──────────────────────────────────────────────────────────────┘ │
|
|
115
|
+
│ │ │
|
|
116
|
+
│ HALT ◄───────────┼───────────► PASS │
|
|
117
|
+
│ │ │ │ │
|
|
118
|
+
│ ▼ ▼ ▼ │
|
|
119
|
+
│ ┌─────────┐ ┌─────────┐ ┌─────────────────────────────┐ │
|
|
120
|
+
│ │ STOP & │ │ WARN │ │ CONTINUE TO: │ │
|
|
121
|
+
│ │INVESTIGATE│ │ USER │ │ - Walk-Forward CV │ │
|
|
122
|
+
│ └─────────┘ └─────────┘ │ - Statistical Tests (DM/PT)│ │
|
|
123
|
+
│ │ - Conformal Prediction │ │
|
|
124
|
+
│ │ - Deployment │ │
|
|
125
|
+
│ └─────────────────────────────┘ │
|
|
126
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Gate Priority
|
|
130
|
+
|
|
131
|
+
| Status | Meaning | Action |
|
|
132
|
+
|--------|---------|--------|
|
|
133
|
+
| **HALT** | Critical failure detected | Stop immediately, investigate |
|
|
134
|
+
| **WARN** | Suspicious signal | Proceed with caution, verify externally |
|
|
135
|
+
| **PASS** | Validation passed | Continue to next stage |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## What Makes This Unique
|
|
140
|
+
|
|
141
|
+
1. **Shuffled Target Test** — The definitive leakage detector
|
|
142
|
+
- If your model beats a permuted baseline, features encode target position
|
|
143
|
+
- Catches: rolling stats on full series, lookahead bias, centered windows
|
|
144
|
+
|
|
145
|
+
2. **HALT/WARN/PASS Framework** — Actionable validation status
|
|
146
|
+
- Not just metrics, but decisions
|
|
147
|
+
- Prioritized: HALT > WARN > PASS
|
|
148
|
+
|
|
149
|
+
3. **Temporal-Aware Conformal Prediction**
|
|
150
|
+
- Adaptive conformal for distribution shift (Gibbs & Candès 2021)
|
|
151
|
+
- Approximate coverage for time series (exact guarantees require exchangeability)
|
|
152
|
+
|
|
153
|
+
4. **High-Persistence Metrics** — For sticky series (ACF(1) > 0.9)
|
|
154
|
+
- MASE, MC-SS ratio, directional accuracy
|
|
155
|
+
- Standard metrics mislead on near-unit-root data
|
|
156
|
+
|
|
157
|
+
5. **sklearn Integration** — Drop-in replacement
|
|
158
|
+
- `WalkForwardCV` works with `cross_val_score`, `GridSearchCV`
|
|
159
|
+
- Proper gap enforcement for h-step forecasting
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Julia Implementation
|
|
164
|
+
|
|
165
|
+
The Julia version of this library is available in a separate repository: **[temporalcv.jl](https://github.com/brandondebehring/temporalcv.jl)**.
|
|
166
|
+
|
|
167
|
+
It provides native Julia implementations of the same core validation gates and statistical tests.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Comparison vs sklearn TimeSeriesSplit
|
|
172
|
+
|
|
173
|
+
| Feature | temporalcv | sklearn | Winner |
|
|
174
|
+
|---------|------------|---------|--------|
|
|
175
|
+
| Gap Enforcement | ✅ Native | ✅ v1.0+ | Both |
|
|
176
|
+
| Window Types | Expanding + Sliding | Expanding only | **temporalcv** |
|
|
177
|
+
| Leakage Detection | 3 validation gates | None | **temporalcv** |
|
|
178
|
+
| Statistical Tests | DM, PT, HAC | None | **temporalcv** |
|
|
179
|
+
| Conformal Prediction | Split + Adaptive | External (MAPIE) | **temporalcv** |
|
|
180
|
+
| Financial CV | Purging + Embargo | None | **temporalcv** |
|
|
181
|
+
| Split Speed | ~0.035 ms | ~0.012 ms | sklearn |
|
|
182
|
+
|
|
183
|
+
**Key Insight**: sklearn's `TimeSeriesSplit` handles basic temporal splits well. temporalcv adds the validation layer that catches bugs *before* they corrupt your results.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Installation
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
pip install temporalcv
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
For development:
|
|
194
|
+
```bash
|
|
195
|
+
pip install temporalcv[dev]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Optional Dependencies
|
|
199
|
+
|
|
200
|
+
temporalcv has modular dependencies for specific features:
|
|
201
|
+
|
|
202
|
+
| Feature | Install Command | When Needed |
|
|
203
|
+
|---------|----------------|-------------|
|
|
204
|
+
| **Benchmarks** | `pip install temporalcv[benchmarks]` | Running M4/M5 benchmarks |
|
|
205
|
+
| **Changepoint** | `pip install temporalcv[changepoint]` | PELT algorithm (requires `ruptures`) |
|
|
206
|
+
| **Model Comparison** | `pip install temporalcv[compare]` | Benchmark runner with DM tests |
|
|
207
|
+
| **Development** | `pip install temporalcv[dev]` | Testing, linting, type checking |
|
|
208
|
+
| **All Features** | `pip install temporalcv[all]` | Everything above |
|
|
209
|
+
|
|
210
|
+
**Core dependencies** (always installed):
|
|
211
|
+
- `numpy >= 1.23.0`
|
|
212
|
+
- `scipy >= 1.9.0`
|
|
213
|
+
- `scikit-learn >= 1.1.0`
|
|
214
|
+
- `pandas >= 1.5.0`
|
|
215
|
+
|
|
216
|
+
### Platform Compatibility
|
|
217
|
+
|
|
218
|
+
| Platform | Status | Tested Versions |
|
|
219
|
+
|----------|--------|-----------------|
|
|
220
|
+
| **Linux** | ✅ Fully supported | Ubuntu 20.04+, Debian 11+ |
|
|
221
|
+
| **macOS** | ✅ Fully supported | macOS 11+ (Intel & Apple Silicon) |
|
|
222
|
+
| **Windows** | ✅ Fully supported | Windows 10+, Windows Server 2019+ |
|
|
223
|
+
|
|
224
|
+
**Python versions**: 3.9, 3.10, 3.11, 3.12
|
|
225
|
+
|
|
226
|
+
**CI Matrix**: All combinations tested on every PR via GitHub Actions.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Quick Example
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from temporalcv import run_gates, WalkForwardCV
|
|
234
|
+
from temporalcv.gates import gate_shuffled_target, gate_suspicious_improvement
|
|
235
|
+
|
|
236
|
+
# Validate your model doesn't have leakage
|
|
237
|
+
# Step 1: Compute gate results
|
|
238
|
+
# Note: n_shuffles>=100 required for statistical power in permutation mode (default)
|
|
239
|
+
gate_results = [
|
|
240
|
+
gate_shuffled_target(my_model, X, y, n_shuffles=100),
|
|
241
|
+
gate_suspicious_improvement(model_mae, persistence_mae, threshold=0.20),
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
# Step 2: Aggregate into report
|
|
245
|
+
report = run_gates(gate_results)
|
|
246
|
+
|
|
247
|
+
if report.status == "HALT":
|
|
248
|
+
raise ValueError(f"Leakage detected: {report.summary()}")
|
|
249
|
+
|
|
250
|
+
# Walk-forward CV with proper gap enforcement
|
|
251
|
+
cv = WalkForwardCV(
|
|
252
|
+
window_type="sliding",
|
|
253
|
+
window_size=104,
|
|
254
|
+
horizon=2, # Minimum required separation for 2-step forecasting
|
|
255
|
+
extra_gap=0, # Optional: add safety margin (default: 0)
|
|
256
|
+
test_size=1
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
for train_idx, test_idx in cv.split(X, y):
|
|
260
|
+
# Guaranteed: train_idx[-1] + gap < test_idx[0]
|
|
261
|
+
model.fit(X[train_idx], y[train_idx])
|
|
262
|
+
predictions = model.predict(X[test_idx])
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Features
|
|
268
|
+
|
|
269
|
+
### Validation Gates
|
|
270
|
+
- **Shuffled target test** - Definitive leakage detection
|
|
271
|
+
- **Synthetic AR(1) bounds** - Theoretical validation
|
|
272
|
+
- **Suspicious improvement detection** - >20% = investigate
|
|
273
|
+
- **Temporal boundary audit** - No future in features
|
|
274
|
+
|
|
275
|
+
### Statistical Tests
|
|
276
|
+
- **Diebold-Mariano test** - With HAC variance estimation
|
|
277
|
+
- **Pesaran-Timmermann test** - Direction accuracy (3-class)
|
|
278
|
+
|
|
279
|
+
### Walk-Forward CV
|
|
280
|
+
- Sliding and expanding windows
|
|
281
|
+
- Gap parameter enforcement
|
|
282
|
+
- sklearn-compatible splitter API
|
|
283
|
+
|
|
284
|
+
### High-Persistence Metrics
|
|
285
|
+
- **MC-SS** - Move-Conditional Skill Score
|
|
286
|
+
- **Move-only MAE** - Error when target moved
|
|
287
|
+
- **Direction Brier** - Probabilistic direction accuracy
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Examples
|
|
292
|
+
|
|
293
|
+
Real-world case studies demonstrating key features:
|
|
294
|
+
|
|
295
|
+
| Example | Description |
|
|
296
|
+
|---------|-------------|
|
|
297
|
+
| [01_leakage_detection.py](examples/01_leakage_detection.py) | Shuffled target test catches lookahead bias |
|
|
298
|
+
| [02_walk_forward_cv.py](examples/02_walk_forward_cv.py) | Gap enforcement for h-step forecasting |
|
|
299
|
+
| [03_statistical_tests.py](examples/03_statistical_tests.py) | DM test: is improvement significant? |
|
|
300
|
+
| [04_high_persistence.py](examples/04_high_persistence.py) | MASE metrics for sticky series |
|
|
301
|
+
| [05_conformal_prediction.py](examples/05_conformal_prediction.py) | Adaptive intervals under distribution shift |
|
|
302
|
+
|
|
303
|
+
**Interactive Demo**: [](https://colab.research.google.com/github/brandonmbehring-dev/temporalcv/blob/main/notebooks/demo.ipynb)
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Benchmark Comparison
|
|
308
|
+
|
|
309
|
+
### Feature Matrix
|
|
310
|
+
|
|
311
|
+
| Feature | temporalcv | sklearn | sktime | Darts |
|
|
312
|
+
|---------|------------|---------|--------|-------|
|
|
313
|
+
| **Gap enforcement** | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ Manual |
|
|
314
|
+
| **Leakage detection** | ✅ Gates | ❌ None | ❌ None | ❌ None |
|
|
315
|
+
| **Horizon validation** | ✅ Warnings | ❌ None | ❌ None | ❌ None |
|
|
316
|
+
| **Statistical tests (DM)** | ✅ HAC variance | ❌ None | ✅ Basic | ❌ None |
|
|
317
|
+
| **Conformal prediction** | ✅ Adaptive | ❌ None | ❌ None | ✅ Split |
|
|
318
|
+
| **sklearn compatible** | ✅ Full | ✅ Native | ✅ Full | ❌ Partial |
|
|
319
|
+
|
|
320
|
+
### Why Not Just sklearn's TimeSeriesSplit?
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
from sklearn.model_selection import TimeSeriesSplit
|
|
324
|
+
|
|
325
|
+
# sklearn: No gap, no horizon validation
|
|
326
|
+
cv = TimeSeriesSplit(n_splits=5) # Target leakage possible for h>1
|
|
327
|
+
|
|
328
|
+
# temporalcv: Gap enforcement + validation
|
|
329
|
+
from temporalcv import WalkForwardCV
|
|
330
|
+
cv = WalkForwardCV(n_splits=5, horizon=2, extra_gap=0) # total_separation = horizon + extra_gap
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Benchmark Runner
|
|
334
|
+
|
|
335
|
+
Compare models across datasets:
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
from temporalcv.benchmarks import create_synthetic_dataset
|
|
339
|
+
from temporalcv.compare import run_benchmark_suite, NaiveAdapter
|
|
340
|
+
|
|
341
|
+
datasets = [create_synthetic_dataset(seed=i) for i in range(3)]
|
|
342
|
+
report = run_benchmark_suite(datasets, [NaiveAdapter()], include_dm_test=True)
|
|
343
|
+
print(report.to_markdown())
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Documentation
|
|
349
|
+
|
|
350
|
+
### Getting Started
|
|
351
|
+
- [**Quickstart Guide**](docs/quickstart.md) - Get started in 5 minutes
|
|
352
|
+
|
|
353
|
+
### Tutorials
|
|
354
|
+
- [Leakage Detection](docs/tutorials/leakage_detection.md) - Catch data leakage with validation gates
|
|
355
|
+
- [Walk-Forward CV](docs/tutorials/walk_forward_cv.md) - Proper temporal cross-validation
|
|
356
|
+
- [High-Persistence Metrics](docs/tutorials/high_persistence.md) - Metrics for sticky series
|
|
357
|
+
- [Uncertainty Quantification](docs/tutorials/uncertainty.md) - Prediction intervals with coverage guarantees
|
|
358
|
+
|
|
359
|
+
### API Reference
|
|
360
|
+
- [Validation Gates](docs/api/gates.md) - HALT/PASS/WARN framework
|
|
361
|
+
- [Walk-Forward CV](docs/api/cv.md) - sklearn-compatible temporal CV
|
|
362
|
+
- [Statistical Tests](docs/api/statistical_tests.md) - DM test, PT test, HAC variance
|
|
363
|
+
- [High-Persistence Metrics](docs/api/persistence.md) - MC-SS, move-conditional MAE
|
|
364
|
+
- [Regime Classification](docs/api/regimes.md) - Volatility and direction regimes
|
|
365
|
+
- [Conformal Prediction](docs/api/conformal.md) - Distribution-free intervals
|
|
366
|
+
- [Bagging](docs/api/bagging.md) - Time-series-aware bagging
|
|
367
|
+
- [Event Metrics](docs/api/metrics.md) - Brier score, PR-AUC
|
|
368
|
+
|
|
369
|
+
### Internal
|
|
370
|
+
- [Planning Documentation](docs/plans/INDEX.md)
|
|
371
|
+
- [Ecosystem Gap Analysis](docs/plans/reference/ecosystem_gaps.md)
|
|
372
|
+
|
|
373
|
+
### Help & Support
|
|
374
|
+
- [**Troubleshooting Guide**](docs/troubleshooting.md) - Common issues and solutions
|
|
375
|
+
- [**Testing Strategy**](docs/testing_strategy.md) - How temporalcv is tested
|
|
376
|
+
- [**Benchmark Methodology**](docs/benchmarks/methodology.md) - How benchmark results are generated
|
|
377
|
+
- [**GitHub Issues**](https://github.com/brandonmbehring-dev/temporalcv/issues) - Report bugs or request features
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Citation
|
|
382
|
+
|
|
383
|
+
If you use temporalcv in your research, please cite:
|
|
384
|
+
|
|
385
|
+
```bibtex
|
|
386
|
+
@software{temporalcv2025,
|
|
387
|
+
author = {Behring, Brandon},
|
|
388
|
+
title = {temporalcv: Temporal cross-validation with leakage protection},
|
|
389
|
+
year = {2025},
|
|
390
|
+
publisher = {GitHub},
|
|
391
|
+
url = {https://github.com/brandonmbehring-dev/temporalcv},
|
|
392
|
+
version = {1.0.0}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
See [CITATION.cff](CITATION.cff) for additional citation formats.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## License
|
|
401
|
+
|
|
402
|
+
MIT License - see [LICENSE](LICENSE)
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Contributing
|
|
407
|
+
|
|
408
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|