Create professional Statistical Process Control (SPC) charts to monitor process stability. Supports X-bar & R, X-bar & S, I-MR, p-chart, and c-chart types with automatic control limit calculation and out-of-control detection using Western Electric rules.
Not sure which chart to use or how to format your data? Click a chart type below to load a sample dataset:
A control chart (also called a Shewhart chart or process behavior chart) is a statistical tool used to monitor whether a process is in a state of statistical control. Developed by Walter Shewhart at Bell Labs in the 1920s, control charts are the foundation of Statistical Process Control (SPC).
The chart plots data points over time against a center line (process average) and upper and lower control limits (UCL/LCL), typically set at 3 standard deviations from the center. Points within these limits indicate normal variation; points outside suggest special cause variation requiring investigation.
For continuous data with subgroups of 2-10. Monitors process mean (X-bar) and variability (Range).
For continuous data with larger subgroups (>10). Uses standard deviation instead of range for better precision.
For individual measurements (no subgroups). Monitors individual values and consecutive moving ranges.
For attribute data. Monitors proportion of nonconforming items. Allows variable sample sizes.
For count data. Monitors number of nonconformities per unit with constant sample size.
Beyond simple limit violations, these rules detect non-random patterns that indicate process instability:
Any point beyond the UCL or LCL (3-sigma).
7 consecutive points on one side of the center line (indicates shift in process mean).
6 consecutive points increasing or decreasing (indicates process drift).
| n | A2 | D3 | D4 | d2 |
|---|---|---|---|---|
| 2 | 1.88 | 0 | 3.267 | 1.128 |
| 3 | 1.023 | 0 | 2.575 | 1.693 |
| 4 | 0.729 | 0 | 2.282 | 2.059 |
| 5 | 0.577 | 0 | 2.115 | 2.326 |
| 6 | 0.483 | 0 | 2.004 | 2.534 |
| 7 | 0.419 | 0.076 | 1.924 | 2.704 |
| 8 | 0.373 | 0.136 | 1.864 | 2.847 |
| 9 | 0.337 | 0.184 | 1.816 | 2.97 |
| 10 | 0.308 | 0.223 | 1.777 | 3.078 |
Using Matplotlib and Seaborn to create X-bar and R charts.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Sample subgroup data (5 measurements per subgroup, 20 subgroups)
np.random.seed(42)
n_subgroups = 20
subgroup_size = 5
data = 25.0 + np.random.normal(0, 0.02, (n_subgroups, subgroup_size))
# Calculate statistics
x_bars = data.mean(axis=1)
ranges = data.max(axis=1) - data.min(axis=1)
x_double_bar = x_bars.mean()
r_bar = ranges.mean()
# SPC constants for n=5
A2 = 0.577
D3 = 0.0
D4 = 2.115
# Control limits
x_ucl = x_double_bar + A2 * r_bar
x_lcl = x_double_bar - A2 * r_bar
r_ucl = D4 * r_bar
r_lcl = D3 * r_bar
subgroups = np.arange(1, n_subgroups + 1)
# Styling
sns.set_theme(style="whitegrid")
fig, axes = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# X-bar chart
sns.lineplot(x=subgroups, y=x_bars, marker="o", ax=axes[0], color="#1565C0")
axes[0].axhline(x_double_bar, color="green", label="Center Line")
axes[0].axhline(x_ucl, color="red", linestyle="--", label="UCL/LCL")
axes[0].axhline(x_lcl, color="red", linestyle="--")
axes[0].set_title("X-bar Chart")
axes[0].set_ylabel("Subgroup Mean")
axes[0].legend(loc="best")
# R chart
sns.lineplot(x=subgroups, y=ranges, marker="o", ax=axes[1], color="#1565C0")
axes[1].axhline(r_bar, color="green", label="Center Line")
axes[1].axhline(r_ucl, color="red", linestyle="--", label="UCL/LCL")
axes[1].axhline(r_lcl, color="red", linestyle="--")
axes[1].set_title("R Chart")
axes[1].set_xlabel("Subgroup")
axes[1].set_ylabel("Range")
axes[1].legend(loc="best")
plt.suptitle("X-bar and R Control Charts", y=1.02)
plt.tight_layout()
plt.show()Using either qcc (SPC-focused) or ggplot2 (fully custom).
Option 1: qcc package
library(qcc)
# sample data: 20 subgroups of 5 measurements each
set.seed(42)
data_matrix <- matrix(rnorm(100, mean = 25, sd = 0.02), ncol = 5)
# X-bar and R chart
qcc(data_matrix, type = "xbar", title = "X-bar Chart")
qcc(data_matrix, type = "R", title = "R Chart")
# individual and Moving Range chart
individual_data <- rnorm(25, mean = 25, sd = 0.02)
qcc(individual_data, type = "xbar.one", title = "I Chart")
# p-chart
defects <- rbinom(20, size = 100, prob = 0.05)
qcc(defects, type = "p", sizes = rep(100, 20), title = "p-Chart")Option 2: ggplot2
library(tidyverse)
# sample data: 20 subgroups of 5 measurements each
set.seed(42)
n_subgroups <- 20
subgroup_size <- 5
data_matrix <- matrix(rnorm(n_subgroups * subgroup_size, mean = 25, sd = 0.02), ncol = subgroup_size)
# subgroup stats
x_bars <- rowMeans(data_matrix)
ranges <- apply(data_matrix, 1, function(x) max(x) - min(x))
x_double_bar <- mean(x_bars)
r_bar <- mean(ranges)
# SPC constants for n=5
A2 <- 0.577
D3 <- 0.0
D4 <- 2.115
# control limits
x_ucl <- x_double_bar + A2 * r_bar
x_lcl <- x_double_bar - A2 * r_bar
r_ucl <- D4 * r_bar
r_lcl <- D3 * r_bar
df_x <- data.frame(subgroup = 1:n_subgroups, value = x_bars)
df_r <- data.frame(subgroup = 1:n_subgroups, value = ranges)
# X-bar chart
ggplot(df_x, aes(x = subgroup, y = value)) +
geom_line(color = "#1565C0") +
geom_point(color = "#1565C0") +
geom_hline(yintercept = x_double_bar, color = "darkgreen") +
geom_hline(yintercept = x_ucl, linetype = "dashed", color = "red") +
geom_hline(yintercept = x_lcl, linetype = "dashed", color = "red") +
labs(title = "X-bar Chart", x = "Subgroup", y = "Subgroup Mean") +
theme_minimal()
# R chart
ggplot(df_r, aes(x = subgroup, y = value)) +
geom_line(color = "#1565C0") +
geom_point(color = "#1565C0") +
geom_hline(yintercept = r_bar, color = "darkgreen") +
geom_hline(yintercept = r_ucl, linetype = "dashed", color = "red") +
geom_hline(yintercept = r_lcl, linetype = "dashed", color = "red") +
labs(title = "R Chart", x = "Subgroup", y = "Range") +
theme_minimal()Control limits (UCL/LCL) are calculated from the data and represent the natural variation of the process. Specification limits (USL/LSL) are set by customers or engineers and represent acceptable ranges. A process can be in control but still produce out-of-spec parts.
For reliable control limits, collect at least 20-25 subgroups (or 25+ individual measurements for I-MR). The process should be in control when establishing initial limits.
Investigate the assignable cause. Check for equipment changes, material lot changes, operator differences, or environmental factors. Document findings and take corrective action. Do NOT simply remove the point without understanding why it occurred.
Use X-bar & R when you can collect multiple measurements per time period (subgroups). Use I-MR when each time period yields only one measurement, or when measurements are expensive/destructive and subgrouping is not practical.
Create professional Statistical Process Control (SPC) charts to monitor process stability. Supports X-bar & R, X-bar & S, I-MR, p-chart, and c-chart types with automatic control limit calculation and out-of-control detection using Western Electric rules.
Not sure which chart to use or how to format your data? Click a chart type below to load a sample dataset:
A control chart (also called a Shewhart chart or process behavior chart) is a statistical tool used to monitor whether a process is in a state of statistical control. Developed by Walter Shewhart at Bell Labs in the 1920s, control charts are the foundation of Statistical Process Control (SPC).
The chart plots data points over time against a center line (process average) and upper and lower control limits (UCL/LCL), typically set at 3 standard deviations from the center. Points within these limits indicate normal variation; points outside suggest special cause variation requiring investigation.
For continuous data with subgroups of 2-10. Monitors process mean (X-bar) and variability (Range).
For continuous data with larger subgroups (>10). Uses standard deviation instead of range for better precision.
For individual measurements (no subgroups). Monitors individual values and consecutive moving ranges.
For attribute data. Monitors proportion of nonconforming items. Allows variable sample sizes.
For count data. Monitors number of nonconformities per unit with constant sample size.
Beyond simple limit violations, these rules detect non-random patterns that indicate process instability:
Any point beyond the UCL or LCL (3-sigma).
7 consecutive points on one side of the center line (indicates shift in process mean).
6 consecutive points increasing or decreasing (indicates process drift).
| n | A2 | D3 | D4 | d2 |
|---|---|---|---|---|
| 2 | 1.88 | 0 | 3.267 | 1.128 |
| 3 | 1.023 | 0 | 2.575 | 1.693 |
| 4 | 0.729 | 0 | 2.282 | 2.059 |
| 5 | 0.577 | 0 | 2.115 | 2.326 |
| 6 | 0.483 | 0 | 2.004 | 2.534 |
| 7 | 0.419 | 0.076 | 1.924 | 2.704 |
| 8 | 0.373 | 0.136 | 1.864 | 2.847 |
| 9 | 0.337 | 0.184 | 1.816 | 2.97 |
| 10 | 0.308 | 0.223 | 1.777 | 3.078 |
Using Matplotlib and Seaborn to create X-bar and R charts.
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Sample subgroup data (5 measurements per subgroup, 20 subgroups)
np.random.seed(42)
n_subgroups = 20
subgroup_size = 5
data = 25.0 + np.random.normal(0, 0.02, (n_subgroups, subgroup_size))
# Calculate statistics
x_bars = data.mean(axis=1)
ranges = data.max(axis=1) - data.min(axis=1)
x_double_bar = x_bars.mean()
r_bar = ranges.mean()
# SPC constants for n=5
A2 = 0.577
D3 = 0.0
D4 = 2.115
# Control limits
x_ucl = x_double_bar + A2 * r_bar
x_lcl = x_double_bar - A2 * r_bar
r_ucl = D4 * r_bar
r_lcl = D3 * r_bar
subgroups = np.arange(1, n_subgroups + 1)
# Styling
sns.set_theme(style="whitegrid")
fig, axes = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# X-bar chart
sns.lineplot(x=subgroups, y=x_bars, marker="o", ax=axes[0], color="#1565C0")
axes[0].axhline(x_double_bar, color="green", label="Center Line")
axes[0].axhline(x_ucl, color="red", linestyle="--", label="UCL/LCL")
axes[0].axhline(x_lcl, color="red", linestyle="--")
axes[0].set_title("X-bar Chart")
axes[0].set_ylabel("Subgroup Mean")
axes[0].legend(loc="best")
# R chart
sns.lineplot(x=subgroups, y=ranges, marker="o", ax=axes[1], color="#1565C0")
axes[1].axhline(r_bar, color="green", label="Center Line")
axes[1].axhline(r_ucl, color="red", linestyle="--", label="UCL/LCL")
axes[1].axhline(r_lcl, color="red", linestyle="--")
axes[1].set_title("R Chart")
axes[1].set_xlabel("Subgroup")
axes[1].set_ylabel("Range")
axes[1].legend(loc="best")
plt.suptitle("X-bar and R Control Charts", y=1.02)
plt.tight_layout()
plt.show()Using either qcc (SPC-focused) or ggplot2 (fully custom).
Option 1: qcc package
library(qcc)
# sample data: 20 subgroups of 5 measurements each
set.seed(42)
data_matrix <- matrix(rnorm(100, mean = 25, sd = 0.02), ncol = 5)
# X-bar and R chart
qcc(data_matrix, type = "xbar", title = "X-bar Chart")
qcc(data_matrix, type = "R", title = "R Chart")
# individual and Moving Range chart
individual_data <- rnorm(25, mean = 25, sd = 0.02)
qcc(individual_data, type = "xbar.one", title = "I Chart")
# p-chart
defects <- rbinom(20, size = 100, prob = 0.05)
qcc(defects, type = "p", sizes = rep(100, 20), title = "p-Chart")Option 2: ggplot2
library(tidyverse)
# sample data: 20 subgroups of 5 measurements each
set.seed(42)
n_subgroups <- 20
subgroup_size <- 5
data_matrix <- matrix(rnorm(n_subgroups * subgroup_size, mean = 25, sd = 0.02), ncol = subgroup_size)
# subgroup stats
x_bars <- rowMeans(data_matrix)
ranges <- apply(data_matrix, 1, function(x) max(x) - min(x))
x_double_bar <- mean(x_bars)
r_bar <- mean(ranges)
# SPC constants for n=5
A2 <- 0.577
D3 <- 0.0
D4 <- 2.115
# control limits
x_ucl <- x_double_bar + A2 * r_bar
x_lcl <- x_double_bar - A2 * r_bar
r_ucl <- D4 * r_bar
r_lcl <- D3 * r_bar
df_x <- data.frame(subgroup = 1:n_subgroups, value = x_bars)
df_r <- data.frame(subgroup = 1:n_subgroups, value = ranges)
# X-bar chart
ggplot(df_x, aes(x = subgroup, y = value)) +
geom_line(color = "#1565C0") +
geom_point(color = "#1565C0") +
geom_hline(yintercept = x_double_bar, color = "darkgreen") +
geom_hline(yintercept = x_ucl, linetype = "dashed", color = "red") +
geom_hline(yintercept = x_lcl, linetype = "dashed", color = "red") +
labs(title = "X-bar Chart", x = "Subgroup", y = "Subgroup Mean") +
theme_minimal()
# R chart
ggplot(df_r, aes(x = subgroup, y = value)) +
geom_line(color = "#1565C0") +
geom_point(color = "#1565C0") +
geom_hline(yintercept = r_bar, color = "darkgreen") +
geom_hline(yintercept = r_ucl, linetype = "dashed", color = "red") +
geom_hline(yintercept = r_lcl, linetype = "dashed", color = "red") +
labs(title = "R Chart", x = "Subgroup", y = "Range") +
theme_minimal()Control limits (UCL/LCL) are calculated from the data and represent the natural variation of the process. Specification limits (USL/LSL) are set by customers or engineers and represent acceptable ranges. A process can be in control but still produce out-of-spec parts.
For reliable control limits, collect at least 20-25 subgroups (or 25+ individual measurements for I-MR). The process should be in control when establishing initial limits.
Investigate the assignable cause. Check for equipment changes, material lot changes, operator differences, or environmental factors. Document findings and take corrective action. Do NOT simply remove the point without understanding why it occurred.
Use X-bar & R when you can collect multiple measurements per time period (subgroups). Use I-MR when each time period yields only one measurement, or when measurements are expensive/destructive and subgrouping is not practical.