Score events. Learn continuously. Adapt to the stream.
Documentation · Quickstart · Model guide · API reference · Changelog
aberrant is a typed Python library for unsupervised anomaly detection on data
that arrives one event at a time. Its models share a compact online interface:
score_one(x) evaluates the current event and learn_one(x) updates the model.
This lets an application adapt continuously without coordinating an external
batch-retraining loop.
Most models consume a dict[str, float], while graph and time-aware models
document their required keys explicitly. Detector state, warm-up behavior,
memory policy, and score scale remain model-specific rather than being hidden
behind a batch-estimator abstraction.
Note
ABERRANT is pre-1.0 and under active development. Public APIs may change as the model contracts and implementations mature.
- Use one streaming contract across isolation forests, distance methods, sketches, graph detectors, online statistics, SVMs, time-series methods, and reconstruction models.
- Choose the right state strategy from sliding windows, bounded sketches, fading summaries, and model-specific incremental updates.
- Compose online preprocessing with detectors using
|pipelines. - Separate detection from policy with drift detectors and static or adaptive score thresholds.
- Run repeatable experiments with registry-backed benchmark streams and a validated local dataset cache.
- Extend without framework coupling through typed, structural transformer
and model protocols. The distribution includes
py.typedmetadata.
ABERRANT requires Python 3.10 or newer and is continuously tested on CPython 3.10, 3.11, and 3.12.
pip install aberrantOptional extras
| 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:
pip install "aberrant[eval,faiss]"The following core-only example learns a scaled isolation forest from a synthetic stream. The first 64 events warm up the pipeline; every later event is scored before it is learned.
import numpy as np
from aberrant.model.iforest import OnlineIsolationForest
from aberrant.transform.preprocessing import StandardScaler
rng = np.random.default_rng(42)
stream = np.vstack(
[
rng.normal(size=(400, 2)),
rng.normal(loc=5.0, size=(20, 2)),
]
)
detector = StandardScaler() | OnlineIsolationForest(
num_trees=25,
window_size=256,
seed=42,
)
scores = []
for step, values in enumerate(stream):
event = {"x": float(values[0]), "y": float(values[1])}
if step >= 64:
scores.append((step, detector.score_one(event)))
detector.learn_one(event)
for step, score in sorted(scores, key=lambda item: item[1], reverse=True)[:5]:
print(f"event={step}, anomaly_score={score:.3f}")Higher scores are more anomalous under the common model contract, but their numeric range and calibration differ by detector. Compare or threshold scores only according to the selected model's documented semantics.
| Goal | Start with |
|---|---|
| General multivariate detection | OnlineIsolationForest or another isolation-forest variant |
| Local-neighborhood or density anomalies | LocalOutlierFactor, KNN, SDOStream, or a cell-based detector |
| Compact projection or frequency sketches | StreamingLODA, MStream, or StreamingRSHash |
| Anomalous edges and graph evolution | AnoEdgeL, ISCONNA, MIDAS, or SignedGraphSketchDetector |
| Discords in a scalar time series | XLagDAMP |
| Interpretable rolling statistics | Univariate and multivariate moving statistics |
| Adaptive margin-based detection | Online SVM models |
| Learned reconstruction error | OnlineAutoencoderEnsemble or the optional PyTorch Autoencoder |
| Detecting distribution drift | ADWIN, KSWIN, or PageHinkley |
| Turning a score into an alert signal | QuantileThreshold or ThresholdModel |
See the model guide for inputs, score interpretation, warm-up behavior, and memory characteristics.
Included public model families
| 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 | XLagDAMP |
| SVM | GraphGatedOneClassSVM, IncrementalOneClassSVMAdaptiveKernel |
| Statistical | MovingAverage, MovingAverageAbsoluteDeviation, MovingGeometricAverage, MovingHarmonicAverage, MovingInterquartileRange, MovingKurtosis, MovingMedian, MovingQuantile, MovingSkewness, MovingVariance, MovingCorrelationCoefficient, MovingCovariance, MovingMahalanobisDistance |
| Reconstruction | OnlineAutoencoderEnsemble, optional Autoencoder |
| Score policy | QuantileThreshold, ThresholdModel |
| Baselines | NullModel, RandomModel |
| Drift detection | ADWIN, KSWIN, PageHinkley |
Transformers compose left to right, with at most one terminal model:
from aberrant.model.iforest import OnlineIsolationForest
from aberrant.transform import IncrementalPCA, StandardScaler
detector = (
StandardScaler()
| IncrementalPCA(n_components=3, n0=100)
| OnlineIsolationForest(window_size=512, seed=42)
)Any custom object satisfying TransformerProtocol or ModelProtocol can join
a pipeline; subclassing an ABERRANT base class is optional. Read the
pipeline guide
for lifecycle and composition rules.
The dataset API downloads, validates, and caches registered benchmark data, then exposes it as feature dictionaries and evaluation labels:
from itertools import islice
from aberrant.model.iforest import OnlineIsolationForest
from aberrant.stream.dataset import Dataset, load
dataset = load(Dataset.SHUTTLE)
detector = OnlineIsolationForest(num_trees=25, window_size=512, seed=42)
for event, label in islice(dataset.stream(), 100):
score = detector.score_one(event)
detector.learn_one(event)
print(f"label={label!r}, anomaly_score={score:.3f}")Labels are provided for evaluation; unsupervised detectors learn only from the event mapping. Cache location and download behavior are configurable through the streaming guide.
Important
For an honest prequential evaluation, call score_one(event) before
learn_one(event). Scoring does not call any component's learn_one or
incorporate the candidate into learned reference state. During
Pipeline.learn_one, each transformer first learns the event and then passes
its post-update transform to the next stage. Score scales and warm-up behavior
are model-specific, so a single numeric threshold is not portable across
detector families.
The evaluation guide covers warm-up separation, label leakage, and useful metrics for imbalanced anomaly streams.
Read the documentation, browse the examples, or report a problem in the issue tracker. Contributions are welcome; start with the contributing guide.
ABERRANT is distributed under the MIT License.