ABOD - Angle-Based Outlier Detection.

Classes

ABODDetector

class algorithms.anomaly.abod.ABODDetector(Classifier)

ABOD detects outliers from the variance of angles to other points.

In high dimension every pairwise distance converges to the same value — the curse of dimensionality that quietly ruins distance-based detectors. Angles do not concentrate the same way. ABOD exploits this: from a point inside a cloud, other points are scattered in every direction and the angles between them vary widely; from a point outside the cloud, all other points lie in roughly one direction and the angles barely vary. Low angle variance therefore means outlier.
Constructor
__init__(
    self,
    n_neighbors: int = 10,
    contamination: float = 0.1,
)

Overview

  1. For a point x, take its n_neighbors nearest neighbours.
  2. For every pair of those neighbours, compute the angle they subtend at
x, weighted by the inverse of the distances involved.
  1. The score is the variance of those weighted cosines.
  2. Small variance means the surrounding points are all in one direction,
which means x sits outside the cloud.

Theory

The angle-based outlier factor of x is the variance

\mathrm{ABOF}(x) = \mathrm{Var}_{y, z} \left( \frac{\langle y - x,\ z - x \rangle} {\|y - x\|^2 \ \|z - x\|^2} \right)

over pairs y, z drawn from the point's neighbourhood. The \|\cdot\|^2 weighting in the denominator makes distant pairs count less, so the measure blends angular spread with proximity rather than being purely angular.

Exact ABOD considers all pairs, at O(n^3) — unusable beyond a few hundred points. This class implements FastABOD, which restricts the pairs to each point's n_neighbors nearest neighbours, giving O(n^2 d + n k^2). The approximation is good precisely when it matters: the neighbours dominate the weighted variance anyway.

Parameters

n_neighbors
int = 10
Size of the neighbourhood whose pairs are considered. Cost grows with its square, so values beyond ~30 rarely pay for themselves.
contamination
float = 0.1
Expected proportion of outliers. Sets the decision threshold.

Attributes

X_train_
np.ndarray of shape (n_samples, n_features)
Training data retained for neighbour search.
threshold_
float
Decision-function value separating inliers from outliers.
n_features_in_
int
Number of features seen during fit.

Notes

Complexity. Scoring is O(m n d) for the neighbour search plus O(m k^2 d) for the pairwise angles. Memory is O(n d). The distance matrix is computed by the shared C++ kernel tuiml._cpp_ext.distance.

When to use. ABOD earns its cost in high dimension, where KNNDetector and LocalOutlierFactorDetector degrade as distances concentrate. In low dimension it offers little over kNN for considerably more compute. As with any geometric method, scale the features first.

Warning — clustered anomalies mask each other. ABOD assumes anomalies are isolated. When several sit together in a tight group, each one's nearest neighbours are the other anomalies, which surround it from all sides; worse, the 1/\|\cdot\|^2 weighting rewards a tight neighbourhood with a large factor. The group then scores as more normal than the genuine inliers and the ranking inverts. Measured on 300 Gaussian inliers in 50 dimensions with 15 anomalies placed at distance 6:

====================== ========== ========== anomaly cluster spread ABOD AUC kNN AUC ====================== ========== ========== 0.05 (very tight) 0.00 1.00 0.30 0.00 1.00 1.00 (as spread as 0.04 1.00 the inliers) 2.00 1.00 1.00 ====================== ========== ==========

The same effect shows up as the anomaly count grows: 1 or 3 isolated anomalies score 1.00, while 15 clustered ones score 0.19. If anomalies may arrive in bursts — a batch of fraudulent transactions, a stuck sensor emitting the same reading — use KNNDetector or ECODDetector instead, neither of which has this failure mode.

References

Kriegel2008
Kriegel, H.-P., Schubert, M., & Zimek, A. (2008). Angle-Based Outlier Detection in High-Dimensional Data. ACM SIGKDD, 444-452. :doi:`10.1145/1401890.1401946`
python
>>> import numpy as np
>>> from tuiml.algorithms.anomaly import ABODDetector
>>> rng = np.random.default_rng(0)
>>> X = np.vstack([rng.normal(0, 1, (200, 20)), rng.normal(6, 1, (3, 20))])
>>> detector = ABODDetector(n_neighbors=10, contamination=0.05).fit(X)
>>> int((detector.predict(X)[-3:] == -1).sum())  # isolated anomalies
3

Replacing those 3 isolated anomalies with a tight group of 15 inverts the

ranking entirely — see the warning above before choosing this detector.

Methods

get_parameter_schema (cls) -> Dict[str, Dict[str, Any]]

Return JSON Schema for algorithm parameters.

get_capabilities (cls) -> List[str]

Return supported capabilities.

get_complexity (cls) -> str

Return complexity analysis.

get_references (cls) -> List[str]

Return academic citations.

fit (self, X: np.ndarray, _y: Optional[np.ndarray]=None) -> 'ABODDetector'

Fit the ABOD detector.

Parameters
X
np.ndarray of shape (n_samples, n_features)
Training data. Labels are ignored; the method is unsupervised.
_y
np.ndarray
Ignored, present for API consistency.
Returns
self
ABODDetector
The fitted detector.
decision_function (self, X: np.ndarray) -> np.ndarray

Compute anomaly scores for samples.

Parameters
X
np.ndarray of shape (n_samples, n_features)
Input samples.
Returns
scores
np.ndarray of shape (n_samples,)
Anomaly scores. Lower scores indicate anomalies.
predict (self, X: np.ndarray) -> np.ndarray

Predict if samples are anomalies or not.

Parameters
X
np.ndarray of shape (n_samples, n_features)
Input samples.
Returns
predictions
np.ndarray of shape (n_samples,)
-1 for anomalies, 1 for normal instances.
score_samples (self, X: np.ndarray) -> np.ndarray

Alias for decision_function for compatibility.

Parameters
X
np.ndarray of shape (n_samples, n_features)
Input samples.
Returns
scores
np.ndarray of shape (n_samples,)
Anomaly scores. Lower scores indicate anomalies.
__repr__ (self) -> str

Return a readable representation of the detector.