Skip to content

Anomaly Detection with Isolation Forests

What anomaly detection is

Anomaly detection (also called outlier detection) finds instances that deviate strongly from the norm. The odd-ones-out are called anomalies or outliers; the regular instances are called inliers.

It shows up everywhere:

  • fraud detection on credit card transactions
  • spotting defective products on a manufacturing line
  • catching sensor failures or sudden usage spikes
  • cleaning a dataset of bad instances before training another model on it

Géron draws a useful distinction:

  • anomaly detection — the training set may already contain some outliers
  • novelty detection — the algorithm is trained only on “clean” data, so anything unlike the training set is flagged as novel

Why Isolation Forest works

Isolation Forest isolates points using random splits, the same way a Decision Tree grows: at every node it picks a random feature and a random threshold between that feature’s min and max, chopping the data in two. Keep doing this and every instance eventually ends up alone in its own region.

The trick is that anomalies are easier to isolate:

  • they sit far from other points, so a handful of random cuts is enough to wall them off
  • normal points are surrounded by neighbors, so it takes many more cuts to isolate them

Average the number of splits needed across a whole forest of these random trees, and you get an anomaly score for free — no distance metric, no density estimate.

diagram Isolation Forest mermaid
Random splits isolate far-away points in just a few steps; normal points need many more splits.

Scikit-learn example

Ten obvious outliers scattered around a tight blob of normal points — Isolation Forest should catch most of them:

isolation_forest.py
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.datasets import make_blobs
 
rng = np.random.RandomState(42)
X_inliers, _ = make_blobs(n_samples=120, centers=1, cluster_std=0.5, random_state=42)
X_outliers = rng.uniform(low=-6, high=6, size=(12, 2))
X = np.vstack([X_inliers, X_outliers])
 
iso = IsolationForest(n_estimators=200, contamination=0.1, random_state=42)
labels = iso.fit_predict(X)   # 1 = normal, -1 = anomaly
 
print("Total points:", len(X))
print("Detected anomalies:", int((labels == -1).sum()))
isolation_forest.py
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.datasets import make_blobs
 
rng = np.random.RandomState(42)
X_inliers, _ = make_blobs(n_samples=120, centers=1, cluster_std=0.5, random_state=42)
X_outliers = rng.uniform(low=-6, high=6, size=(12, 2))
X = np.vstack([X_inliers, X_outliers])
 
iso = IsolationForest(n_estimators=200, contamination=0.1, random_state=42)
labels = iso.fit_predict(X)   # 1 = normal, -1 = anomaly
 
print("Total points:", len(X))
print("Detected anomalies:", int((labels == -1).sum()))
Output
Total points: 132
Detected anomalies: 14
Output
Total points: 132
Detected anomalies: 14

Scoring anomalies

fit_predict()fit_predict() only gives you a hard yes/no. score_samples()score_samples() gives a continuous score instead — the lower the score, the more anomalous the point — so you can rank instances or pick your own cutoff:

score_samples.py
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.datasets import make_blobs
 
rng = np.random.RandomState(42)
X_inliers, _ = make_blobs(n_samples=120, centers=1, cluster_std=0.5, random_state=42)
X_outliers = rng.uniform(low=-6, high=6, size=(12, 2))
X = np.vstack([X_inliers, X_outliers])
 
iso = IsolationForest(n_estimators=200, contamination=0.1, random_state=42)
iso.fit(X)
 
scores = iso.score_samples(X)
most_anomalous = np.argsort(scores)[0]
print("Lowest anomaly score index:", most_anomalous)
print("Is it one of the injected outliers?", most_anomalous >= 120)
score_samples.py
import numpy as np
from sklearn.ensemble import IsolationForest
from sklearn.datasets import make_blobs
 
rng = np.random.RandomState(42)
X_inliers, _ = make_blobs(n_samples=120, centers=1, cluster_std=0.5, random_state=42)
X_outliers = rng.uniform(low=-6, high=6, size=(12, 2))
X = np.vstack([X_inliers, X_outliers])
 
iso = IsolationForest(n_estimators=200, contamination=0.1, random_state=42)
iso.fit(X)
 
scores = iso.score_samples(X)
most_anomalous = np.argsort(scores)[0]
print("Lowest anomaly score index:", most_anomalous)
print("Is it one of the injected outliers?", most_anomalous >= 120)
Output
Lowest anomaly score index: 126
Is it one of the injected outliers? True
Output
Lowest anomaly score index: 126
Is it one of the injected outliers? True

Tips

  • contaminationcontamination should be your best guess of the anomaly rate — raise it and you’ll flag more points as anomalies (more false positives); lower it and you’ll miss some real ones (more false negatives). It’s the usual precision/recall trade-off.
  • scale features first if they differ dramatically in range — a feature measured in the thousands can dominate the random splits
  • decision_function()decision_function() also works and is centered so 0 is roughly the cutoff between normal and anomalous

Pros and cons

Pros:

  • efficient even in high-dimensional data
  • no distance or density computation required
  • scales well — building random trees is cheap

Cons:

  • contaminationcontamination needs a reasonable guess; get it very wrong and the labels are noisy
  • like any random-forest-style method, individual runs vary slightly unless you fix random_staterandom_state

Mini-checkpoint

If contamination is too high, what happens?

  • you’ll label too many normal points as anomalies (more false positives).

Visualize it

Watch random axis-aligned splits carve up the plane. Each split cuts one region in two; a point turns red the moment it ends up alone in its own region — that’s “isolated.” The four corner outliers get isolated in just a couple of splits, while the crowded cluster in the middle takes many more:

sketch Isolation Forest isolates outliers fast p5.js
Random splits chop the plane into regions. Corner outliers get isolated (turn red) in only a few splits; the crowded cluster takes many more.

🧪 Try It Yourself

Exercise 1 – Fit an Isolation Forest

Exercise 2 – Rank Points by Anomaly Score

Exercise 3 – Raising Contamination Flags More Points

Next

Continue to Association Rule Learning (Apriori Algorithm) — a very different unsupervised technique for finding “if A then B” patterns instead of scoring outliers.

If this helped you, consider buying me a coffee ☕

Buy me a coffee

Was this page helpful?

Let us know how we did