Note
Go to the end to download the full example code.
Quickstart: temporalcv in 50 lines¶
This is the fastest way to understand temporalcv’s core value proposition.
- Run this example:
python examples/00_quickstart.py
- What you’ll learn:
How validation gates catch data leakage
The HALT/WARN/PASS decision framework
Basic walk-forward cross-validation
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import Ridge
from temporalcv.cv import WalkForwardCV
from temporalcv.gates import gate_signal_verification, gate_suspicious_improvement, run_gates
from temporalcv.viz import apply_tufte_style
# Generate simple time series data
rng = np.random.default_rng(42)
n = 200
y = np.cumsum(rng.standard_normal(n)) # Random walk
X = np.column_stack([np.roll(y, i) for i in range(1, 6)]) # Lag features
X, y = X[5:], y[5:] # Remove NaN rows
# Step 1: Walk-forward cross-validation (the right way)
cv = WalkForwardCV(n_splits=5, extra_gap=1, test_size=20)
print("Walk-Forward CV splits:")
for i, (train, test) in enumerate(cv.split(X, y)):
print(f" Fold {i+1}: train={len(train)}, test={len(test)}, gap enforced")
# Step 2: Fit model and compute errors
model = Ridge(alpha=1.0)
all_errors = []
persistence_errors = []
for train_idx, test_idx in cv.split(X, y):
model.fit(X[train_idx], y[train_idx])
preds = model.predict(X[test_idx])
all_errors.extend(np.abs(y[test_idx] - preds))
persistence_errors.extend(np.abs(y[test_idx] - y[test_idx - 1]))
model_mae = np.mean(all_errors)
baseline_mae = np.mean(persistence_errors)
print(f"\nModel MAE: {model_mae:.4f}")
print(f"Baseline MAE: {baseline_mae:.4f}")
print(f"Improvement: {(1 - model_mae/baseline_mae)*100:.1f}%")
# Step 3: Run validation gates
print("\n--- Validation Gates ---")
gates = [
gate_signal_verification(model, X, y, n_shuffles=100, random_state=42),
gate_suspicious_improvement(model_mae, baseline_mae, threshold=0.20),
]
report = run_gates(gates)
print(report.summary())
# Step 4: Make decision
if report.status == "HALT":
print("\n⛔ HALT: Investigate before deploying!")
elif report.status == "WARN":
print("\n⚠️ WARN: Proceed with caution")
else:
print("\n✅ PASS: Safe to continue")
Walk-Forward CV splits:
Fold 1: train=94, test=20, gap enforced
Fold 2: train=114, test=20, gap enforced
Fold 3: train=134, test=20, gap enforced
Fold 4: train=154, test=20, gap enforced
Fold 5: train=174, test=20, gap enforced
Model MAE: 0.8162
Baseline MAE: 0.7818
Improvement: -4.4%
--- Validation Gates ---
============================================================
VALIDATION REPORT
============================================================
[HALT] signal_verification: Permutation test: p=0.0099 < α=0.05 (model has signal)
[PASS] suspicious_improvement: Improvement -4.4% is reasonable
============================================================
OVERALL STATUS: HALT
============================================================
HALTED GATES (require investigation):
- signal_verification: Model has predictive signal. Investigate source: legitimate temporal patterns (expected for AR models) OR data leakage (check feature engineering).
⛔ HALT: Investigate before deploying!
Visualize Walk-Forward CV Folds¶
This plot shows how WalkForwardCV splits the data temporally, with training (blue) and test (orange) sets, and the gap between them.
fig, ax = plt.subplots(figsize=(10, 4))
cv = WalkForwardCV(n_splits=5, extra_gap=1, test_size=20)
n_samples = len(X)
for fold_idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):
# Training set
ax.barh(
fold_idx,
len(train_idx),
left=train_idx[0],
height=0.6,
color="#1f77b4",
alpha=0.8,
label="Train" if fold_idx == 0 else "",
)
# Gap
gap_start = train_idx[-1] + 1
gap_end = test_idx[0]
ax.barh(
fold_idx,
gap_end - gap_start,
left=gap_start,
height=0.6,
color="#d62728",
alpha=0.5,
label="Gap" if fold_idx == 0 else "",
)
# Test set
ax.barh(
fold_idx,
len(test_idx),
left=test_idx[0],
height=0.6,
color="#ff7f0e",
alpha=0.8,
label="Test" if fold_idx == 0 else "",
)
ax.set_xlabel("Sample Index")
ax.set_ylabel("CV Fold")
ax.set_yticks(range(5))
ax.set_yticklabels([f"Fold {i+1}" for i in range(5)])
ax.set_title("Walk-Forward Cross-Validation with Gap Enforcement")
ax.legend(loc="upper left")
ax.set_xlim(0, n_samples)
# Apply Tufte styling
apply_tufte_style(ax)
plt.tight_layout()
plt.show()

Total running time of the script: (0 minutes 1.310 seconds)