Tiny Anomaly Lab is an anomaly-detection workbench for small time-series data.
It runs one experiment on bundled data and writes a reproducible interactive report. The lab shows the feature pipeline, leakage-safe evaluation, model cards, feature contributions, and an interactive chart.
The lab targets a few hundred rows with a timestamp, two sensor channels and a known anomaly label. It is a teaching and review tool. It is not a production control system.
- Polars reads and shapes the data.
- scikit-learn trains the detectors and the scaler.
- Plotly renders a self-contained HTML report.
The project uses only established, open-source dependencies. No paid service, secret or network call is needed for the demo or for the tests.
| Module | Task |
|---|---|
data.py |
Loads the bundled CSV and validates the schema. |
features.py |
Builds causal rolling and lag features. |
split.py |
Splits the feature frame by time only. |
models.py |
Holds the detector registry, scorers and contributions. |
evaluate.py |
Computes ranking and threshold metrics. |
export.py |
Writes scores and flagged rows to CSV. |
model_card.py |
Renders a Markdown model card. |
report.py |
Writes a self-contained Plotly HTML report. |
experiment.py |
Runs one experiment from start to finish. |
__main__.py |
Exposes the command line interface. |
The experiment runs these modules in one pipeline. The data step loads the CSV. The feature step adds causal rows. The split step divides rows by time. The model step fits the detector and contribution method. The evaluate step scores and flags rows. The report and export steps write the output files.
The detector is fit on the calibration window only. The scaler is fit on the calibration window only. The threshold comes from calibration scores, not from evaluation labels. All features use past and current rows only.
Each feature at row t uses values from row t or earlier rows.
Rolling means and standard deviations run on a shifted series, so a score at
t could have been produced live at time t.
The calibration window may carry labelled anomalies. By default the lab drops those rows before it fits the detector. This step uses training-time labels only. It never reads the evaluation window.
Use a virtual environment.
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS and Linux
source .venv/bin/activate
pip install -e ".[dev]"Pin a set of dependencies when you need one.
pip freeze > requirements.txtRun the built-in experiment.
tiny-anomaly-lab --output outputOr call the package as a module.
python -m tiny_anomaly_lab --output outputThe command writes output/report.html, output/model_card.md,
output/scores.csv and output/flagged_rows.csv. Open the report in a web
browser. No server and no network are needed.
Three detectors ship with the lab.
isolation_forestwraps the scikit-learn Isolation Forest.zscore_rulescores the largest standardised residual.mahalanobisscores the correlation-aware Mahalanobis distance.
Pick the detector with a flag.
tiny-anomaly-lab --detector mahalanobis --output outputEach detector can attribute its row score to the input features. The lab uses
this in two places: a contribution panel in the report and a top_feature
column in the flagged-rows CSV.
zscore_rule: contribution is the absolute standardised residual. The largest value marks the feature that drove the score.mahalanobis: contribution is a signed exact split of the squared distance. Row contributions sum to the squared score. A negative value shows a feature that offsets the distance through correlation.isolation_forest: no additive decomposition ships with the lab. The report omits the contribution panel for this detector.
Two CSV files sit next to the report.
scores.csvholds every feature-frame row with its score, flag and acontrib_column per feature.flagged_rows.csvholds the flagged evaluation rows with the raw value columns, the score, the threshold and the dominant contributor.
| Flag | Default | Meaning |
|---|---|---|
--detector |
isolation_forest |
Detector name. |
--calibration-fraction |
0.5 |
Share of rows used for calibration. |
--contamination |
0.05 |
Expected anomaly share. |
--window |
6 |
Rolling window in hours. |
--lags |
1 24 |
Lag offsets in hours. |
--random-state |
7 |
Seed for the detector. |
--source-csv |
builtin | Optional CSV path. |
--output |
output |
Directory for the artifacts. |
The file tiny_anomaly_lab/data/sensor_sample.csv holds 336 hourly rows. It
spans 14 days of synthetic sensor data. It injects four anomaly types: an
isolated spike, a six-hour level shift, a four-hour flatline and a variance
burst. Seventeen rows carry the anomaly label.
Regenerate the file with a fixed seed.
python scripts/build_dataset.pyThis output comes from the default run on the bundled data.
Detector: isolation_forest
ROC AUC: 0.7082 Average precision: 0.3970
Flagged 9 rows in evaluation (TP=3, FP=6).
Report: output\report.html
Model card: output\model_card.md
Scores: output\scores.csv
Flagged rows: output\flagged_rows.csv
This output comes from the z-score rule run.
Detector: zscore_rule
ROC AUC: 0.7151 Average precision: 0.5175
Flagged 4 rows in evaluation (TP=3, FP=1).
This output comes from the Mahalanobis run.
Detector: mahalanobis
ROC AUC: 0.6938 Average precision: 0.4756
Flagged 6 rows in evaluation (TP=3, FP=3).
A committed sample lives under examples/sample_run/. Open
examples/sample_run/report.html to view the interactive figure. The
examples/sample_run_mahalanobis/ folder holds the same artifacts for the
Mahalanobis detector, including a contribution panel and signed contributions
in flagged_rows.csv.
The model card records the detector, the data windows, the leakage controls, the metrics, the per-feature contribution method, the intended use and the limitations.
Run the deterministic test suite.
python -m pytest -qRun the linter.
ruff check .The suite covers data loading, causal features, the split, the metrics, the detectors, the per-feature contributions, the CSV export, the experiment, the model card and the report.
The bundled dataset is synthetic and small. Results are illustrative. Isolation Forest scores shift when the random state or the scikit-learn version changes. Causal rolling features lag true change points by the window length. The detector assumes the calibration window is close to normal operation. The Mahalanobis covariance uses a shrinkage estimate that trades bias for stability on small windows, and its signed contributions can hide a feature behind a correlated partner.
tiny_anomaly_lab/ Source package.
data/ Bundled CSV.
tests/ Deterministic test suite.
scripts/ Dataset builder.
examples/ Committed sample artifacts.
.github/workflows/ Continuous integration.
Later releases stay independent of this one.
Done in this release:
- Add a multivariate detector beside the two current scorers.
- Add local feature contribution for the z-score rule and the new detector.
- Add CSV export of the flagged rows and the scores.
Still open:
- Add a stream evaluation that replays rows one at a time.
- Add a container image for a fully reproducible run.
MIT.