skills/timesfm-forecasting/references/examples_and_validation.md
Runnable examples, the pre-delivery quality checklist, the mistakes that most often produce wrong forecasts, and the regression checks that confirm the skill still works.
Three fully-working reference examples live in examples/. Use them as ground truth for correct API usage and expected output shape.
| Example | Directory | What It Demonstrates | When To Use It |
|---|---|---|---|
| Global Temperature Forecast | examples/global-temperature/ | Basic model.forecast() call, CSV -> PNG -> GIF pipeline, 36-month NOAA context | Starting point; copy-paste baseline for any univariate series |
| Anomaly Detection | examples/anomaly-detection/ | Two-phase detection: linear detrend + Z-score on context, quantile PI on forecast; 2-panel viz | Any task requiring outlier detection on historical + forecasted data |
| Covariates (XReg) | examples/covariates-forecasting/ | forecast_with_covariates() API (TimesFM 2.5), covariate decomposition, 2x2 shared-axis viz | Retail, energy, or any series with known exogenous drivers |
# Global temperature (no TimesFM 2.5 needed)
cd examples/global-temperature && python run_forecast.py && python visualize_forecast.py
# Anomaly detection (uses TimesFM 1.0)
cd examples/anomaly-detection && python detect_anomalies.py
# Covariates (API demo -- requires TimesFM 2.5 + timesfm[xreg] for real inference)
cd examples/covariates-forecasting && python demo_covariates.py
| Example | Key output files | Acceptance criteria |
|---|---|---|
| global-temperature | output/forecast_output.json, output/forecast_visualization.png | point_forecast has 12 values; PNG shows context + forecast + PI bands |
| anomaly-detection | output/anomaly_detection.json, output/anomaly_detection.png | Sep 2023 flagged CRITICAL (z >= 3.0); >= 2 forecast CRITICAL from injected anomalies |
| covariates-forecasting | output/sales_with_covariates.csv, output/covariates_data.png | CSV has 108 rows (3 stores x 36 weeks); stores have distinct price arrays |
Run this checklist after every TimesFM task before declaring success:
point_fc shape is (n_series, horizon), quant_fc is (n_series, horizon, 10)freq=[0] for monthly data. TimesFM 2.5: no freq flag.np.isnan(point_fc).any() should be False. Check input series for gaps first.sharex=True. All time axes must cover the same span..gitattributes (repo root already configured).tempfile.mkdtemp() and annotated in code.matplotlib.use('Agg') -- must appear before any pyplot import when running headless.infer_is_positive -- set False for temperature anomalies, financial returns, or any series that can be negative.These bugs have appeared in this skill's examples. Learn from them:
Quantile index off-by-one -- The most common mistake. quant_fc[..., 0] is the mean, not q0. q10 = index 1, q90 = index 9. Always define named constants: IDX_Q10, IDX_Q20, IDX_Q80, IDX_Q90 = 1, 2, 8, 9.
Variable shadowing in comprehensions -- If you build per-series covariate dicts inside a loop, do NOT use the loop variable as the comprehension variable. Accumulate into separate dict[str, ndarray] outside the loop, then assign.
# WRONG -- outer `store_id` gets shadowed:
covariates = {store_id: arr[store_id] for store_id in stores} # inside outer loop over store_id
# CORRECT -- use a different name or accumulate beforehand:
prices_by_store: dict[str, np.ndarray] = {}
for store_id, config in stores.items():
prices_by_store[store_id] = compute_price(config)
Wrong CSV column name -- The global-temperature CSV uses anomaly_c, not anomaly. Always print(df.columns) before accessing.
tight_layout() warning with sharex=True -- Harmless; suppress with plt.tight_layout(rect=[0, 0, 1, 0.97]) or ignore.
TimesFM 2.5 required for forecast_with_covariates() -- TimesFM 1.0 does NOT have this method. Install pip install timesfm[xreg] and use checkpoint google/timesfm-2.5-200m-pytorch.
Future covariates must span the full horizon -- Dynamic covariates (price, promotions, holidays) must have values for BOTH the context AND the forecast horizon. You cannot pass context-only arrays.
Anomaly thresholds must be defined once -- Define CRITICAL_Z = 3.0, WARNING_Z = 2.0 as module-level constants. Never hardcode 3 or 2 inline.
Context anomaly detection uses residuals, not raw values -- Always detrend first (np.polyfit linear, or seasonal decomposition), then Z-score the residuals. Raw-value Z-scores are misleading on trending data.
Use the example outputs as regression baselines. If you change forecasting logic, verify:
# Anomaly detection regression check:
python -c "
import json
d = json.load(open('examples/anomaly-detection/output/anomaly_detection.json'))
ctx = d['context_summary']
assert ctx['critical'] >= 1, 'Sep 2023 must be CRITICAL'
assert any(r['date'] == '2023-09' and r['severity'] == 'CRITICAL'
for r in d['context_detections']), 'Sep 2023 not found'
print('Anomaly detection regression: PASS')"
# Covariates regression check:
python -c "
import pandas as pd
df = pd.read_csv('examples/covariates-forecasting/output/sales_with_covariates.csv')
assert len(df) == 108, f'Expected 108 rows, got {len(df)}'
prices = df.groupby('store_id')['price'].mean()
assert prices['store_A'] > prices['store_B'] > prices['store_C'], 'Store price ordering wrong'
print('Covariates regression: PASS')"