Metadata-Version: 2.5
Name: aberrant
Version: 1.5.0
Summary: Real-time anomaly detection for data streams
Project-URL: Homepage, https://oliverhennhoefer.github.io/aberrant
Project-URL: Documentation, https://oliverhennhoefer.github.io/aberrant
Project-URL: Source, https://github.com/OliverHennhoefer/aberrant
Project-URL: Issues, https://github.com/OliverHennhoefer/aberrant/issues
Project-URL: Changelog, https://github.com/OliverHennhoefer/aberrant/blob/main/CHANGELOG.md
Author: Pascal Heinzelmann, Marius Hoell, Marco Catoir
Author-email: Oliver Hennhoefer <oliver.hennhoefer@mail.de>
Maintainer-email: Oliver Hennhoefer <oliver.hennhoefer@mail.de>
License: MIT License
        
        Copyright (c) 2025 Oliver Hennhöfer
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: anomaly detection,concept drift,data streams,drift detection,fraud detection,graph anomaly detection,half-space trees,iot,isolation forest,online forecasting,online learning,outlier detection,real-time,streaming,time series anomaly detection,unsupervised learning
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: filelock<5.0.0,>=4.2.7
Requires-Dist: numpy<3.0.0,>=2.2.3
Requires-Dist: scipy<2.0.0,>=1.17.1
Requires-Dist: tqdm<5.0.0,>=4.67.1
Provides-Extra: all
Requires-Dist: faiss-cpu<2.0.0,>=1.10.0; extra == 'all'
Requires-Dist: hypothesis<7.0.0,>=6.0.0; extra == 'all'
Requires-Dist: mkdocstrings-python<3.0.0,>=2.0.7; extra == 'all'
Requires-Dist: mypy<3.0.0,>=1.13.0; extra == 'all'
Requires-Dist: pip-audit==2.10.1; extra == 'all'
Requires-Dist: pre-commit<5.0.0,>=4.0.0; extra == 'all'
Requires-Dist: pygments>=2.20.0; extra == 'all'
Requires-Dist: pytest-benchmark<6.0.0,>=5.3.1; extra == 'all'
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == 'all'
Requires-Dist: pytest<10.0.0,>=8.0.0; extra == 'all'
Requires-Dist: river<1.0.0,>=0.22.0; extra == 'all'
Requires-Dist: ruff<1.0.0,>=0.8.0; extra == 'all'
Requires-Dist: scikit-learn<2.0.0,>=1.9.1; extra == 'all'
Requires-Dist: torch<3.0.0,>=2.15.0; extra == 'all'
Requires-Dist: zensical<0.1.0,>=0.0.45; extra == 'all'
Provides-Extra: benchmark
Requires-Dist: pytest-benchmark<6.0.0,>=5.3.1; extra == 'benchmark'
Requires-Dist: river<1.0.0,>=0.22.0; extra == 'benchmark'
Provides-Extra: dev
Requires-Dist: hypothesis<7.0.0,>=6.0.0; extra == 'dev'
Requires-Dist: mypy<3.0.0,>=1.13.0; extra == 'dev'
Requires-Dist: pre-commit<5.0.0,>=4.0.0; extra == 'dev'
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == 'dev'
Requires-Dist: pytest<10.0.0,>=8.0.0; extra == 'dev'
Requires-Dist: ruff<1.0.0,>=0.8.0; extra == 'dev'
Requires-Dist: scikit-learn<2.0.0,>=1.9.1; extra == 'dev'
Requires-Dist: torch<3.0.0,>=2.6.0; extra == 'dev'
Provides-Extra: dl
Requires-Dist: torch<3.0.0,>=2.6.0; extra == 'dl'
Provides-Extra: docs
Requires-Dist: mkdocstrings-python<3.0.0,>=2.0.7; extra == 'docs'
Requires-Dist: pygments>=2.20.0; extra == 'docs'
Requires-Dist: zensical<0.1.0,>=0.0.45; extra == 'docs'
Provides-Extra: eval
Requires-Dist: scikit-learn<2.0.0,>=1.9.1; extra == 'eval'
Provides-Extra: faiss
Requires-Dist: faiss-cpu<2.0.0,>=1.10.0; extra == 'faiss'
Provides-Extra: security
Requires-Dist: pip-audit==2.10.1; extra == 'security'
Description-Content-Type: text/markdown

<p align="center">
  <a href="https://oliverhennhoefer.github.io/aberrant/">
    <img src="https://raw.githubusercontent.com/OliverHennhoefer/aberrant/main/docs/img/aberrant.png" alt="aberrant" width="420">
  </a>
</p>

<h3 align="center">Real-time anomaly detection for data streams.</h3>

<p align="center">
  <a href="https://pypi.org/project/aberrant/"><img src="https://img.shields.io/pypi/v/aberrant.svg" alt="PyPI version"></a>
  <a href="https://pypi.org/project/aberrant/"><img src="https://img.shields.io/pypi/pyversions/aberrant.svg" alt="Supported Python versions"></a>
  <a href="https://github.com/OliverHennhoefer/aberrant/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/OliverHennhoefer/aberrant/ci.yml?branch=main&label=CI" alt="CI status"></a>
  <a href="https://codecov.io/gh/OliverHennhoefer/aberrant"><img src="https://codecov.io/gh/OliverHennhoefer/aberrant/graph/badge.svg" alt="Code coverage"></a>
  <a href="https://scorecard.dev/viewer/?uri=github.com/OliverHennhoefer/aberrant"><img src="https://api.scorecard.dev/projects/github.com/OliverHennhoefer/aberrant/badge" alt="OpenSSF Scorecard"></a>
  <a href="https://oliverhennhoefer.github.io/aberrant/"><img src="https://img.shields.io/github/actions/workflow/status/OliverHennhoefer/aberrant/docs.yml?branch=main&label=Documentation" alt="Documentation status"></a>
  <a href="https://github.com/OliverHennhoefer/aberrant/blob/main/LICENSE"><img src="https://img.shields.io/github/license/OliverHennhoefer/aberrant" alt="MIT license"></a>
</p>

<p align="center">
  <a href="https://oliverhennhoefer.github.io/aberrant/"><b>Documentation</b></a> ·
  <a href="https://oliverhennhoefer.github.io/aberrant/quickstart/">Quickstart</a> ·
  <a href="https://oliverhennhoefer.github.io/aberrant/user_guide/models/">Model guide</a> ·
  <a href="https://oliverhennhoefer.github.io/aberrant/user_guide/evaluation/">Evaluation</a> ·
  <a href="https://oliverhennhoefer.github.io/aberrant/api/">API reference</a> ·
  <a href="https://github.com/OliverHennhoefer/aberrant/blob/main/CHANGELOG.md">Changelog</a>
</p>

<p align="center">
  <b>Running it in production?</b>
  The <a href="https://oliverhennhoefer.github.io/aberrant/user_guide/integrations/">integration guide</a>
  shows how to build detectors from configurations.
</p>

## Quickstart

```python
# pip install "aberrant[eval]"
from itertools import islice

from aberrant.drift import ADWIN
from aberrant.evaluate import PrequentialEvaluator
from aberrant.model import QuantileThreshold
from aberrant.model.distance import SDOStream
from aberrant.stream.dataset import Dataset, load
from aberrant.transform import IncrementalPCA, StandardScaler

stream = load(Dataset.SHUTTLE).stream()
preprocess = StandardScaler() | IncrementalPCA(n_components=5, n0=100)
for event, _ in islice(stream, 512):
    preprocess.learn_one(event)

detector = SDOStream(k=128, seed=42)
evaluator = PrequentialEvaluator(
    detector,
    warmup=256,
    metrics=True,
    metric_window_size=None,  # Retain all labeled scores for full-stream metrics.
)
threshold = QuantileThreshold(quantile=0.99, window_size=256)
drift = ADWIN(max_window_size=2048)

projected_stream = ((preprocess.transform_one(event), label) for event, label in stream)
alerts = 0
for record in evaluator.iter_evaluate(projected_stream):
    score = record.score
    if score is None:  # The detector is still warming up.
        continue

    score_event = {"score": score}
    if threshold.score_one(score_event) == 1.0:
        alerts += 1
    threshold.learn_one(score_event)
    drift.update(score)

result = evaluator.result()
print(f"Average precision = {result.average_precision:.3f}")
print(f"{alerts} alerts, {drift.n_detections} score drift signals")
```

## Why aberrant

- **Broad toolbox.** Forest, density, sketch, graph, matrix-profile, SVM and autoencoder detectors.
- **Score to alert.** Drift detectors and adaptive thresholds turn scores into decisions.
- **Built for services.** `|` pipelines, bounded state, detectors built from plain configuration.
- **Measured on the stream.** Prequential evaluation, benchmark streams, separate timings.
- **Typed and extensible.** Ships `py.typed` and any protocol-conforming object joins a pipeline.

|                                                 | aberrant | river | PySAD |
| ----------------------------------------------- | :------: | :---: | :---: |
| Isolation forests and half-space trees          | ✓        | ✓     | ✓     |
| Projection sketches (LODA, RS-Hash)             | ✓        | ✓     | ✓     |
| Graph-edge streams (MIDAS, AnoEdge, ISCONNA)    | ✓        |       |       |
| Time-series discords (matrix profile, DAMP)     | ✓        |       |       |
| Drift detectors (ADWIN, KSWIN, Page-Hinkley)    | ✓        | ✓     |       |
| Score thresholds or calibration                 | ✓        | ✓     | ✓     |
| Detectors built from declarative configuration  | ✓        |       |       |
| General online ML (classification, regression)  |          | ✓     |       |

### Non-trivial detection

Many anomalies have no outlying value at all. In this ECG from the UCR Time
Series Anomaly Archive, one malformed heartbeat sits among roughly 180 normal
ones, under sensor noise and baseline wander, and every one of its values lies
inside the normal range. `XLagDAMP`, a matrix-profile discord detector, scores
the stream in a single pass from the first sample, with no training split and
bounded memory, and its highest score lands on that beat. Because the beat
differs in shape rather than in value, a detector that scores single values has
no outlier to find.

<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/OliverHennhoefer/aberrant/main/docs/img/discord-dark.svg">
    <img src="https://raw.githubusercontent.com/OliverHennhoefer/aberrant/main/docs/img/discord-light.svg" width="100%" alt="A 30,000-sample noisy ECG with 181 heartbeats, one of them anomalous. The XLagDAMP score stream has a single dominant peak on the anomalous beat. An overlay of all beats shows the anomalous beat stays within the normal value range but has a different shape.">
  </picture>
</p>

<p align="center"><sub>Series <code>013_UCR_Anomaly_DISTORTEDECG3</code> (Wu and Keogh, 2021), with <code>XLagDAMP(subsequence_length=167)</code>. Reproduce with <a href="https://github.com/OliverHennhoefer/aberrant/blob/main/docs/scripts/discord_figure.py"><code>docs/scripts/discord_figure.py</code></a>.</sub></p>

---

## Installation

`aberrant` requires Python 3.11 or newer and is continuously tested on CPython
3.11 to 3.14 on Linux and Windows.

```bash
pip install aberrant
```

<details>
<summary><strong>Optional extras</strong></summary>

| Extra | Adds |
| --- | --- |
| `eval` | scikit-learn metrics for model evaluation |
| `dl` | the PyTorch-backed `Autoencoder` |
| `faiss` | the FAISS similarity-search engine used by models such as `KNN` |
| `benchmark` | River and pytest-benchmark |
| `docs` | the documentation build toolchain |
| `dev` | linting, typing, testing, and development dependencies |
| `all` | all optional and development dependencies |

For example:

```bash
pip install "aberrant[eval,faiss]"
```

</details>

## Choose a starting point

| Goal | Start with |
| --- | --- |
| General multivariate detection | [`OnlineIsolationForest` or another isolation-forest variant](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#isolation-forests) |
| Local-neighborhood or density anomalies | [`LocalOutlierFactor`, `KNN`, `SDOStream`, or a cell-based detector](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#distance-and-neighborhood-detectors) |
| Compact projection or frequency sketches | [`StreamingLODA`, `MStream`, or `StreamingRSHash`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#sketch-detectors) |
| Anomalous edges and graph evolution | [`AnoEdgeL`, `ISCONNA`, `MIDAS`, or `SignedGraphSketchDetector`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#graph-stream-detectors) |
| Discords in a scalar time series | [`RollingMatrixProfile` or `XLagDAMP`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#time-series-discord-detection) |
| Departures from a known seasonal forecast | [`SeasonalResidualDetector`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#seasonal-forecast-residuals) |
| Changes in relationships between time-series channels | [`MultivariateRollingMatrixProfile`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#time-series-discord-detection) |
| Interpretable rolling statistics | [Univariate and multivariate moving statistics](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#statistical-detectors) |
| Adaptive margin-based detection | [Online SVM models](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#svm-detectors) |
| Learned reconstruction error | [`OnlineAutoencoderEnsemble` or the optional PyTorch `Autoencoder`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#reconstruction-detectors) |
| Detecting distribution drift | [`ADWIN`, `KSWIN`, or `PageHinkley`](https://oliverhennhoefer.github.io/aberrant/api/drift/) |
| Turning a score into an alert signal | [`QuantileThreshold` or `ThresholdModel`](https://oliverhennhoefer.github.io/aberrant/user_guide/models/#core-utility-models) |
| Misses of an online forecast or regression, at any lead | [`ResidualDetector` with `HoltWinters`, `SeasonalNaive` or `RLSRegressor`](https://oliverhennhoefer.github.io/aberrant/user_guide/forecasting/) |
| An alarm threshold that adapts online, with delayed feedback | [`ScoreCalibrator` with ACI, conformal PID, SAOCP and twelve more](https://oliverhennhoefer.github.io/aberrant/user_guide/calibration/) |

See the [model guide](https://oliverhennhoefer.github.io/aberrant/user_guide/models/)
for inputs, score interpretation, warm-up behavior, and memory characteristics.

<details>
<summary><strong>Included public model families</strong></summary>

| Family | Implementations |
| --- | --- |
| Isolation forest | `ASDIsolationForest`, `HalfSpaceTrees`, `MondrianIsolationForest`, `OnlineIsolationForest`, `RandomCutForest`, `StreamRandomHistogramForest`, `XStream` |
| Distance | `CellNeighborhoodDetector`, `KNN`, `LocalOutlierFactor`, `SDOStream`, `StationaryRegionNeighborDetector` |
| Sketch | `MStream`, `StreamingLODA`, `StreamingRSHash` |
| Graph | `AnoEdgeL`, `ISCONNA`, `MIDAS`, `SignedGraphSketchDetector` |
| Time series | `MultivariateRollingMatrixProfile`, `RollingMatrixProfile`, `SeasonalResidualDetector`, `XLagDAMP` |
| SVM | `GraphGatedOneClassSVM`, `IncrementalOneClassSVMAdaptiveKernel` |
| Statistical | `MovingAverage`, `MovingAverageAbsoluteDeviation`, `MovingGeometricAverage`, `MovingHarmonicAverage`, `MovingInterquartileRange`, `MovingKurtosis`, `MovingMedian`, `MovingQuantile`, `MovingSkewness`, `MovingVariance`, `MovingCorrelationCoefficient`, `MovingCovariance`, `MovingMahalanobisDistance`, `OnlineECOD`, `OnlineCOPOD` |
| Reconstruction | `OnlineAutoencoderEnsemble`, optional `Autoencoder` |
| Score policy | `QuantileThreshold`, `ThresholdModel` |
| Baselines | `NullModel`, `RandomModel` |
| Drift detection | `ADWIN`, `KSWIN`, `PageHinkley` |

</details>

## Pipelines and custom components

Transformers compose left to right, with at most one terminal model:

```python
from aberrant.model.iforest import OnlineIsolationForest
from aberrant.transform import IncrementalPCA, StandardScaler

detector = (
    StandardScaler()  # PCA needs features on comparable scales
    | IncrementalPCA(n_components=3, n0=100)
    | OnlineIsolationForest(window_size=512, seed=42)
)
```

Read the
[pipeline guide](https://oliverhennhoefer.github.io/aberrant/user_guide/pipelines/)
for lifecycle and composition rules.

---

<p align="center">
  <a href="https://www.dlr.de/">
    <img src="https://www.dlr.de/de/pt-lf/aktuelles/pressematerial/logos/bmwk/vorschaubild_bmwk_logo-mit-foerderzusatz_en/@@images/image-600-ea91cd9090327104991124b30fe1de7d.png" alt="Funding acknowledgement" width="250">
  </a>
</p>
