Functional Weave
Code in Rust

monitor.anomaly@1.0.0

README.md

2,555 bytes · view raw

# monitor.anomaly

Says whether a new value of a metric (a latency, a queue depth, requests a
minute) stands out from its recent history, and in which direction. Two
methods:

- `stddev`: the z-score, `z = (value - mean) / sd`, with the population
  standard deviation of the history. An anomaly when `|z| > k`, with
  `k = thresholdHundredths / 100` (3.0 is the usual "three sigma").
- `mad`: the modified z-score of Iglewicz and Hoaglin,
  `M = 0.6745 (value - median) / MAD`, where MAD is the median of the
  absolute deviations from the median. An anomaly when `|M| > k`; they
  recommend 3.5 (`350`).

## Which to use

The mean and standard deviation are themselves dragged by the outliers you
are looking for: one spike in the history inflates the standard deviation and
hides the next spike. The median and MAD barely move. With history
10, 11, 12, 13, 50, a new value of 30 scores 0.69 by `stddev` (normal) but
12.14 by `mad` (an anomaly). Use `mad` for anything spiky, which is most
operational metrics; `stddev` for smooth, roughly normal ones.

## Exact, not floating point

Everything is integer arithmetic (BigInt in TypeScript, i128 in Rust), so the
three languages agree to the last digit and the comparison with the threshold
is exact: a z of exactly 3.00 against a threshold of 300 is not an anomaly
(`>`, not `>=`), in every language. The reported numbers are rounded as the
manifest says: `centerMilli` half-up (halves away from zero), `spreadMilli`
and `scoreHundredths` floored. The 0.6745 constant is used as exactly
6745/10000. In Rust, sums of squares must stay under 2^127: values up to about
10^15 with thousands of history points are fine.

## Zero spread

When the history is flat (standard deviation 0) or more than half of it is the
same value (MAD 0), there is no scale to measure against: any value different
from the center is an anomaly and equal is normal, and the score is null.
Iglewicz and Hoaglin note this weakness of the MAD; feed it a longer or more
varied history if it matters.

## Errors

- `history must have at least 2 values, received 1`
- `thresholdHundredths must be at least 1, received 0`
- `unknown anomaly method: iqr`

## Sources

- Boris Iglewicz and David C. Hoaglin (1993), *How to Detect and Handle
  Outliers*, ASQC Quality Press (ASQC Basic References in Quality Control,
  vol. 16): the modified z-score and the 3.5 threshold. Summarised by NIST/SEMATECH
  e-Handbook of Statistical Methods, 1.3.5.17 "Detection of Outliers",
  https://www.itl.nist.gov/div898/handbook/eda/section3/eda35h.htm