🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
Search courses…
💼 Servicesℹ️ About✉️ ContactView Pricing Plansfrom $10

Statistical Process Control — Control Charts

Advanced Statistical MethodsQuality Control🟢 Free Lesson

Advertisement

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:

UCL=μ0+kσXˉ,LCL=μ0kσXˉ\text{UCL} = \mu_0 + k\sigma_{\bar{X}}, \quad \text{LCL} = \mu_0 - k\sigma_{\bar{X}}

where μ0\mu_0 is the target (in-control mean), σXˉ\sigma_{\bar{X}} is the standard error, and k=3k = 3 (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 α0.0027\alpha \approx 0.0027 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 Xˉj\bar{X}_j of size nn:

UCL=μ0+3σn,Center Line=μ0,LCL=μ03σn\text{UCL} = \mu_0 + 3\frac{\sigma}{\sqrt{n}}, \quad \text{Center Line} = \mu_0, \quad \text{LCL} = \mu_0 - 3\frac{\sigma}{\sqrt{n}}

When σ\sigma is unknown, estimate from the range: σ^=Rˉ/d2\hat{\sigma} = \bar{R} / d_2, where d2d_2 is a control chart constant.

Subgroup size nnd2d_2D3D_3D4D_4A2A_2
21.12803.2671.880
31.69302.5741.023
42.05902.2820.729
52.32602.1140.577
103.0780.2231.7770.308

X-bar Control Limits (Using Range)

UCL=Xˉ+A2Rˉ,LCL=XˉA2Rˉ\text{UCL} = \bar{X} + A_2 \bar{R}, \quad \text{LCL} = \bar{X} - A_2 \bar{R}

Here,

  • Xˉ\bar{X}=Grand mean of subgroup averages
  • Rˉ\bar{R}=Average range across subgroups
  • A2A_2=Control chart constant (depends on subgroup size n)

R Chart (Process Variability)

DfR Chart

Monitor process variability using the subgroup range Rj=X(j)maxX(j)minR_j = X_{(j)}^{\max} - X_{(j)}^{\min}:

UCLR=D4Rˉ,Center Line=Rˉ,LCLR=D3Rˉ\text{UCL}_R = D_4 \bar{R}, \quad \text{Center Line} = \bar{R}, \quad \text{LCL}_R = D_3 \bar{R}

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 (n>10n > 10), 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 njn_j:

UCLj=pˉ+3pˉ(1pˉ)nj,LCLj=pˉ3pˉ(1pˉ)nj\text{UCL}_j = \bar{p} + 3\sqrt{\frac{\bar{p}(1-\bar{p})}{n_j}}, \quad \text{LCL}_j = \bar{p} - 3\sqrt{\frac{\bar{p}(1-\bar{p})}{n_j}}

where pˉ=j=1mdjj=1mnj\bar{p} = \frac{\sum_{j=1}^m d_j}{\sum_{j=1}^m n_j} is the overall proportion defective across mm 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: Zj=(pjpˉ)/pˉ(1pˉ)/njZ_j = (p_j - \bar{p}) / \sqrt{\bar{p}(1-\bar{p})/n_j}.


c Chart (Count of Defects)

Dfc Chart

Monitor the count of defects per unit (constant inspection unit):

UCL=cˉ+3cˉ,Center Line=cˉ,LCL=max(0,cˉ3cˉ)\text{UCL} = \bar{c} + 3\sqrt{\bar{c}}, \quad \text{Center Line} = \bar{c}, \quad \text{LCL} = \max(0, \bar{c} - 3\sqrt{\bar{c}})

where cˉ=j=1mcjm\bar{c} = \frac{\sum_{j=1}^m c_j}{m} 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: uj=cj/nju_j = c_j / n_j with limits uˉ±3uˉ/nj\bar{u} \pm 3\sqrt{\bar{u}/n_j}.


Western Electric Rules

DfWestern Electric Rules

These rules increase sensitivity to small shifts by detecting patterns, not just single points outside limits:

RuleConditionSignal
1Any point beyond 3σ limitsImmediate out-of-control
22 of 3 consecutive points beyond 2σ (same side)Trend or shift
34 of 5 consecutive points beyond 1σ (same side)Small persistent shift
48 consecutive points on same side of center lineProcess 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

Cp=USLLSL6σC_p = \frac{USL - LSL}{6\sigma}

Here,

  • USLUSL=Upper Specification Limit
  • LSLLSL=Lower Specification Limit
  • σ\sigma=Process standard deviation

Cp>1C_p > 1 means the process variation fits within the specification width. Cp=1C_p = 1 means 6σ exactly equals the specification range.

Cpk — Process Centering

Cpk=min(USLμ3σ,μLSL3σ)=Cp(1k)C_{pk} = \min\left(\frac{USL - \mu}{3\sigma}, \frac{\mu - LSL}{3\sigma}\right) = C_p(1 - k)

Here,

  • μ\mu=Process mean
  • k=Tμ(USLLSL)/2k = \frac{T - \mu}{(USL - LSL)/2}=Centering coefficient (T = target)

Cpk Interpretation

CpkC_{pk}Interpretation
<1.0< 1.0Process does not meet specifications
1.01.331.0 - 1.33Marginally capable
1.331.671.33 - 1.67Adequate for most applications
1.672.01.67 - 2.0Excellent (Six Sigma threshold at 2.0)
>2.0> 2.0World-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:

ARL=1α\text{ARL} = \frac{1}{\alpha}

where α\alpha is the false alarm probability per point. For 3-sigma limits: ARL0=1/0.0027370\text{ARL}_0 = 1/0.0027 \approx 370 (in-control ARL).

Out-of-Control ARL

ARL1=11β\text{ARL}_1 = \frac{1}{1 - \beta}

Here,

  • β\beta=Probability of not detecting the shift at a single point
  • 1β1-\beta=Power of the control chart at the shift

ARL and Shift Magnitude

For the X-bar chart with 3-sigma limits and n=5n = 5:

  • Shift of 1σ1\sigma: ARL144\text{ARL}_1 \approx 44 (detects in ~44 subgroups)
  • Shift of 2σ2\sigma: ARL16\text{ARL}_1 \approx 6
  • Shift of 3σ3\sigma: ARL12\text{ARL}_1 \approx 2

Larger shifts are detected faster. For small shifts (<1.5σ< 1.5\sigma), CUSUM or EWMA charts are more efficient.


CUSUM Chart

DfCumulative Sum (CUSUM) Chart

The CUSUM accumulates deviations from the target:

St+=max(0,  St1++(Xtμ0)K)S_t^+ = \max(0, \; S_{t-1}^+ + (X_t - \mu_0) - K)
St=max(0,  St1+(μ0Xt)K)S_t^- = \max(0, \; S_{t-1}^- + (\mu_0 - X_t) - K)

where K=δσ/2K = \delta\sigma/2 is the allowance value (reference value) and δ\delta is the shift size to detect. An out-of-control signal occurs when St+>HS_t^+ > H or St>HS_t^- > H.

CUSUM Design

The CUSUM is designed to detect a specific shift size δ\delta:

  • K=δ/2K = \delta/2 (in standard deviation units)
  • H=5H = 5 (common choice) gives ARL0465\text{ARL}_0 \approx 465 for δ=0.5σ\delta = 0.5\sigma
  • Tabular CUSUM is easier to implement than the graphical version

EWMA Chart

DfEWMA Chart

The Exponentially Weighted Moving Average chart uses:

Zt=λXt+(1λ)Zt1Z_t = \lambda X_t + (1 - \lambda) Z_{t-1}

where Z0=μ0Z_0 = \mu_0 and λ(0,1]\lambda \in (0, 1] is the smoothing parameter. Control limits:

UCL=μ0+Lσλ2λ[1(1λ)2t]\text{UCL} = \mu_0 + L\sigma\sqrt{\frac{\lambda}{2-\lambda}\left[1 - (1-\lambda)^{2t}\right]}
LCL=μ0Lσλ2λ[1(1λ)2t]\text{LCL} = \mu_0 - L\sigma\sqrt{\frac{\lambda}{2-\lambda}\left[1 - (1-\lambda)^{2t}\right]}

where L=3L = 3 (standard choice).

EWMA Tuning

  • Small λ\lambda (0.05-0.1): good for detecting small shifts (slow response)
  • Large λ\lambda (0.25-0.4): good for detecting large shifts (fast response)
  • λ=1\lambda = 1: 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

SituationRecommended Chart
Large shifts (>1.5σ> 1.5\sigma)X-bar (Shewhart)
Small shifts (<1.5σ< 1.5\sigma)CUSUM or EWMA
Quick detection of shiftEWMA with large λ\lambda
Persistent small driftCUSUM or EWMA with small λ\lambda
Monitor count datac chart or u chart
Monitor proportion defectivep chart

Related Topics


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 — Cpk>1.33C_{pk} > 1.33 is adequate
  • ARL quantifies detection speed: ARL0370\text{ARL}_0 \approx 370 for 3-sigma limits (in-control)
  • CUSUM and EWMA charts are more efficient than X-bar for detecting small shifts (<1.5σ< 1.5\sigma)
  • 3-sigma limits balance false alarms (α=0.0027\alpha = 0.0027) with detection power for moderate shifts

Premium Content

Statistical Process Control — Control Charts

Unlock this lesson and 900+ advanced tutorials with a Premium plan.

🎯End-to-end Projects
💼Interview Prep
📜Certificates
🤝Community Access

Already a member? Log in

Need Expert Statistics Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement