Lesson 3 of 7

Local Outlier Factor and One-Class SVM

In Lesson 2, the isolation forest scored every charge on one global question: how few random cuts fence this point off from the whole dataset? That works beautifully for a point sitting far out on its own. But some outliers are not far from everything, they are only far from their own neighbourhood, and a global score walks right past them. That is exactly how a fraudulent $130 charge on Maya's card slips through: it is a touch bigger than her everyday $22 coffees but nowhere near her hundreds-of-dollars big purchases, so nothing global flags it.

This lesson catches those. First the Local Outlier Factor (LOF), which scores each point against the density of its own neighbours. Then the one-class SVM, which learns a boundary around the normal region so you can wave any new point at it and ask, inside or out?

By the end of this lesson you will be able to:

  • Explain why a single global cutoff both misses a locally-sparse outlier and false-alarms on legitimate spread-out points
  • Define LOF as a local density ratio (k-distance, reachability distance, local density, the LOF ratio) and read LOF near 1 versus LOF well above 1
  • Compute LOF from scratch in R and interpret the scores
  • Fit a one-class SVM on normal data, tune its two knobs, and flag new points that fall outside the learned boundary

Prerequisites: you can run R and read base R (matrix, data.frame, scale, dist, sapply, small functions), and you have done Lesson 1: What Is an Anomaly? (global vs local outliers, distance vs density) and Lesson 2: Isolation Forest (a global detector). No SVM background is needed; the margin idea is built up here.

The problem

A global cutoff misses a local outlier

Let us keep working with Maya's card, but look at the shape of her spending more carefully. It has two natural clusters. A big, tight crowd of everyday charges: coffee, groceries, a few dollars, a few miles from home. And a small, loose scatter of legitimate big purchases: a laptop, a flight, hundreds of dollars, sometimes far away. Now a fraudulent $130 charge lands, 9 miles out. It is a bit bigger than her everyday spend, but nowhere near her big-purchase range.

Watch what a global rule does with it. First build the card (each lesson runs in a fresh R session, so we create the data here, run this once):

RInteractive R
set.seed(1) everyday <- data.frame(amount = round(rnorm(60, 22, 5), 2), # 60 small local charges miles = round(abs(rnorm(60, 3, 1)), 1)) biglegit <- data.frame(amount = round(rnorm(12, 520, 70), 2), # 12 big, sometimes-far purchases miles = round(abs(rnorm(12, 18, 4)), 1)) fraud <- data.frame(amount = 130, miles = 9) # the charge we want to catch card <- rbind(everyday, biglegit, fraud) card$kind <- c(rep("everyday", 60), rep("big legit", 12), "FRAUD") Z <- scale(card[, c("amount", "miles")]) # put amount and miles on one common scale nrow(card) #> [1] 73

  

The obvious global rule (and the natural next step from Lesson 1's z-score) is: how far does each charge sit from the center of Maya's spending? Rank by that distance and look at the top of the list.

RInteractive R
card$global_dist <- sqrt(rowSums(Z^2)) # distance from the center, in 2-D (Lesson 1's z-score idea) head(card[order(-card$global_dist), c("kind", "amount", "miles")], 5) # the "most unusual" charges #> kind amount miles #> 70 big legit 497.30 22.7 #> 61 big legit 484.58 20.1 #> 63 big legit 504.98 19.2 #> 68 big legit 517.37 17.8 #> 66 big legit 569.89 15.9 rank(-card$global_dist)[card$kind == "FRAUD"] # where the fraud ranks (1 = most extreme) #> [1] 22

  

The 12 charges a global cutoff screams about are all 12 of Maya's legitimate big purchases, pure false alarms. And the actual fraud? It ranks 22nd of 73, buried in the crowd of ordinary charges. By distance-from-center, the fraud looks completely normal.

Key Insight
The fraud is not a global outlier. It is a local one: it sits in a sparse pocket right beside Maya's dense everyday cluster. To catch it we must stop asking "how far from everyone?" and start asking "how does this point's crowding compare to the crowding of the points around it?"