Skip to content

feat(core): accept X_df and futr_exog_list in detect_anomalies - #82

Open
sharmis-tp wants to merge 1 commit into
TimeCopilot:mainfrom
sharmis-tp:issue-75-detect-anomalies-xdf
Open

sharmis-tp wants to merge 1 commit into
TimeCopilot:mainfrom
sharmis-tp:issue-75-detect-anomalies-xdf

Conversation

@sharmis-tp

Copy link
Copy Markdown

Closes #75

detect_anomalies() scores residuals from in-sample cross-validation, but unlike forecast() and cross_validation() it had no way to pass known-future covariates. This adds X_df and futr_exog_list to detect_anomalies() on Forecaster and FoundationForecast, using the same contract and exog_strategy as forecast and CV.

What changed

  • Forecaster.detect_anomalies(df, h, freq, n_windows, level, X_df=None, *, futr_exog_list=None). Covariates reach the existing CV split loop, which already builds a per-fold X_df from the holdout rows. There are two ways to pass them:
    • exog columns in df, as for cross_validation() (this already worked implicitly, and is now documented), or
    • a separate X_df with a row for every (unique_id, ds) in df. Every window is scored in-sample, so the covariates must cover the whole history. A new merge_insample_exog helper in core/exog/covariates.py merges X_df into df before the windows are built and ignores any extra rows. It raises a clear ValueError when X_df is missing rows, has duplicate keys, lacks unique_id/ds, or repeats a covariate that is already a column of df.
  • With exog_strategy=False, X_df and futr_exog_list are ignored and scoring is univariate, as in forecast and CV.
  • FoundationForecast.detect_anomalies passes X_df and futr_exog_list to every member. As suggested in the thread, members without native exog set exog_strategy=False, so a mixed ensemble works. _validate_models_horizon_exog now covers detect_anomalies too, so an "auto" member without native exog fails up front with the usual "does not support known-future exogenous variables" error, and fallback_model is not used in its place. This matches forecast and CV. One behaviour change: before this PR, exog columns in df with such a member raised inside the model, and a configured fallback_model then ran instead.

Tests

  • tests/core/test_forecaster.py: a native-exog spy model (ExogSpyModel in tests/helpers.py) checks that detect_anomalies calls the forecast path with each fold's X_df rows and that its forecasts use them. Also covered: X_df and exog columns in df give identical results (even with X_df shuffled); futr_exog_list selects columns; exog_strategy=False ignores X_df; the invalid-X_df errors; and a non-native model rejecting X_df.
  • tests/test_foundation_forecast.py: a mixed ensemble (native spy plus a model with exog_strategy=False) with X_df, and up-front validation before the fallback runs, both with X_df and with exog columns in df.
  • tests/models/test_models.py: a parametrized test_exog_detect_anomalies_with_X_df that skips models where supports_native_futr_exog() is false.
  • DummyModel in tests/helpers.py now takes exog_strategy.

Docs

  • docs/exogenous-variables.md: detect_anomalies in the Data API and exog_strategy table, a new "Anomaly detection" section, and runnable examples (a mixed FoundationForecast with exog in df, and Chronos-2 with a separate X_df).
  • docs/forecasting-parameters.md: a note under "Anomaly detection defaults".
  • README.md: the Highlights and support-matrix lines now cover detect_anomalies alongside forecast/cross_validation.

Checks run (Python 3.11, uv sync --group dev)

  • uv run pre-commit run --all-files and --hook-stage manual (ruff, ruff-format, mypy, toml-sort): pass
  • uv run pytest tests/core tests/test_foundation_forecast.py tests/test_package_imports.py: 147 passed. The 12 new tests in tests/core/test_forecaster.py and tests/test_foundation_forecast.py fail on main.
  • uv run pytest tests/models/test_models.py -k test_exog_detect_anomalies_with_X_df, run for the Chronos-2 (×2), Timestral-5M, TiRex-2 and T0 alpha/beta/gamma entries: 7 passed. TimesFM-3 and TimeH2O were not run locally, so CI covers them.
  • uv run pytest -m docs -o addopts= -n 0 tests/docs/test_docs.py -k exogenous-variables.md: pass
  • uv run --group docs zensical build: no issues

@CLAassistant

CLAassistant commented Oct 28, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(core): accept X_df / exogenous variables in detect_anomalies

2 participants