kNN-based outlier detection by distance to the k-th nearest neighbour.
Classes
Score outliers by their distance to the k nearest neighbours.
ECODDetector and HBOSDetector are blind.__init__( self, n_neighbors: int = 5, method: str = 'largest', metric: str = 'euclidean', contamination: float = 0.1, )
Overview
- Index the training data for nearest-neighbour search.
-
For each point, find its
knearest training neighbours. -
Reduce those
kdistances to a single score withmethod. - Larger distance means more anomalous.
Theory
With d_{(1)} \leq \dots \leq d_{(k)} the sorted distances from x to its k nearest neighbours, the three reductions are
'largest' is the classic formulation and reacts fastest to a single isolated point; 'mean' and 'median' are steadier when the data has small tight clusters that 'largest' would flag wholesale.
The method measures global distance, so it assumes one roughly uniform density scale. Where density varies across regions — a sparse cluster that is perfectly normal for its neighbourhood — a global radius mislabels the whole sparse region, and LocalOutlierFactorDetector, which normalises by local density, is the correct tool.
Parameters
n_neighbors
k. Set this larger than the biggest group of anomalies you expect. Anomalies arriving in a tight group of more than k points become each other's nearest neighbours, so their distances look small and the group masks itself; once k exceeds the group size the neighbourhood reaches back to the inliers and the score recovers. Smaller values react faster to genuinely isolated points.
method
k distances are reduced to one score.
metric
contamination
Attributes
X_train_
threshold_
n_features_in_
fit.
Notes
Complexity. Fitting only stores the data. Scoring is O(m n d) by brute force, which is the dominant cost and the method's real limit — the pairwise distance loop runs in the shared C++ kernel tuiml._cpp_ext.distance, but the quadratic term remains. Memory is O(n d).
When to use. Use kNN when anomalies are defined by position in the joint space and the dataset is small enough for a quadratic scan — up to roughly 10^4 points. Features must be scaled first: an unscaled feature with a large range dominates the distance and the detector silently becomes univariate. Above that size, or in high dimension where distances concentrate, prefer IsolationForestDetector or the per-feature detectors.
References
See Also
>>> import numpy as np
>>> from tuiml.algorithms.anomaly import KNNDetector
>>> rng = np.random.default_rng(0)
>>> X = np.vstack([rng.normal(0, 1, (200, 2)), rng.normal(7, 0.5, (10, 2))])
>>> detector = KNNDetector(n_neighbors=15, contamination=0.05).fit(X)
>>> int((detector.predict(X)[-10:] == -1).sum())
10
Note n_neighbors=15 against a group of 10 anomalies. Dropping to
n_neighbors=5 lets the group mask itself and finds only 2 of them:
>>> masked = KNNDetector(n_neighbors=5, contamination=0.05).fit(X)
>>> int((masked.predict(X)[-10:] == -1).sum())
2
Methods
fit
(self, X: np.ndarray, _y: Optional[np.ndarray]=None) -> 'KNNDetector'
fit
(self, X: np.ndarray, _y: Optional[np.ndarray]=None) -> 'KNNDetector'
Fit the kNN detector.
Parameters
X
_y
Returns
self