Statistical Process Control — Control Charts
Advanced Statistical Methods
Detecting Process Shifts in Real Time
Statistical process control uses control charts to monitor processes and distinguish between common-cause and special-cause variation. X-bar, R, p, and c charts provide visual signals when a process goes out of control.
- Manufacturing — Detect equipment degradation before defective products are produced
- Healthcare — Monitor infection rates and patient outcomes for early warning signals
- Software engineering — Track defect rates across releases to maintain quality standards
Control charts turn raw process data into actionable signals for timely intervention.
Statistical process control (SPC) uses control charts to monitor whether a process remains in a state of statistical control — stable over time with only common-cause variation present.
The Control Chart Framework
DfControl Chart
A control chart plots a process statistic over time against control limits derived from the process variability when in control. Points outside the limits signal a special cause variation that warrants investigation.
Upper Control Limit (UCL) and Lower Control Limit (LCL) are set at:
where is the target (in-control mean), is the standard error, and (3-sigma limits).
Why 3-Sigma Limits?
The 3-sigma convention balances two error rates: Type I (false alarm — signal when process is in control) occurs with probability per point (two-sided). Type II (missed signal — no alarm when process is out of control) depends on the magnitude of the shift. For small shifts, more sensitive charts (CUSUM, EWMA) are preferred.
X-bar Chart (Process Mean)
DfX-bar Chart
Monitor the process mean using subgroup means of size :
When is unknown, estimate from the range: , where is a control chart constant.
| Subgroup size | ||||
|---|---|---|---|---|
| 2 | 1.128 | 0 | 3.267 | 1.880 |
| 3 | 1.693 | 0 | 2.574 | 1.023 |
| 4 | 2.059 | 0 | 2.282 | 0.729 |
| 5 | 2.326 | 0 | 2.114 | 0.577 |
| 10 | 3.078 | 0.223 | 1.777 | 0.308 |
X-bar Control Limits (Using Range)
Here,
- =Grand mean of subgroup averages
- =Average range across subgroups
- =Control chart constant (depends on subgroup size n)
R Chart (Process Variability)
DfR Chart
Monitor process variability using the subgroup range :
The R chart must be in control before the X-bar chart is interpreted.
R Chart First!
Always check the R chart before the X-bar chart. If process variability is out of control, the X-bar control limits are unreliable. For large subgroups (), use the S chart (standard deviation) instead of the R chart.
p Chart (Proportion Defective)
Dfp Chart
Monitor the proportion of defective items in subgroups of size :
where is the overall proportion defective across subgroups.
Variable Width Limits
When subgroup sizes vary, the control limits vary for each subgroup. Points should be plotted against their specific limits, not a constant band. Alternatively, use the standardized p chart: .
c Chart (Count of Defects)
Dfc Chart
Monitor the count of defects per unit (constant inspection unit):
where is the average number of defects.
c vs. u Chart
The c chart assumes a constant inspection unit. The u chart allows variable sample sizes: with limits .
Western Electric Rules
DfWestern Electric Rules
These rules increase sensitivity to small shifts by detecting patterns, not just single points outside limits:
| Rule | Condition | Signal |
|---|---|---|
| 1 | Any point beyond 3σ limits | Immediate out-of-control |
| 2 | 2 of 3 consecutive points beyond 2σ (same side) | Trend or shift |
| 3 | 4 of 5 consecutive points beyond 1σ (same side) | Small persistent shift |
| 4 | 8 consecutive points on same side of center line | Process mean shift |
Rules and False Alarms
Each rule increases the false alarm rate. Applying all four rules simultaneously increases the per-point false alarm rate beyond the 0.0027 of rule 1 alone. Use the Supplemental Rules (Western Electric / Nelson rules) judiciously.
Process Capability Indices
Cp — Process Potential
Here,
- =Upper Specification Limit
- =Lower Specification Limit
- =Process standard deviation
means the process variation fits within the specification width. means 6σ exactly equals the specification range.
Cpk — Process Centering
Here,
- =Process mean
- =Centering coefficient (T = target)
Cpk Interpretation
| Interpretation | |
|---|---|
| Process does not meet specifications | |
| Marginally capable | |
| Adequate for most applications | |
| Excellent (Six Sigma threshold at 2.0) | |
| World-class (fewer than 3.4 defects per million) |
Average Run Length (ARL)
DfAverage Run Length
The ARL is the expected number of points plotted before an out-of-control signal:
where is the false alarm probability per point. For 3-sigma limits: (in-control ARL).
Out-of-Control ARL
Here,
- =Probability of not detecting the shift at a single point
- =Power of the control chart at the shift
ARL and Shift Magnitude
For the X-bar chart with 3-sigma limits and :
- Shift of : (detects in ~44 subgroups)
- Shift of :
- Shift of :
Larger shifts are detected faster. For small shifts (), CUSUM or EWMA charts are more efficient.
CUSUM Chart
DfCumulative Sum (CUSUM) Chart
The CUSUM accumulates deviations from the target:
where is the allowance value (reference value) and is the shift size to detect. An out-of-control signal occurs when or .
CUSUM Design
The CUSUM is designed to detect a specific shift size :
- (in standard deviation units)
- (common choice) gives for
- Tabular CUSUM is easier to implement than the graphical version
EWMA Chart
DfEWMA Chart
The Exponentially Weighted Moving Average chart uses:
where and is the smoothing parameter. Control limits:
where (standard choice).
EWMA Tuning
- Small (0.05-0.1): good for detecting small shifts (slow response)
- Large (0.25-0.4): good for detecting large shifts (fast response)
- : equivalent to Shewhart (X-bar) chart
- The EWMA chart is equivalent to a CUSUM chart with specific parameter choices
Python Implementation
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(42)
# --- Simulate in-control and out-of-control process ---
n_subgroups = 50
subgroup_size = 5
mu0 = 50.0
sigma = 2.0
# In-control: first 30 subgroups
X_in = np.random.randn(30, subgroup_size) * sigma + mu0
# Out-of-control: shift of 1.5σ after subgroup 30
X_out = np.random.randn(20, subgroup_size) * sigma + (mu0 + 1.5 * sigma)
X = np.vstack([X_in, X_out])
xbar = X.mean(axis=1)
R = X.max(axis=1) - X.min(axis=1)
R_bar = R[:30].mean() # Use in-control phase for limits
xbar_bar = xbar[:30].mean()
# --- X-bar chart ---
A2 = 0.577 # For n=5
UCL_xbar = xbar_bar + A2 * R_bar
LCL_xbar = xbar_bar - A2 * R_bar
# --- R chart ---
D4 = 2.114 # For n=5
D3 = 0 # For n=5
UCL_R = D4 * R_bar
LCL_R = D3 * R_bar
# --- Western Electric rules detection ---
def detect_western_electric(xbar, center, ucl, lcl):
sigma_est = (ucl - center) / 3
signals = np.zeros(len(xbar), dtype=bool)
for i in range(len(xbar)):
# Rule 1: Beyond 3σ
if xbar[i] > ucl or xbar[i] < lcl:
signals[i] = True
# Rule 2: 2 of 3 beyond 2σ
if i >= 2:
above_2s = np.sum(xbar[i-2:i+1] > center + 2*sigma_est) >= 2
below_2s = np.sum(xbar[i-2:i+1] < center - 2*sigma_est) >= 2
if above_2s or below_2s:
signals[i] = True
# Rule 3: 4 of 5 beyond 1σ
if i >= 4:
above_1s = np.sum(xbar[i-4:i+1] > center + sigma_est) >= 4
below_1s = np.sum(xbar[i-4:i+1] < center - sigma_est) >= 4
if above_1s or below_1s:
signals[i] = True
# Rule 4: 8 consecutive same side
if i >= 7:
if np.all(xbar[i-7:i+1] > center) or np.all(xbar[i-7:i+1] < center):
signals[i] = True
return signals
signals = detect_western_electric(xbar, xbar_bar, UCL_xbar, LCL_xbar)
# --- CUSUM chart ---
def cusum_chart(x, mu0, sigma, K_factor=0.5, H=5):
K = K_factor * sigma
S_pos = np.zeros(len(x) + 1)
S_neg = np.zeros(len(x) + 1)
for t in range(len(x)):
S_pos[t+1] = max(0, S_pos[t] + (x[t] - mu0) - K)
S_neg[t+1] = max(0, S_neg[t] + (mu0 - x[t]) - K)
return S_pos[1:], S_neg[1:]
S_pos, S_neg = cusum_chart(xbar, mu0, sigma, K_factor=0.5, H=5*sigma)
# --- EWMA chart ---
def ewma_chart(x, mu0, sigma, lam=0.2, L=3):
z = np.zeros(len(x) + 1)
z[0] = mu0
for t in range(len(x)):
z[t+1] = lam * x[t] + (1 - lam) * z[t]
z = z[1:]
ucl = mu0 + L * sigma * np.sqrt(lam / (2 - lam))
lcl = mu0 - L * sigma * np.sqrt(lam / (2 - lam))
return z, ucl, lcl
ewma_z, ewma_ucl, ewma_lcl = ewma_chart(xbar, mu0, sigma, lam=0.2)
# --- Process capability ---
USL = mu0 + 3 * sigma
LSL = mu0 - 3 * sigma
Cp = (USL - LSL) / (6 * sigma)
Cpk = min((USL - mu0) / (3 * sigma), (mu0 - LSL) / (3 * sigma))
print(f"Cp = {Cp:.3f}, Cpk = {Cpk:.3f}")
# --- Plotting ---
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# X-bar chart
axes[0, 0].plot(range(1, n_subgroups+1), xbar, 'b-o', markersize=4)
axes[0, 0].axhline(UCL_xbar, color='red', linestyle='--', label='UCL')
axes[0, 0].axhline(xbar_bar, color='green', linestyle='-', label='CL')
axes[0, 0].axhline(LCL_xbar, color='red', linestyle='--', label='LCL')
axes[0, 0].axvline(30.5, color='orange', linestyle=':', label='Shift')
if signals.any():
axes[0, 0].scatter(np.where(signals)[0]+1, xbar[signals], color='red', s=80, zorder=5, label='Signal')
axes[0, 0].set_title('X-bar Chart')
axes[0, 0].set_xlabel('Subgroup')
axes[0, 0].set_ylabel('X-bar')
axes[0, 0].legend()
# R chart
axes[0, 1].plot(range(1, n_subgroups+1), R, 'b-o', markersize=4)
axes[0, 1].axhline(UCL_R, color='red', linestyle='--', label='UCL')
axes[0, 1].axhline(R_bar, color='green', linestyle='-', label='CL')
axes[0, 1].axhline(LCL_R, color='red', linestyle='--', label='LCL')
axes[0, 1].set_title('R Chart')
axes[0, 1].set_xlabel('Subgroup')
axes[0, 1].set_ylabel('Range')
axes[0, 1].legend()
# CUSUM
axes[1, 0].plot(range(1, n_subgroups+1), S_pos, 'b-', label='CUSUM⁺')
axes[1, 0].plot(range(1, n_subgroups+1), S_neg, 'r-', label='CUSUM⁻')
axes[1, 0].axhline(5*sigma, color='gray', linestyle='--', label='Decision interval H')
axes[1, 0].axvline(30.5, color='orange', linestyle=':', label='Shift')
axes[1, 0].set_title('CUSUM Chart')
axes[1, 0].set_xlabel('Subgroup')
axes[1, 0].set_ylabel('CUSUM statistic')
axes[1, 0].legend()
# EWMA
axes[1, 1].plot(range(1, n_subgroups+1), ewma_z, 'b-', linewidth=2, label='EWMA')
axes[1, 1].axhline(ewma_ucl, color='red', linestyle='--', label='UCL')
axes[1, 1].axhline(mu0, color='green', linestyle='-', label='CL')
axes[1, 1].axhline(ewma_lcl, color='red', linestyle='--', label='LCL')
axes[1, 1].axvline(30.5, color='orange', linestyle=':', label='Shift')
axes[1, 1].set_title('EWMA Chart (λ=0.2)')
axes[1, 1].set_xlabel('Subgroup')
axes[1, 1].set_ylabel('EWMA statistic')
axes[1, 1].legend()
plt.tight_layout()
plt.savefig('spc_control_charts.png', dpi=150)
plt.show()
# --- ARL calculation via simulation ---
def simulate_arl(mu_shift, sigma, mu0, n=5, n_sims=10000, k=3):
arl_count = 0
for _ in range(n_sims):
t = 0
while True:
t += 1
x = np.random.randn(n) * sigma + mu0 + mu_shift
xbar_t = x.mean()
R_t = x.max() - x.min()
ucl = mu0 + k * sigma / np.sqrt(n)
lcl = mu0 - k * sigma / np.sqrt(n)
if xbar_t > ucl or xbar_t < lcl:
arl_count += t
break
if t > 10000:
arl_count += t
break
return arl_count / n_sims
print("\n=== Average Run Length (X-bar chart, n=5) ===")
for shift in [0, 0.5, 1.0, 1.5, 2.0, 3.0]:
arl = simulate_arl(shift * sigma, sigma, mu0)
print(f"Shift = {shift}σ: ARL ≈ {arl:.1f}")
Chart Selection Guide
| Situation | Recommended Chart |
|---|---|
| Large shifts () | X-bar (Shewhart) |
| Small shifts () | CUSUM or EWMA |
| Quick detection of shift | EWMA with large |
| Persistent small drift | CUSUM or EWMA with small |
| Monitor count data | c chart or u chart |
| Monitor proportion defective | p chart |
Related Topics
- See Six Sigma for DMAIC methodology
- See Design of Experiments for process optimization
- See Hypothesis Testing for the inference framework underlying control charts
Key Takeaways
Summary: Statistical Process Control
- Control charts distinguish common-cause from special-cause variation using 3-sigma limits
- X-bar chart monitors process mean; R chart monitors variability — always check R chart first
- p chart (proportion defective), c chart (defect count), u chart (defects per unit) for attribute data
- Western Electric rules increase sensitivity to small shifts by detecting patterns
- Cp measures process potential (width vs. specs); Cpk measures centering — is adequate
- ARL quantifies detection speed: for 3-sigma limits (in-control)
- CUSUM and EWMA charts are more efficient than X-bar for detecting small shifts ()
- 3-sigma limits balance false alarms () with detection power for moderate shifts