TAI BUI
Back to curriculum
BuildPython

Anomaly Detection

Normal is easy to define. Abnormal is whatever doesn't fit.

TIME: ~75 minutesPREREQUISITES: Phase 2, Lessons 01-09

Learning Objectives

  • Implement Z-score, IQR, and Isolation Forest anomaly detection methods from scratch
  • Distinguish between point, contextual, and collective anomalies and select the appropriate detection method for each
  • Explain why anomaly detection is framed as modeling normal data rather than classifying anomalies
  • Compare unsupervised anomaly detection with supervised classification and evaluate the tradeoff between novel anomaly coverage and precision

The Problem

A credit card gets swiped in New York at 2pm, then in Tokyo at 2:05pm. A factory sensor reads 150 degrees against a normal range of 80-120. A server takes 50,000 requests per second when the daily average is 200.

Those are anomalies, and finding them matters: fraud costs billions, equipment failures cost downtime, and network intrusions cost data.

The problem is you rarely have labeled examples to learn from. Fraud is maybe 0.1% of transactions. Equipment failures happen a handful of times a year. There's almost nothing in the "anomaly" class to train a classifier on. And even the labels you do have won't cover what's coming: tomorrow's fraud scheme doesn't look like today's, so a classifier trained on old fraud misses the new kind entirely.

Anomaly detection flips the problem around. Instead of learning what's abnormal, you learn what's normal, then flag anything that deviates from it. No labels required, no assumption you've already seen every failure mode, and it scales to datasets too large for a human to eyeball.

Warm-up0/3
1. A feature has a standard deviation of 0 across a dataset. What does that tell you about the feature?Warm-up
A feature column whose six values are hidden, with an arrow to the result of np.std on it: 0.0.
The six values are hidden on purpose.
2. Why is the median less affected by one extreme value than the mean is?Warm-up
Two rows of five values, 12 14 15 17 18 and 12 14 15 17 180, each with its mean and median printed.
Typed example values, statistics computed.
3. Which of these is an unsupervised learning task?Warm-up
A small table of three rows with columns size and rooms and a third column called answer filled with question marks.
Illustrative numbers.
Check: The Problem0/1
1. Why is anomaly detection typically framed as an unsupervised problem rather than classification?Explain
Two rows of three boxes. Classification: features plus a label, a classifier, a class. Anomaly detection: features with no label, a detector, a deviation score.

The Concept

Types of Anomalies

An anomaly is a data point, or a sequence of them, that deviates enough from the expected pattern of normal data to be worth a second look. Not every anomaly deviates the same way, though, and the type determines which method actually finds it:

  • Point anomalies. A single data point that is unusual regardless of context. A temperature reading of 500 degrees. A transaction of $50,000 from an account that normally spends $50.
  • Contextual anomalies. A data point that is unusual given its context. A temperature of 90 degrees is normal in summer, anomalous in winter. Same value, different context.
  • Collective anomalies. A sequence of data points that is unusual as a group, even though each individual point might be normal. Five login failures is normal. Fifty in a row is a brute-force attack.

Most methods detect point anomalies well. Contextual anomalies need time or location features layered in. Collective anomalies need methods built for sequences, not single points.

Mermaid diagram

The Unsupervised Framing

In standard classification you have labeled examples of both classes. In anomaly detection you're usually in one of three situations:

  1. Fully unsupervised. No labels at all. Fit the detector on all the data and hope anomalies are rare enough that they don't corrupt your model of "normal."
  2. Semi-supervised. You have a clean dataset of normal data only. Fit on that clean set, then score everything else. This is the strongest setup when you can get it.
  3. Weakly supervised. You have a few labeled anomalies. Use them to evaluate, not to train: fit unsupervised, then measure precision and recall against the labeled subset.

The key difference from classification is what you're modeling: not a decision boundary between two classes, but the shape of the normal distribution, with deviation from it standing in for suspicion.

Supervised vs Unsupervised: The Tradeoff

If you do have labeled anomalies, the real question is whether to train on them (supervised classification) or hold them back for evaluation only (unsupervised detection).

Supervised (treat as classification):

  • Catches the exact types of anomalies you have seen before
  • Higher precision on known anomaly types
  • Misses novel anomaly types entirely
  • Requires retraining when new anomaly types emerge
  • Needs enough anomaly examples (often too few)

Unsupervised (model normal, flag deviations):

  • Catches any deviation from normal, including novel types
  • Does not require labeled anomalies
  • Higher false positive rate (not everything unusual is bad)
  • More robust to distribution shift

In practice, the best systems combine both: unsupervised detection for broad coverage, supervised models for known high-priority anomaly types, and human review for the ambiguous cases in between.

Z-Score Method

The simplest approach. Compute the mean and standard deviation of each feature. Flag any point more than k standard deviations from the mean.

text
z_score = (x - mean) / std
anomaly if |z_score| > threshold

The default threshold is 3.0 (99.7% of normal data falls within 3 standard deviations for a Gaussian distribution).

Strengths: Simple. Fast. Interpretable ("this value is 4.5 standard deviations from normal").

Weaknesses: Assumes data is normally distributed. Sensitive to outliers in the training data (the outliers shift the mean and inflate the std, making them harder to detect). Fails on multimodal distributions.

When it works well: Single-feature monitoring where data is roughly bell-shaped. Server response times, manufacturing tolerances, sensor readings with stable baselines.

When it fails: Multi-cluster data (two office locations with different baseline temperatures), skewed data (transaction amounts where $1000 is rare but not anomalous), data with outliers in the training set.

IQR Method

More robust than Z-score. Uses the interquartile range instead of mean and standard deviation.

Q1 = 25th percentile
Q3 = 75th percentile
IQR = Q3 - Q1
lower_bound = Q1 - factor * IQR
upper_bound = Q3 + factor * IQR
anomaly if x < lower_bound or x > upper_bound

The default factor is 1.5.

Strengths: Robust to outliers (percentiles are not affected by extreme values). Works on skewed distributions. No normality assumption.

Weaknesses: Univariate only (applies per feature independently). Cannot detect anomalies that are unusual only when features are considered together (a point might be normal in each feature individually but anomalous in the joint space).

Practical note: The 1.5 factor in IQR corresponds to the whiskers in a box plot. Points outside the whiskers are potential outliers. Using 3.0 instead of 1.5 makes the detector more conservative (fewer flags, fewer false positives). The right factor depends on your tolerance for false alarms.

Isolation Forest

The key insight: anomalies are few and different. In a random partitioning of the data, anomalies isolate fast because they need fewer random splits to get separated from the rest.

Mermaid diagram

How it works:

  1. Build many random trees (an isolation forest)
  2. At each node, pick a random feature and a random split value between the feature's min and max
  3. Keep splitting until every point is isolated (in its own leaf)
  4. Anomalies have shorter average path lengths across all trees

Why it works: Normal points live in dense regions. Many random splits are needed to isolate one from its neighbors. Anomalies live in sparse regions. One or two random splits are enough to isolate them.

The anomaly score is based on the average path length across all trees, normalized by the expected path length of a random binary search tree:

score(x) = 2^(-average_path_length(x) / c(n))

Where c(n) is the expected path length for n samples. Score near 1 means anomaly. Score near 0.5 means normal. Score near 0 means very normal (deep in dense clusters).

Strengths: No distribution assumptions. Works in high dimensions. Scales well (sublinear in sample size because each tree uses a subsample). Handles mixed feature types.

Weaknesses: Struggles with anomalies in dense regions (masking effect). Random splitting is less effective when many features are irrelevant.

Key hyperparameters:

  • n_estimators: Number of trees. 100 is usually enough. More trees give more stable scores but slower computation.
  • max_samples: Number of samples per tree. 256 is the default in the original paper. Smaller values make individual trees less accurate but increase diversity. The subsampling is what makes Isolation Forest fast: each tree sees only a small fraction of the data.
  • contamination: Expected fraction of anomalies. Used only for setting the threshold. Does not affect the scores themselves.

Local Outlier Factor (LOF)

LOF compares the local density around a point to the density around its neighbors. A point in a sparse region surrounded by dense regions is anomalous.

How it works:

  1. For each point, find its k nearest neighbors
  2. Compute the local reachability density (how dense is the neighborhood)
  3. Compare each point's density to its neighbors' densities
  4. If a point has much lower density than its neighbors, it is an outlier

LOF score:

  • LOF close to 1.0 means similar density as neighbors (normal)
  • LOF greater than 1.0 means lower density than neighbors (potentially anomalous)
  • LOF much greater than 1.0 (e.g., 2.0+) means significantly lower density (likely anomaly)

The "local" part is critical. Consider a dataset with two clusters: a dense cluster of 1000 points and a sparse cluster of 50 points. A point on the edge of the sparse cluster isn't globally unusual — it has 50 neighbors. But it is locally unusual if its immediate neighbors are denser than it is. LOF captures this nuance that global methods miss.

Strengths: Detects local anomalies (points that are unusual in their neighborhood, even if they are not globally unusual). Works on clusters of different densities.

Weaknesses: Slow on large datasets (O(n^2) for naive implementation). Sensitive to the choice of k. Does not work well in very high dimensions (curse of dimensionality affects distance calculations).

Comparison

MethodAssumptionsSpeedHandles High DimsDetects Local Anomalies
Z-scoreNormal distributionVery fastYes (per feature)No
IQRNone (per feature)Very fastYes (per feature)No
Isolation ForestNoneFastYesPartially
LOFDistance is meaningfulSlowPoorlyYes

Evaluation Challenges

Evaluating anomaly detectors is harder than evaluating classifiers:

  • Extreme class imbalance. With 0.1% anomalies, predicting "normal" for everything gives 99.9% accuracy. Accuracy is useless.
  • AUROC is misleading. With heavy imbalance, AUROC can look good even when the model misses most anomalies at practical thresholds.
  • Better metrics: Precision@k (of the top k flagged items, how many are real anomalies), AUPRC (area under precision-recall curve), and recall at a fixed false positive rate.

Mermaid diagram

Anomaly Detection Pipeline

In practice, anomaly detection follows this workflow:

  1. Collect baseline data. Ideally, a period where you know there are no (or very few) anomalies.
  2. Feature engineering. Raw features plus derived features (rolling statistics, time features, ratios).
  3. Train the detector. Fit on the baseline data. The model learns what "normal" looks like.
  4. Score new data. Each new observation gets an anomaly score.
  5. Threshold selection. Choose the score cutoff. This is a business decision: higher threshold means fewer false alarms but more missed anomalies.
  6. Alert and investigate. Flagged points go to human review or automated response.
  7. Feedback collection. Record whether flagged items were true anomalies or false alarms. Use this data to evaluate the detector and tune the threshold over time.

The pipeline is never "done." Data distributions shift, new anomaly types emerge, and thresholds need adjustment. Treat anomaly detection as a living system, not a one-time model.

Check: The Concept0/29
1. What is an anomaly?Define
Scatter of about 70 points along a rising diagonal band with one ringed point lying well off the band, though inside the plotted range of both features.
Illustrative 2-D data. The ringed point is one record.
2. What is a point anomaly?Define
Line plot of 40 sensor readings staying inside a shaded 80 to 120 band, with one reading at 150 ringed above it.
Generated readings around the lesson's 80 to 120 range.
3. A temperature of 90F is normal in summer but anomalous in winter. What type of anomaly is this?Define
Shaded band showing a typical monthly temperature range, low in January and high in July, with a 90F reading plotted in January and another in July.
Illustrative seasonal range, not real weather data.
4. Fifty failed login attempts in a row is unusual, even though a few failed logins individually would be normal. What type of anomaly is this?Define
A row of 70 login attempts in time order: 20 mostly successful ones with two isolated failures, followed by an unbroken run of 50 failures.
Illustrative log.
5. What is a Z-score?Define
Histogram of one feature with a dashed line at its mean and a marked value x further right, labelled Z-score of x equals question mark.
Generated bell-shaped feature, illustrative.
6. What is the IQR of a feature?Define
Twelve sorted values 4, 6, 7, 9, 10, 12, 14, 15, 17, 19, 21, 40 in a row of cells with IQR equals question mark below.
7. What is an Isolation Forest?Define
Python session fitting an IsolationForest and printing three scores 0.421, 0.539, 0.741 for points p1, p2, p3, beside a scatter of 120 training points with p1 near the middle and p3 far outside.
Captured from scikit-learn 1.9.1 on generated data. Score is minus score_samples.
8. What is the Local Outlier Factor (LOF)?Define
Scatter of a tight cluster at lower left and a looser cluster at upper right, with point C just outside the tight cluster joined by lines to its 10 nearest neighbors.
Generated 2-D data, k = 10.
9. What is Precision@k?Define
Table of eight ranked transactions with their suspicion scores, and a dashed line below rank 5 labelled k = 5.
Typed, illustrative scores.
10. What is AUPRC?Define
Table of five score cutoffs with how many rows each flags and how many of the 20 anomalies each catches, and the summary number AUPRC = 0.81.
Captured from scikit-learn on generated data; counts derived from the captured run; cutoffs are examples.
11. Why do contextual anomalies need time or location features added before most methods can find them?Explain
Table of six temperature readings with columns temp_F and month, including a 90 in July and a 90 in January.
12. In the weakly supervised setup, why are the few labeled anomalies used to evaluate rather than to train?Explain
Two sources on the left, many unlabeled rows and a few rows known to be anomalies, with arrows to two steps on the right: fit the detector, and measure precision and recall.
13. You build both an unsupervised anomaly detector and a supervised fraud classifier. When should you prefer the unsupervised approach?Explain
Two cards. Unsupervised detector: input is transactions without fraud labels, output is a deviation score. Supervised classifier: input is transactions marked fraud or not fraud, output is a fraud prediction.
14. Which cost comes with choosing unsupervised detection over a supervised classifier?Explain
Table of the five highest-scoring flags from a detector, with rank and score filled in and the reviewer verdict column showing question marks.
Scores captured from a scikit-learn run on generated data; the verdicts are unknown here.
15. The Z-score method flags points more than 3 standard deviations from the mean. When does this approach fail?Explain
Bell curve with the region between minus 3 and plus 3 standard deviations shaded as not flagged, and the tails beyond it marked flagged.
Rule from the lesson drawn on a Gaussian shape.
16. How does the IQR method flag anomalies, and why is it more robust than Z-score to outliers in the training data?Explain
Strip of 43 feature values with a box from Q1 = 47.3 to Q3 = 54.5 and a median line, and three values lying well to the right.
Generated data; quartiles computed.
17. How does Isolation Forest detect anomalies differently from distance-based methods?Explain
Scatter of a round cluster of 150 points with two labelled points: A inside the cluster and B outside it at upper right.
Generated 2-D data.
18. Why do normal points need more random splits to isolate than anomalies do?Explain
Number line with only min = 10 and max = 40 marked, the 30 values themselves not drawn.
Typed, illustrative.
19. What does a Local Outlier Factor (LOF) score much greater than 1.0 indicate about a point?Explain
Number line from 0 to 4 with three points: A at 0.99, B at 0.92 and C at 3.84.
Captured from scikit-learn LocalOutlierFactor (n_neighbors=10) on generated data.
20. Why does LOF compare a point's density with its neighbors' instead of with the whole dataset?Explain
Scatter with a tight cluster at lower left and a looser cluster at upper right. Point C, ringed, lies a little outside the tight cluster.
Generated 2-D data.
21. Why does the lesson recommend Precision@k and AUPRC over plain accuracy or AUROC for evaluating anomaly detectors?Explain
Four boxes named accuracy, AUROC, Precision@k and AUPRC, each with a question mark for its value.
Metric names from the question.
22. In the lesson's anomaly detection pipeline, what happens in the feedback collection step, and why does it matter?Explain
Seven numbered pipeline boxes in two rows: baseline data, feature engineering, train detector, score new data, threshold selection, alert and investigate, feedback collection, the last highlighted.
23. A detector flags 100 transactions, and 40 of them turn out to be real fraud. What is its Precision@100?Apply
Three boxes: 100 flagged transactions, 40 of them real fraud, and Precision@100 equals question mark.
24. A Z-score detector was fit on data containing a few huge outliers. Afterward it misses points that are clearly extreme. Why?Analyze
Histogram of a feature with a main cluster near 0 and four huge values around 26 to 32, and a new point at 8 marked to the right of the cluster.
Generated data, illustrative.
25. Temperatures come from two offices with different baselines, and Z-score misses readings that sit between the two clusters. What is the cause?Analyze
Number line of readings: a cluster of 40 near 15 for office 1, a cluster of 40 near 30 for office 2, and three red squares between them.
Generated, illustrative values.
26. Each of a point's features lies inside the IQR bounds, yet the point is far off the main cluster's diagonal. Why does IQR miss it?Analyze
Scatter along a rising diagonal with tick marks on both axes showing each feature's values, and a ringed point Q off the diagonal whose dashed projections land inside both ranges.
Generated 2-D data.
27. Isolation Forest finds few of the anomalies in data with 50 features where anomalies differ in only 5. What is the likely cause?Analyze
Grid of 13 rows by 50 feature columns, with orange cells marking values far from usual: five columns are orange in the bottom three rows and a few cells elsewhere.
Generated data: rows 11 to 13 are shifted in five of the 50 features.
28. You have months of verified-clean pump data and six labeled failures. How do you use them to build a detector?Design
A wide outlined block for months of verified-clean pump readings, six red squares for six labeled failures, and the line detector to build question mark.
29. A fraud system must catch fraud schemes never seen before and also never miss one known high-priority scheme. Which design fits?Design
Two requirement boxes, catching schemes never seen before and never missing one known high-priority scheme, above a box reading Design: question mark.

Build It

The code in code/anomaly_detection.py implements Z-score, IQR, and Isolation Forest from scratch.

Z-Score Detector

python
def zscore_detect(X, threshold=3.0):
    mean = X.mean(axis=0)
    std = X.std(axis=0)
    std[std == 0] = 1.0
    z = np.abs((X - mean) / std)
    return z.max(axis=1) > threshold

Simple and vectorized. Flags a point if any feature exceeds the threshold.

IQR Detector

python
def iqr_detect(X, factor=1.5):
    q1 = np.percentile(X, 25, axis=0)
    q3 = np.percentile(X, 75, axis=0)
    iqr = q3 - q1
    iqr[iqr == 0] = 1.0
    lower = q1 - factor * iqr
    upper = q3 + factor * iqr
    outside = (X < lower) | (X > upper)
    return outside.any(axis=1)

Isolation Forest from Scratch

The from-scratch implementation builds isolation trees that randomly partition the feature space:

python
class IsolationTree:
    def __init__(self, max_depth):
        self.max_depth = max_depth
 
    def fit(self, X, depth=0):
        n, p = X.shape
        if depth >= self.max_depth or n <= 1:
            self.is_leaf = True
            self.size = n
            return self
        self.is_leaf = False
        self.feature = np.random.randint(p)
        x_min = X[:, self.feature].min()
        x_max = X[:, self.feature].max()
        if x_min == x_max:
            self.is_leaf = True
            self.size = n
            return self
        self.threshold = np.random.uniform(x_min, x_max)
        left_mask = X[:, self.feature] < self.threshold
        self.left = IsolationTree(self.max_depth).fit(X[left_mask], depth + 1)
        self.right = IsolationTree(self.max_depth).fit(X[~left_mask], depth + 1)
        return self

The path length to isolate a point determines its anomaly score. Shorter paths mean more anomalous.

The IsolationForest class wraps multiple trees:

python
class IsolationForest:
    def __init__(self, n_estimators=100, max_samples=256, seed=42):
        self.n_estimators = n_estimators
        self.max_samples = max_samples
 
    def fit(self, X):
        sample_size = min(self.max_samples, X.shape[0])
        max_depth = int(np.ceil(np.log2(sample_size)))
        for _ in range(self.n_estimators):
            idx = rng.choice(X.shape[0], size=sample_size, replace=False)
            tree = IsolationTree(max_depth=max_depth)
            tree.fit(X[idx])
            self.trees.append(tree)
 
    def anomaly_score(self, X):
        avg_path = average path length across all trees
        scores = 2.0 ** (-avg_path / c(max_samples))
        return scores

The normalization factor c(n) is the expected path length of an unsuccessful search in a binary search tree with n elements. It equals 2 * H(n-1) - 2*(n-1)/n where H is the harmonic number. This normalization ensures scores are comparable across datasets of different sizes.

Demo Scenarios

The code generates multiple test scenarios:

  1. Single cluster with outliers. A 2D Gaussian cluster with anomalies injected far from the center. All methods should work here.
  2. Multimodal data. Three clusters of different sizes and densities. Points between clusters are anomalous. Z-score struggles because the per-feature ranges are wide.
  3. High-dimensional data. 50 features, but anomalies differ in only 5 of them. Tests whether methods can find anomalies in a subset of features.

Each demo compares all methods using precision, recall, F1, and Precision@k.

Check: Build It0/5
1. A point has z-scores of 1.2 on feature A and 3.4 on feature B, and zscore_detect uses threshold=3.0. What happens to the point?Apply
Code of zscore_detect with its final return line replaced by a placeholder, and two bars of |z| for one point: feature A 1.2 and feature B 3.4, with a dashed line at the threshold 3.0.
Return line hidden here, shown in the explanation.
2. In iqr_detect with factor=1.5, a feature has Q1 = 10 and Q3 = 20. Which value is flagged?Apply
Number line from -10 to 50 with a box from Q1 = 10 to Q3 = 20, and the call iqr_detect with factor 1.5.
3. With Q1 = 10 and Q3 = 20, the value 40 is flagged at factor=1.5. What happens to it at factor=3.0?Apply
Number line with a box from Q1 = 10 to Q3 = 20, a dashed upper bound at 35 for factor 1.5, and the value 40 beyond it.
4. IsolationForest.fit runs with max_samples=256 on a dataset of 100 rows. What max_depth does each tree get?Apply
Chain of boxes: X with 100 rows, max_samples equals 256, then sample_size and max_depth both question marks.
5. In the isolation score 2^(-avg_path / c(n)), a point's average path length equals c(n). What is its score?Apply
The isolation score formula above two bars of equal height, one for c(n) and one for the average path length of x, and score equals question mark.

Use It

With sklearn (using library implementations, not from-scratch):

python
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
 
iso = IsolationForest(n_estimators=100, contamination=0.05, random_state=42)
iso.fit(X_train)
predictions = iso.predict(X_test)
 
lof = LocalOutlierFactor(n_neighbors=20, contamination=0.05, novelty=True)
lof.fit(X_train)
predictions = lof.predict(X_test)

Note contamination sets the expected fraction of anomalies. Setting it correctly matters: set it too low and you miss anomalies, set it too high and you create false alarms.

The code in anomaly_detection.py compares from-scratch implementations against sklearn on the same data.

sklearn Contamination Parameter

The contamination parameter in sklearn determines the threshold for converting continuous anomaly scores into binary predictions. It does not change the underlying scores.

python
iso_5 = IsolationForest(contamination=0.05)
iso_10 = IsolationForest(contamination=0.10)

Both produce the same anomaly scores. But iso_5 flags the top 5% while iso_10 flags the top 10%. If you do not know the true anomaly rate (you usually do not), set contamination to "auto" and work with the raw scores directly. Set your own threshold based on the cost tradeoff between false positives and false negatives.

One-Class SVM

Another unsupervised anomaly detector worth knowing. One-Class SVM fits a boundary around normal data in a high-dimensional feature space (using the kernel trick).

python
from sklearn.svm import OneClassSVM
 
oc_svm = OneClassSVM(kernel="rbf", gamma="auto", nu=0.05)
oc_svm.fit(X_train)
predictions = oc_svm.predict(X_test)

The nu parameter approximates the fraction of anomalies. One-Class SVM works well on small to medium datasets but does not scale to very large data (the kernel matrix grows quadratically).

Autoencoder Approach (Preview)

Autoencoders are neural networks that learn to compress and reconstruct data. Train on normal data. At test time, anomalies have high reconstruction error because the network learned to reconstruct normal patterns only.

This is covered in Phase 3 (Deep Learning), but the principle is the same: model what is normal, flag what deviates.

Ensemble Anomaly Detection

Just as ensemble methods improve classification (Lesson 11), combining multiple anomaly detectors improves detection. The simplest approach:

  1. Run multiple detectors (Z-score, IQR, Isolation Forest, LOF)
  2. Normalize each detector's scores to [0, 1]
  3. Average the normalized scores
  4. Flag points above the threshold on the average score

This reduces false positives because different methods have different failure modes. A point flagged by all four methods is almost certainly anomalous. A point flagged by only one might be a quirk of that method.

More sophisticated ensembles weight each detector by its estimated reliability (measured on a validation set with known anomalies, if available).

Production Considerations

  1. Threshold drift. As data distribution shifts, a fixed threshold becomes outdated. Monitor the distribution of anomaly scores and adjust periodically.
  2. Alert fatigue. Too many false alarms and operators stop paying attention. Start with a high threshold (fewer, more reliable alerts) and lower it as trust builds.
  3. Ensemble approach. In production, combine multiple detectors. Flag a point only if multiple methods agree it is anomalous. This reduces false positives significantly.
  4. Feature engineering. Raw features are rarely enough. Add rolling statistics, ratios, time-since-last-event, and domain-specific features. A good feature set matters more than the choice of detector.
  5. Feedback loop. When operators investigate flagged items and confirm or dismiss them, feed this back into the system. Accumulate labeled data over time to evaluate and improve the detector.
Check: Use It0/6
1. What does the contamination hyperparameter specify?Define
Python session constructing IsolationForest with contamination=0.05, fitting it, and predicting on ten test points to get an array of 1 and -1 values.
Code from the lesson's Use It section. Predictions captured from scikit-learn on generated data (1 normal, -1 flagged).
2. What does One-Class SVM do, and what limits its use on very large datasets?Define
Python session creating OneClassSVM with an rbf kernel, gamma auto and nu 0.05, and fitting it, above a scatter of 140 training points in two blobs.
Code from the lesson. Points are generated.
3. Why can sklearn's contamination parameter be changed without changing the anomaly scores?Explain
Python session creating two IsolationForest objects, one with contamination 0.05 and one with 0.10, both fit on the same training data.
Code from the lesson.
4. Why does combining multiple anomaly detectors (Z-score, IQR, Isolation Forest, LOF) into an ensemble reduce false positives?Explain
Four detectors, Z-score, IQR, Isolation Forest and LOF, feeding a normalize step and then an average, after which points are flagged.
Steps from the lesson's ensemble recipe.
5. Two IsolationForest models are fit on the same 1,000 training points, one with contamination=0.05 and one with 0.10. What do they produce?Apply
Python session fitting two IsolationForest models with contamination 0.05 and 0.10 on the same X_train of length 1000.
Typed to match the question.
6. Analysts have begun to ignore the detector's alerts after a flood of false alarms. Which change fits the lesson's advice?Analyze
Ten days of paired bars: alerts raised climbing each day while alerts an analyst opened rise slightly and then fall toward zero.
Illustrative, typed, not measured.

Ship It

This lesson produces:

  • outputs/skill-anomaly-detector.md: a decision skill for choosing the right detector
  • code/anomaly_detection.py: Z-score, IQR, and Isolation Forest from scratch, with sklearn comparison

Choosing a Threshold

The anomaly score is continuous. You need a threshold to make binary decisions. This is a business decision, not a technical one.

Consider two scenarios:

  • Fraud detection. Missing fraud is expensive (chargebacks, customer trust). False alarms cost a human analyst 5 minutes to investigate. Set the threshold low to catch more fraud, accept more false alarms.
  • Equipment maintenance. A false alarm means an unnecessary shutdown costing $50,000. A missed failure means a $500,000 repair. Set the threshold to balance these costs.

In both cases, the optimal threshold depends on the cost ratio between false positives and false negatives. Plot precision and recall at different thresholds, overlay the cost function, and pick the minimum-cost point.

Scaling to Production

For real-time anomaly detection in production:

  1. Batch training, online scoring. Train the model periodically (daily, weekly) on recent normal data. Score each new observation as it arrives.
  2. Feature computation must match. If you trained with rolling statistics over 30 days, you need 30 days of history to compute features for a new observation. Buffer the required history.
  3. Score distribution monitoring. Track the distribution of anomaly scores over time. If the median score drifts upward, either the data is changing or the model is stale.
  4. Explainability. When you flag an anomaly, say why. Z-score: "Feature X is 4.2 standard deviations above normal." Isolation Forest: "This point was isolated in 3.1 splits on average (normal points take 8.5)."
Check: Ship It0/1
1. Fraud detection tolerates false alarms (a 5-minute review) far better than missed fraud, while equipment monitoring weighs a costly false shutdown against an even costlier missed failure. What should differ between the two systems?Design
Two panels for fraud detection and equipment monitoring, each with the cost of a false alarm and of a missed case from the question, and a question mark for what should be set differently.
Costs as stated in the question.
Author's Note

The mistake I see most often with anomaly detection isn't a bad choice of algorithm. It's skipping the conversation about what a false alarm actually costs before you pick a threshold. Isolation Forest and LOF hand you a clean, continuous score, and sklearn's contamination parameter makes it tempting to treat the default as a decision rather than a placeholder. It isn't one. Somebody in your organization already knows the real number: whoever owns the pager, the fraud queue, or the maintenance budget. Go find them before you ship a threshold, not after the false alarms start piling up and everyone stops trusting the system.

The second thing worth saying plainly: don't reach for Isolation Forest by default just because it's the fashionable answer in every tutorial. If your data is one feature and roughly bell-shaped, Z-score will outperform it on interpretability and cost you nothing to explain to a non-technical stakeholder. Save the heavier methods for problems that actually need them: multimodal data, high dimensions, local density differences that a single global threshold can't see. Match the tool to the shape of the data, not to whichever one showed up first in a benchmark paper.

Exercises

  1. Threshold tuning. Run the Z-score detector with thresholds from 1.0 to 5.0 in steps of 0.5. Plot precision and recall at each threshold. Where is the sweet spot for your data?

  2. Multivariate anomalies. Create 2D data where each feature individually looks normal, but the combination is anomalous (e.g., points far from the main cluster diagonal). Show that Z-score per feature misses these but Isolation Forest catches them.

  3. LOF from scratch. Implement Local Outlier Factor using k-nearest neighbors. Compare against sklearn's LocalOutlierFactor on the same data. Use k=10 and k=50: how does the choice of k affect the results?

  4. Streaming anomaly detection. Modify the Z-score detector to work in a streaming setting: update the running mean and variance as new points arrive (Welford's online algorithm). Compare to batch Z-score on the same data.

  5. Real-world evaluation. Take a dataset with known anomalies (credit card fraud from Kaggle, for example). Evaluate all four methods using precision@100, precision@500, and AUPRC. Which method works best? Why?

Key Terms

TermWhat people sayWhat it actually means
Anomaly"Outlier, unusual point"A data point that deviates significantly from the expected pattern of normal data
Point anomaly"A single weird value"An individual observation that is unusual regardless of context
Contextual anomaly"Normal value, wrong context"An observation that is unusual given its context (time, location, etc.) but might be normal in another context
Isolation Forest"Random splits to find outliers"An ensemble of random trees that isolates anomalies with fewer splits than normal points
Local Outlier Factor"Compare density to neighbors"A method that flags points whose local density is much lower than their neighbors' density
Z-score"Standard deviations from mean"(x - mean) / std, measuring how far a point is from the center in units of standard deviation
IQR"Interquartile range"Q3 - Q1, measuring the spread of the middle 50% of data, used for robust outlier detection
Contamination"Expected fraction of anomalies"A hyperparameter telling the detector what proportion of the data it should flag as anomalous
Precision@k"Of the top k flags, how many are real"Precision computed on only the k most suspicious points, useful for imbalanced anomaly detection
AUPRC"Area under precision-recall curve"A metric that summarizes precision-recall performance across all thresholds, better than AUROC for imbalanced data

Further Reading

Code

Ships With