Time-Series Anomaly Detection — The Math Behind the Alerts
An alert should fire on the gap between what you observed and what that moment predicted — not on the raw size of a number. Four rulers for measuring that gap (robust statistics, forecast residuals, subsequence distance, changepoints), built up from analogy to formula, then the two things that actually break in production: how you pick the threshold and how you evaluate.
Bayesian Online Changepoint Detection
Primary source — what this article is built on
undefined2026-08-25
Bayesian Online Changepoint DetectionarXiv:0710.3742Paper page·PDFTowards a Rigorous Evaluation of Time-series Anomaly DetectionarXiv:2109.05257Paper page·PDF
Current Time Series Anomaly Detection Benchmarks are Flawed and are Creating the Illusion of ProgressarXiv:2009.13807Paper page·PDF
2 a.m., and the dashboard turns red
Fire an alert when CPU goes above 90%. That is where monitoring almost always starts. Run it for a month and this almost always happens: a heavy batch job runs early every Sunday, CPU crosses 90%, and the on-call engineer is woken up for nothing. So you raise the threshold to 95% — and now a real weekday incident plateaus at 92% and nobody notices.
The number isn't the problem. The problem is that you defined "anomaly" purely by magnitude. 90% at 3 a.m. on Sunday is normal; 70% on Tuesday afternoon might not be. In a time series, an anomaly is not a value — it is a gap between what you observed and what that moment led you to expect. This article builds four rulers for measuring that gap: summary statistics, forecast residuals, subsequence distance, and changepoints. All you need to bring is a memory of what a mean and a standard deviation are.
Anomalies come in three shapes
A point anomaly is a single value sitting far away from the rest — a sensor glitch that reads 9999 for one sample.
A contextual anomaly is a perfectly ordinary value that should not happen at that moment: heating load spiking in midsummer, or 3 a.m. traffic matching the lunchtime peak. Nothing about it is unusual in the marginal distribution, so no amount of staring at a histogram will surface it.
A subsequence anomaly is a stretch where every individual value is ordinary but the shape is wrong: one irregular beat in an ECG, a vibration sensor whose period wobbles. Look at points independently and you will never see it.
The last two are what make time series distinct, and they are also the two that hurt in production.
Ruler 1: statistical outlyingness
Start with the classic. Measure the distance from the mean in units of standard deviation — the z-score.
Here is the observation at time , the mean, the standard deviation. In words: how many standard deviations away from average is this? Under a normal distribution happens roughly 0.3% of the time, which is where the familiar "three sigma" convention comes from.
Simple — and broken in two ways.
First, an anomaly hides itself. You computed and from data that contains the anomaly. One enormous spike inflates , which shrinks its own . The bigger the anomaly, the harder it is to see; statisticians call this masking. The fix is to swap in statistics that outliers cannot drag around: the median, and MAD (median absolute deviation).
In words: use "the median of the deviations from the median" as your measure of spread. The is only a scaling constant that makes this agree with for normally distributed data — there is no deeper meaning to it. Because the median barely moves until nearly half the data is contaminated, a handful of spikes cannot bend the ruler.
Second, the mean is not constant. With a trend, last year's average is no baseline for today; with a daily cycle, "the average day" describes neither noon nor midnight. The fix is to compute and over a recent window rather than all of history. Manufacturing has been running control charts on exactly this idea for the better part of a century.
import numpy as np
def robust_z(x, window=288): # 288 = one day at 5-minute resolution
med = np.median(x[-window:])
mad = np.median(np.abs(x[-window:] - med))
return (x[-1] - med) / (1.4826 * mad + 1e-9)
The window length is your first hyperparameter. Short windows are responsive, but a sustained anomaly quietly becomes the new normal. Long windows are sluggish and keep alerting on legitimate change. That tug-of-war reappears, in some disguise, in every method below.
Ruler 2: forecast, then look at what's left over
Flip the framing. Build a model that predicts the series when nothing is wrong, and use the gap between prediction and observation — the residual — as your anomaly score.
is the prediction. In words: anomaly detection has been decomposed into a forecasting problem plus some cleanup.
That decomposition earns its keep because it pushes trend, seasonality, and calendar effects entirely into the model. Go back to the Sunday batch job: if the forecast already knows "Sunday pre-dawn runs at 90%," then observing 90% leaves a residual near zero and nothing fires. Tuesday's 70%, against a forecast of 40%, produces a large residual. That is exactly the goal we set at the start — measuring deviation from context instead of magnitude. For building the forecaster itself, see Time-Series Forecasting from Scratch.
There is a trap here, though, and it is a big one. A forecaster that is too flexible will reproduce the anomalies too. Train a high-capacity model until the residuals vanish and it will predict the spikes along with everything else, leaving your detector permanently silent. An anomaly-detection forecaster should be deliberately blunt.
Comments
Sign in to comment