"Every process has a heartbeat. My job is to tell the arrhythmias from the times you simply breathed."
A Level-Headed AI Agent
The Big Picture
Section 12.1 gave you a vocabulary for anomalies: point, contextual, collective. Statistical process control (SPC) is the oldest, most battle-tested machinery for actually catching one class of them online. It was born on a factory floor at Western Electric in the 1920s, and its central idea is disarmingly simple: separate the routine wobble that any healthy sensor stream shows from the genuine shifts that signal something changed. SPC does this with a control chart, a running plot of a monitored statistic bracketed by data-derived limits, plus a small set of decision rules for when to raise a flag. It costs almost nothing to compute, needs no labels, and its false-alarm behavior can be tuned to a number you choose in advance. For sensor AI it is both a standalone detector for stable signals and the honest baseline that any fancier model in this book must beat before it earns its keep.
This section assumes you are comfortable with the Gaussian, standard deviation, and tail probabilities from Chapter 4, and with the ideas of sampling rate and autocorrelation from Chapter 3. We stay with the classical charts here; residual-based thresholds get their own treatment in Section 12.3, and the sequential change-point machinery (CUSUM, BOCPD) in Section 12.4.
Common cause versus special cause
The conceptual heart of SPC, due to Walter Shewhart, is a two-way split of all variation. Common-cause variation is the intrinsic noise floor of a process operating as designed: thermal noise in the ADC, micro-vibration in a bearing, the beat-to-beat jitter of a resting heart. It is stable, predictable in aggregate, and there is no single culprit to chase. Special-cause variation is an intrusion from outside that system: a loosening bolt, a fouled electrode, a firmware regression that biased a reading. A process showing only common-cause variation is said to be in statistical control, meaning its output is a stationary random draw whose distribution you can characterize once and trust.
Why does the distinction matter so much? Because it dictates the correct response. Reacting to common-cause noise as if it were a fault, sometimes called tampering, makes things worse: you chase ghosts, adjust settings that were fine, and inflate the very variance you were trying to shrink. Ignoring a special cause lets a real fault ride. SPC is, at bottom, a formal test that runs on every new sample and answers one question: is this reading still consistent with the in-control distribution, or has a special cause arrived?
Key Insight
A control chart is not a plot of "good" versus "bad" values. It is a running hypothesis test with the null hypothesis "the process is unchanged." The control limits are not engineering tolerances or spec limits handed down by a customer; they are computed from the process's own in-control behavior. A reading can sit comfortably inside spec and still trip the chart, because the chart is detecting a change in the generating distribution, not a violation of an external requirement. Confusing the two is the single most common misuse of SPC.
The Shewhart chart and three-sigma limits
The workhorse is the Shewhart chart. During a clean baseline period (Phase I) you estimate the in-control mean \(\mu_0\) and standard deviation \(\sigma_0\) of the monitored statistic. Then, in monitoring (Phase II), you plot each new value against a center line and a pair of control limits:
$$\text{UCL} = \mu_0 + L\sigma_0, \qquad \text{CL} = \mu_0, \qquad \text{LCL} = \mu_0 - L\sigma_0.$$The multiplier \(L\) is almost universally set to 3, giving the famous three-sigma limits. The choice is not arbitrary. For an in-control Gaussian, the probability of a single point falling outside \(\pm 3\sigma\) by chance is about \(0.0027\), so a lone out-of-limit point is strong evidence of a special cause. Shewhart picked three because it balanced two error costs he could name without knowing the application: too tight and you drown in false alarms, too loose and you miss real shifts. That balance is quantified by the average run length (ARL), the expected number of samples until an alarm. In control, \(\text{ARL}_0 = 1/0.0027 \approx 370\): a plain three-sigma chart false-alarms roughly once every 370 samples even when nothing is wrong. Choosing \(L\) is choosing your tolerable false-alarm rate, exactly the threshold-economics tradeoff that Section 12.7 makes explicit.
Run rules and the sensitivity dial
A single point outside three sigma is a blunt instrument. It catches large, abrupt shifts but is nearly blind to a small, sustained drift, the kind a slowly degrading sensor produces. The Western Electric rules add pattern detectors that read the sequence, not just the last point. The canonical set flags: one point beyond \(3\sigma\); two of three consecutive points beyond \(2\sigma\) on the same side; four of five beyond \(1\sigma\) on the same side; and eight consecutive points on one side of the center line. Each rule targets a signature that pure limit-crossing misses, especially a mean shift too small to escape the outer band on any single sample.
Every rule you add buys sensitivity and spends false-alarm budget. Turn on all four Western Electric rules and the in-control ARL drops from about 370 toward 90, meaning you now false-alarm four times as often. There is no free lunch: the fundamental tension in every detector in this chapter is detection speed against false-alarm rate, and run rules simply let you move along that curve. For a slow drift you would rather catch early, a better tool than piling on rules is a chart with memory.
Memory charts: EWMA for small, persistent shifts
The exponentially weighted moving average (EWMA) chart replaces each raw sample with a running blend of the present and the recent past:
$$y_t = \lambda\, x_t + (1-\lambda)\, y_{t-1}, \qquad 0 < \lambda \le 1.$$This is exactly the exponential smoother from Chapter 6, now pressed into service as a detector. Because \(y_t\) accumulates evidence across samples, a shift of half a sigma that no single point would flag pushes the smoothed value steadily until it crosses its (correspondingly tightened) limits. Small \(\lambda\) (say 0.1 to 0.2) gives a long memory and superb sensitivity to tiny drifts; \(\lambda \to 1\) recovers the memoryless Shewhart chart. The control limits shrink because the variance of \(y_t\) at steady state is \(\sigma_0^2\,\lambda/(2-\lambda)\), smaller than \(\sigma_0^2\):
$$\text{UCL}_t = \mu_0 + L\,\sigma_0\sqrt{\tfrac{\lambda}{2-\lambda}\bigl(1-(1-\lambda)^{2t}\bigr)}.$$import numpy as np
def ewma_chart(x, mu0, sigma0, lam=0.2, L=3.0):
y, prev = np.empty(len(x)), mu0
for t, xt in enumerate(x):
prev = lam * xt + (1 - lam) * prev # smoothed statistic
y[t] = prev
t_idx = np.arange(1, len(x) + 1)
spread = np.sqrt(lam / (2 - lam) * (1 - (1 - lam) ** (2 * t_idx)))
ucl, lcl = mu0 + L * sigma0 * spread, mu0 - L * sigma0 * spread
alarms = np.where((y > ucl) | (y < lcl))[0]
return y, ucl, lcl, alarms
rng = np.random.default_rng(0)
baseline = rng.normal(50.0, 1.0, 200) # Phase I: in control
drift = rng.normal(50.6, 1.0, 200) # +0.6 sigma special cause
stream = np.concatenate([baseline, drift])
y, ucl, lcl, alarms = ewma_chart(stream, mu0=50.0, sigma0=1.0)
print("first alarm at sample", alarms[0] if len(alarms) else "none")
The code above shows the whole mechanism: smooth, compute time-varying limits, flag crossings. Run it and the first alarm lands shortly after sample 200, catching a drift that individual points hide inside the noise.
Library Shortcut
Hand-rolling charts is worth doing once for understanding, but production monitoring should lean on a maintained implementation. The PyNomaly and scikit-learn ecosystems cover the learned detectors of Section 12.5, while dedicated SPC libraries such as spc and the charts in statsmodels ship Shewhart, EWMA, and CUSUM with the run rules and ARL-calibrated limits built in. A validated EWMA monitor with Western Electric rules, Phase I estimation, and limit computation collapses from roughly 40 lines of careful numpy to about 4 lines of configuration, and you inherit tested edge-case handling for startup transients and missing samples that the toy version above ignores.
Practical Example: a CNC spindle that drifts before it screams
An industrial machine shop instruments a CNC milling spindle with an accelerometer and tracks the RMS vibration in a narrow frequency band tied to the tool's rotation. In Phase I, across a week of known-good production, the band RMS sits at 0.42 g with a standard deviation of 0.03 g; those numbers set the center line and sigma. A plain three-sigma Shewhart chart guards against a sudden tool fracture, which slams the RMS well past the upper limit in one sample. But the failure the shop actually cares about is gradual tool wear, which nudges the RMS up by a tenth of a sigma per hour. An EWMA chart with \(\lambda = 0.15\) accumulates that creep and raises a maintenance flag hours before the vibration would ever cross a raw three-sigma line, turning an unplanned line stoppage into a scheduled tool change between shifts. This is precisely the condition-monitoring workflow that Chapter 37 builds out at fleet scale.
Bringing SPC to real sensor streams
Classical SPC was designed for occasional, independent quality measurements, and two of its assumptions break on high-rate sensor data. First, autocorrelation. SPC's limits assume successive samples are independent, but a signal sampled at 1 kHz is heavily correlated from one sample to the next. Feed raw high-rate data into a Shewhart chart and the effective sample count is far smaller than the nominal one, so the naive sigma is wrong and false alarms explode. The fixes are to monitor a decimated or block-summarized statistic (a rational subgroup, one summary per second rather than per sample) or to chart the residuals of a model that has already removed the correlation, which is the bridge to Section 12.3. Second, nonstationarity: real signals have diurnal cycles, duty-cycle regimes, and thermal drift that violate the single fixed \(\mu_0\). Contextual normalization, per-regime baselines, or a slowly adapting center line keep the chart honest, connecting SPC to the streaming and online-learning machinery of Chapter 60. Get these two right and a monitor costing microseconds per sample will hold its own against models that cost a thousand times more.
Exercise
Take a stable sensor trace you have (or synthesize one as in the code above). (1) Estimate \(\mu_0\) and \(\sigma_0\) from the first half and build a three-sigma Shewhart chart on the second half; count the false alarms and compare to the expected \(\text{ARL}_0 \approx 370\). (2) Inject a step of \(0.5\sigma\) partway through and measure how many samples each of a Shewhart and an EWMA (\(\lambda=0.2\)) chart take to detect it. (3) Now add strong autocorrelation by low-pass filtering the trace and rerun the Shewhart chart without changing the limits. Explain the jump in false alarms and propose a rational-subgrouping fix.
Self-Check
1. Why can a reading sit inside the customer's specification limits yet still trip a control chart, and which of the two is the chart actually testing? 2. You turn on all four Western Electric rules and detection gets faster, but your on-call engineer complains. In terms of ARL, what did you trade away? 3. Why does an EWMA chart with small \(\lambda\) beat a Shewhart chart at catching a slow drift, and what does \(\lambda \to 1\) recover?
What's Next
In Section 12.3, we generalize the control-chart idea beyond a single monitored value to residuals: run a model of what the signal should be, watch what it fails to explain, and set thresholds on that error stream. This is how SPC's clean limit logic survives contact with the autocorrelated, nonstationary signals that broke the naive chart, and it sets up the sequential change-point detectors that follow.