Outlier Detection: Quartiles, Distance-Based, Density-Based
Learning Objectives
- Apply the interquartile-range rule while naming the quartile method, and show that two methods flag different points on one dataset.
- Explain masking, and show when the quartile and z-score rules go blind.
- Define distance-based outliers and explain why a global distance misses local outliers.
- Compute reachability distance, local reachability density, and LOF by hand, and match
LocalOutlierFactor, including its sign convention and its handling of ties and duplicates. - Explain why LOF has no universal threshold, and decide whether an outlier is an error or the finding.
Context & Motivation
density-based-clustering-with-dbscan labels sparse points as noise, as a side effect of clustering. Outlier detection makes those points the target. A definition by Hawkins, as quoted by Breunig et al. (2000): "An outlier is an observation that deviates so much from other observations as to arouse suspicion that it was generated by a different mechanism." Every method below turns "deviates so much" into a formula, and each formula sees some deviations and misses others. Flagging a point is also only the start, because a flag does not say whether the point is a bad record or the most interesting one in the data.
Core Theory
The quartile rule, and which quartile
With first and third quartiles and , flag outside
The factor 1.5 is a convention, the default coef of R's boxplot.stats. The quartile itself has several definitions. For sorted data , three common ones are:
| Name | R type |
NumPy method |
Definition |
|---|---|---|---|
| linear | 7 (default) | linear (default) |
position , interpolated |
| Weibull | 6 | weibull |
position , interpolated |
| Tukey hinges | fivenum |
none | medians of the lower and upper halves, the median counted in both when is odd |
Hyndman and Fan (1996) compared the common definitions, noted that one package may use different ones for quantiles and for box plots, and recommended the median-unbiased estimator (type 8). R's quantile() defaults to type 7 while its box plots use hinges. These two agree for odd and differ for even . So state the method with the rule.
Masking
The rule estimates spread from the same data it screens. Masking happens when outliers distort that estimate enough to hide themselves. The quartile rule resists this up to a point: if more than about a quarter of the data sit on one side, lands among them and the fence moves past them.
The z-score rule, , is much more fragile, and small samples can make it impossible.
Property. With the sample standard deviation (divisor ), .
For the bound is , reached by nine equal values and one different, however far away. The rule with threshold 3 cannot fire for . A robust alternative replaces mean and with the median and the median absolute deviation (MAD), which resist up to about half the data.
Distance-based outliers
Knorr and Ng (1998) define distance-based outliers, and Breunig et al. restate the definition: is a -outlier if at least percentage of the objects in lie farther than from it, that is, if , which contains itself, has at most of the size of . Ramaswamy et al. (2000) instead rank points by the distance to their -th nearest neighbor and report the top ones. Both use one global scale. Breunig et al. show the failure: take a dense cluster beside a sparse one, and a point closer to the dense cluster than sparse-cluster points are to their nearest neighbors. Then no flags that point without also flagging many sparse-cluster points.
LOF
Breunig et al. compare a point's density with that of its neighbors. Fix MinPts, here , and exclude from its own neighbor counts.
- : the distance to 's -th nearest neighbor.
- : all objects other than within -distance of . With ties, can exceed .
- Reachability distance: . It smooths distances to very close neighbors.
- Local reachability density and the factor:
Property (Breunig et al., Lemma 1). Let be a set of objects, and let over . If , all -nearest neighbors of are in , and all -nearest neighbors of those are in , then . So objects deep in a tight cluster score near 1. The lemma says nothing about a point outside : the paper's Theorem 1 gives separate bounds for any object.
scikit-learn's conventions
n_neighborsis and excludes the point. DBSCAN'smin_samples, by contrast, includes it.negative_outlier_factor_is : higher means more normal.fit_predictreturns for outliers, for inliers, by comparing withoffset_.- With
contamination='auto',offset_is , so the cut is . With a float,offset_is a percentile of the scores, so you set the outlier fraction yourself. - It uses exactly neighbors, so it does not keep ties as does.
- It adds to the mean reachability distance, which avoids dividing by zero.
- With
novelty=False, the default, only training scores exist:score_samplesanddecision_functionare unavailable. Withnovelty=True, it scores new data andfit_predictis unavailable. That mode is how a threshold fitted on training data is applied to test data without leakage.
Error or finding?
A statistical flag cannot decide this. Check the record's provenance: is the value impossible (a negative age, a sentinel such as ), a unit slip, or a duplicate load? If so, it is an error, so correct or drop it and say so. If the value is possible, keep it and look for a mechanism, because it may be the finding: Breunig et al. report that their strongest soccer outlier was the league's top scorer. When unsure, report results with and without it. Example 1 shows why: dropping 57 moves the mean from to and the median from to .
Worked Examples
Example 1: same data, different flags
Take (), run in NumPy 2.5.
textmethod Q1 Q3 IQR lower upper flagged linear (type 7) 21 27 6.0 12 36 11 40 57 Tukey hinges 21 27 6.0 12 36 11 40 57 median_unbiased (8) 19.67 31.33 11.67 2.17 48.83 57 weibull (type 6) 19 33.5 14.5 -2.75 55.25 57
For type 7 the positions are 3 and 7, so and . For type 6 they are and , so and . The points 11 and 40 are outliers under one definition and normal under another. With odd , hinges equal type 7. Adding 31 makes , and they differ: for type 7 and for hinges.
Example 2: masking
Take and append high values , using type 7.
textm n Q1 Q3 upper fence IQR rule flags max z MAD rule flags 1 10 12.25 16.75 23.5 95 2.832 95 2 11 12.5 17.5 25.0 95 96 2.032 95 96 3 12 12.75 37.25 74.0 95 96 97 1.682 95 96 97 4 13 13.0 95.0 218.0 none 1.476 95 to 98 5 14 13.25 95.75 219.5 none 1.339 95 to 99
At , 31% of the data, is itself an outlier, so the rule flags nothing. The z-score rule with threshold 3 never fires, not even at , where makes it impossible. The MAD column flags , and it flags every planted value in each row.
Example 3: LOF by hand
Points on a line, with and no tied distances:
textA=0 B=1 C=3 D=4.5 E=11
textp N2(p) k-dist reach-dist to each neighbor lrd LOF A B C 3 2 (B), 3 (C) 0.4 0.9545 B A C 2 3 (A), 2 (C) 0.4 0.9545 C D B 2 3.5 (D), 2 (B) 0.3636 1.05 D C B 3.5 2 (C), 3.5 (B) 0.3636 1.05 E D C 8 6.5 (D), 8 (C) 0.1379 2.6364
Follow . Its neighbors are at 6.5 and at 8. Then and , so . Then .
Lemma 1 applies to : reachability distances run from 2 to 4.5, so and the bounds are and . All four points fit. , outside the set, scores , above the bound.
Example 4: matching scikit-learn
LocalOutlierFactor(n_neighbors=2).fit(X) gives
textnegative_outlier_factor_ [-0.9545 -0.9545 -1.05 -1.05 -2.6364] offset_ -1.5 fit_predict [ 1 1 1 1 -1]
The largest gap from the hand values is , the effect of the term. Fitted with novelty=True on to , gets score_samples and decision_function (the score minus offset_).
Ties change the result. For with , the origin has three neighbors at distance 1. An implementation of the paper's keeps all three and gives it , while scikit-learn keeps two and gives .
Example 5: duplicates
The paper assumes no duplicates. It notes that is infinite when has at least MinPts duplicates, and suggests a neighborhood based on distinct coordinates. In scikit-learn, for with n_neighbors=2:
textnegative_outlier_factor_ [-1 -1 -1 -1e+10 -1.7045 -1.925] UserWarning: Duplicate values are leading to incorrect results. ...
The copies get , purely from the term, so the point at 1 scores , and this number has no meaning. With two copies of 0 instead of three, or with n_neighbors=3, every score is finite and the point at 1 scores 1. Deduplicate or raise n_neighbors before reading LOF on data with repeats.
Example 6: global distance misses a local outlier
This dataset is not from the paper, though it follows the idea of its Figure 1. From one rng = numpy.random.default_rng(3), draw in this order rng.normal([0, 0], 1.0, (400, 2)) and rng.normal([10, 0], 0.1, (100, 2)), then add and , for 502 objects. With , the distance column excludes the point itself:
textrank of o1 rank of o2 score o1 score o2 10-NN dist 1 18 6.98 0.86 LOF 2 1 8.48 8.87
Sixteen points of the sparse cluster rank above by distance, while LOF puts it first. With , an object is flagged when at most 5 of the 502 objects, itself included, lie within . That flags at along with 34 sparse-cluster points, still flags it at along with 10, and misses it from on. Here is 0.71 from the dense cluster, farther than 99% of sparse points are from their nearest neighbor, so the paper's condition does not hold and the collateral is partial, but it never vanishes: on a grid of step 0.001, every that flags also flags at least 7 sparse points.
Example 7: no universal threshold
The paper never fixes a cut-off for LOF. It proposes ranking by the maximum LOF over a range of MinPts, with a lower bound of at least 10. Its experiments show top scores of 2.4 and 6.0 on two hockey tests, and it discusses soccer players with LOF above 1.5 as one choice for that dataset. scikit-learn's documentation says 'auto' follows the original paper, but in the paper 1.5 appears only as the cut for reporting the soccer results. On 500 points from rng = numpy.random.default_rng(5), with no outliers planted, drawn as rng.normal(0, 1, (500, 2)) and then rng.random((500, 2)):
textdata k=5 k=10 k=20 k=50 Gaussian: LOF>1.5 11 10 15 24 Gaussian: max 2.355 2.111 2.100 2.260 Uniform: LOF>1.5 3 0 0 0 Uniform: max 1.561 1.315 1.359 1.342
The same cut flags up to 4.8% of a plain Gaussian, and the count changes with . Read LOF as a ranking, and pick a cut from the cost of reviewing a flag.
Common Misconceptions & Pitfalls
- "Three sigma always catches extreme values." It cannot fire for with the sample standard deviation.
- "LOF above 1 means outlier." Values near 1 are normal, and there is no fixed cut.
- "Flagged means delete." Example 1's mean moves by 3.7 from one point. Removing it needs a reason from the data's origin.
Summary
The quartile rule depends on the quartile definition and fails once outliers on one side exceed about a quarter of the data, and the z-score rule can fail far sooner. Distance-based rules use one global scale, so they miss points that are outlying only relative to a dense neighborhood. LOF compares local densities through reachability distance, with values near 1 inside tight clusters. scikit-learn's score is , it uses exactly neighbors excluding the point, and it needs care with ties and duplicates. No threshold is universal, and only the data's origin decides between error and finding.