During our site visit, Arani was unable to share proprietary quality data due to the competitive sensitivity of their industry. However, demonstrating statistical process control (SPC) implementation requires data to construct and interpret control charts.
We created realistic simulated data based on:
This approach allows us to demonstrate how SPC tools would function in Arani’s operational environment.
The X-bar and R chart is the most appropriate control chart for monitoring Arani’s LED lumen output for the following reasons:
| Arani’s Reality | Our Design Choice |
|---|---|
| Tests 3–4 products per day | Rational subgroups of n = 4 |
| Lumen output is the critical performance metric | Continuous measurement data |
| Subgroup size is small (n < 10) | R chart preferred over S chart |
| Need to monitor both process average and variation | X-bar tracks mean; R tracks spread |
Based on industry standards and information from the site visit:
| Parameter | Value | Rationale |
|---|---|---|
| Target | 1000 lumens | Industry standard for commercial LED |
| Tolerance | ±5% (±50 lumens) | Per Arani’s stated policy |
| LSL / USL | 950 / 1050 lumens | Target ± tolerance |
| Subgroup size (n) | 4 | Daily tests (3–4 products/day) |
| Number of subgroups (k) | 25 | Approximately 5 weeks of data |
| Process σ | 18 lumens | Realistic for uncontrolled process |
To demonstrate the chart’s ability to detect process shifts:
This simulates a supplier batch issue, which is realistic given Arani’s 3–6 month offshore lead times and their acknowledged challenges with supplier quality consistency.
# Parameters
n <- 4
k_total <- 25
k_in_control <- 18
k_shifted <- 7
target <- 1000
sigma <- 18
shift <- -25
LSL <- 950
USL <- 1050
# Generate Phase 1: In control
phase1_matrix <- matrix(
rnorm(n * k_in_control, mean = target, sd = sigma),
nrow = k_in_control,
ncol = n,
byrow = TRUE
)
# Generate Phase 2: Process shift (supplier issue)
phase2_matrix <- matrix(
rnorm(n * k_shifted, mean = target + shift, sd = sigma),
nrow = k_shifted,
ncol = n,
byrow = TRUE
)
# Combine data
lumen_data <- rbind(phase1_matrix, phase2_matrix)
colnames(lumen_data) <- paste("Unit", 1:n)
rownames(lumen_data) <- paste("Day", 1:k_total)# Calculate subgroup statistics
x_bar <- apply(lumen_data, 1, mean)
R <- apply(lumen_data, 1, function(x) max(x) - min(x))
# Summary table
summary_df <- data.frame(
Day = 1:k_total,
Unit_1 = round(lumen_data[, 1], 1),
Unit_2 = round(lumen_data[, 2], 1),
Unit_3 = round(lumen_data[, 3], 1),
Unit_4 = round(lumen_data[, 4], 1),
X_bar = round(x_bar, 2),
R = round(R, 2),
Phase = c(rep("In Control", k_in_control), rep("Shifted", k_shifted))
)
knitr::kable(summary_df, caption = "Simulated Lumen Output Data with Subgroup Statistics")| Day | Unit_1 | Unit_2 | Unit_3 | Unit_4 | X_bar | R | Phase | |
|---|---|---|---|---|---|---|---|---|
| Day 1 | 1 | 969.1 | 966.4 | 1018.2 | 1006.3 | 990.02 | 51.85 | In Control |
| Day 2 | 2 | 978.3 | 1027.4 | 1030.1 | 1015.5 | 1012.82 | 51.79 | In Control |
| Day 3 | 3 | 1005.7 | 1011.2 | 1026.3 | 987.0 | 1007.56 | 39.36 | In Control |
| Day 4 | 4 | 1017.2 | 981.3 | 991.0 | 1003.0 | 998.14 | 35.85 | In Control |
| Day 5 | 5 | 992.1 | 956.6 | 1012.0 | 1019.7 | 995.10 | 63.06 | In Control |
| Day 6 | 6 | 1006.0 | 1025.7 | 1006.1 | 1009.6 | 1011.87 | 19.70 | In Control |
| Day 7 | 7 | 994.8 | 1023.8 | 1010.0 | 974.3 | 1000.71 | 49.54 | In Control |
| Day 8 | 8 | 1023.2 | 1009.9 | 979.8 | 963.0 | 993.94 | 60.20 | In Control |
| Day 9 | 9 | 956.0 | 996.1 | 982.5 | 1013.6 | 987.06 | 57.53 | In Control |
| Day 10 | 10 | 1000.9 | 971.0 | 1016.6 | 1026.5 | 1003.73 | 55.55 | In Control |
| Day 11 | 11 | 1018.1 | 1028.0 | 1021.1 | 1041.2 | 1027.10 | 23.05 | In Control |
| Day 12 | 12 | 993.3 | 991.9 | 992.0 | 1006.9 | 996.03 | 15.01 | In Control |
| Day 13 | 13 | 997.4 | 982.3 | 1012.6 | 1013.1 | 1001.33 | 30.84 | In Control |
| Day 14 | 14 | 1014.9 | 982.1 | 994.2 | 984.7 | 993.98 | 32.80 | In Control |
| Day 15 | 15 | 995.9 | 1005.3 | 1020.4 | 1017.0 | 1009.68 | 24.49 | In Control |
| Day 16 | 16 | 1027.0 | 956.1 | 996.3 | 993.8 | 993.29 | 70.95 | In Control |
| Day 17 | 17 | 973.8 | 996.5 | 1001.2 | 1003.5 | 993.76 | 29.72 | In Control |
| Day 18 | 18 | 1003.3 | 999.9 | 990.3 | 966.6 | 990.01 | 36.64 | In Control |
| Day 19 | 19 | 968.8 | 1011.8 | 996.3 | 997.4 | 993.58 | 43.00 | Shifted |
| Day 20 | 20 | 969.6 | 961.6 | 977.8 | 1017.1 | 981.52 | 55.55 | Shifted |
| Day 21 | 21 | 963.7 | 979.6 | 1019.1 | 999.9 | 990.56 | 55.33 | Shifted |
| Day 22 | 22 | 967.4 | 1006.4 | 960.8 | 951.9 | 971.66 | 54.50 | Shifted |
| Day 23 | 23 | 956.2 | 989.8 | 963.1 | 963.8 | 968.24 | 33.59 | Shifted |
| Day 24 | 24 | 973.4 | 997.8 | 976.1 | 949.8 | 974.29 | 48.09 | Shifted |
| Day 25 | 25 | 963.8 | 977.3 | 963.7 | 995.8 | 975.15 | 32.10 | Shifted |
xbar_chart <- qcc(lumen_data, type = "xbar",
title = "X-bar Chart: LED Lumen Output (Arani QC)",
xlab = "Day (Subgroup)",
ylab = "Mean Lumen Output")
# Add specification limits
abline(h = USL, col = "orange", lty = 2, lwd = 2)
abline(h = LSL, col = "orange", lty = 2, lwd = 2)
abline(h = target, col = "purple", lty = 3, lwd = 2)
legend("topright",
legend = c("Control Limits", "Spec Limits", "Target"),
col = c("red", "orange", "purple"),
lty = c(1, 2, 3),
lwd = 2,
cex = 0.8)## UCL: 1025.63 lumens
## Center (X-double-bar): 994.44 lumens
## LCL: 963.26 lumens
r_chart <- qcc(lumen_data, type = "R",
title = "R Chart: LED Lumen Output (Arani QC)",
xlab = "Day (Subgroup)",
ylab = "Range")Using only in-control data (Phase 1) to establish baseline capability:
xbar_phase1 <- qcc(phase1_matrix, type = "xbar", plot = FALSE)
process.capability(xbar_phase1, spec.limits = c(LSL, USL), target = target)##
## Process Capability Analysis
##
## Call:
## process.capability(object = xbar_phase1, spec.limits = c(LSL, USL), target = target)
##
## Number of obs = 72 Target = 1000
## Center = 1000 LSL = 950
## StdDev = 20.18 USL = 1050
##
## Capability indices:
##
## Value 2.5% 97.5%
## Cp 0.8259 0.6902 0.9613
## Cp_l 0.8315 0.6998 0.9632
## Cp_u 0.8203 0.6899 0.9506
## Cp_k 0.8203 0.6649 0.9756
## Cpm 0.8258 0.6911 0.9602
##
## Exp<LSL 0.63% Obs<LSL 0%
## Exp>USL 0.69% Obs>USL 0%
# X-bar chart violations
xbar_violations <- which(x_bar < xbar_chart$limits[1, 1] |
x_bar > xbar_chart$limits[1, 2])
cat("X-bar Chart - Points beyond control limits: ")## X-bar Chart - Points beyond control limits:
if(length(xbar_violations) > 0) {
cat("Days", paste(xbar_violations, collapse = ", "), "\n")
} else {
cat("None\n")
}## Days 11
# R chart violations
r_violations <- which(R < r_chart$limits[1, 1] | R > r_chart$limits[1, 2])
cat("R Chart - Points beyond control limits: ")## R Chart - Points beyond control limits:
if(length(r_violations) > 0) {
cat("Days", paste(r_violations, collapse = ", "), "\n")
} else {
cat("None\n")
}## None
Key Observations:
Days 1–18 (In Control): Points fluctuate randomly around the center line (~1000 lumens), indicating a stable process.
Days 19–25 (Shifted): A clear downward trend is visible, with points clustering below the center line. This pattern indicates a mean shift — exactly what we would expect from a supplier batch issue.
R Chart Stability: The R chart remains relatively stable throughout, confirming that the issue is a shift in the process mean, not an increase in variability.
Practical Implication: With control charts in place, Arani would have detected this supplier issue by Day 21–22 at the latest, allowing them to quarantine affected inventory and contact the supplier before many defective units entered the market.
The p-chart is the most appropriate control chart for monitoring Arani’s incoming shipment quality:
| Arani’s Reality | Our Design Choice |
|---|---|
| Uses pass/fail acceptance sampling | Attribute data (defective vs. conforming) |
| Inspects samples from incoming shipments | Each batch = one inspection opportunity |
| Sample size n = 125 (AQL Level II) | Constant sample size per batch |
| ~50 batches per year | Simulated 30 batches (~7 months) |
| Parameter | Value | Rationale |
|---|---|---|
| Sample size (n) | 125 per batch | AQL Level II (ANSI/ISO 2859-1) |
| Normal defect rate (p) | 0.02 (2%) | Industry average per report Section 3.1 |
| Number of batches | 30 | ~7–8 months of shipments |
This simulates a realistic scenario where a supplier’s quality gradually worsens, perhaps due to cost-cutting measures, equipment degradation, or personnel changes.
# Parameters
k_batches <- 30
n_sample <- 125
p_normal <- 0.02
p_shifted <- 0.06
k_normal <- 22
k_bad <- 8
# Generate Phase 1: Normal supplier quality
defects_phase1 <- rbinom(k_normal, size = n_sample, prob = p_normal)
# Generate Phase 2: Supplier quality deterioration
defects_phase2 <- rbinom(k_bad, size = n_sample, prob = p_shifted)
# Combine
defects <- c(defects_phase1, defects_phase2)
sample_sizes <- rep(n_sample, k_batches)
p_hat <- defects / sample_sizesp_chart_df <- data.frame(
Batch = 1:k_batches,
Sample_Size = sample_sizes,
Defectives = defects,
Proportion = round(p_hat, 4),
Phase = c(rep("Normal", k_normal), rep("Deteriorated", k_bad))
)
knitr::kable(p_chart_df, caption = "Simulated Incoming Inspection Data")| Batch | Sample_Size | Defectives | Proportion | Phase |
|---|---|---|---|---|
| 1 | 125 | 4 | 0.032 | Normal |
| 2 | 125 | 2 | 0.016 | Normal |
| 3 | 125 | 3 | 0.024 | Normal |
| 4 | 125 | 3 | 0.024 | Normal |
| 5 | 125 | 2 | 0.016 | Normal |
| 6 | 125 | 1 | 0.008 | Normal |
| 7 | 125 | 5 | 0.040 | Normal |
| 8 | 125 | 1 | 0.008 | Normal |
| 9 | 125 | 4 | 0.032 | Normal |
| 10 | 125 | 1 | 0.008 | Normal |
| 11 | 125 | 1 | 0.008 | Normal |
| 12 | 125 | 0 | 0.000 | Normal |
| 13 | 125 | 2 | 0.016 | Normal |
| 14 | 125 | 1 | 0.008 | Normal |
| 15 | 125 | 3 | 0.024 | Normal |
| 16 | 125 | 3 | 0.024 | Normal |
| 17 | 125 | 6 | 0.048 | Normal |
| 18 | 125 | 2 | 0.016 | Normal |
| 19 | 125 | 4 | 0.032 | Normal |
| 20 | 125 | 1 | 0.008 | Normal |
| 21 | 125 | 0 | 0.000 | Normal |
| 22 | 125 | 1 | 0.008 | Normal |
| 23 | 125 | 7 | 0.056 | Deteriorated |
| 24 | 125 | 9 | 0.072 | Deteriorated |
| 25 | 125 | 9 | 0.072 | Deteriorated |
| 26 | 125 | 11 | 0.088 | Deteriorated |
| 27 | 125 | 9 | 0.072 | Deteriorated |
| 28 | 125 | 5 | 0.040 | Deteriorated |
| 29 | 125 | 12 | 0.096 | Deteriorated |
| 30 | 125 | 7 | 0.056 | Deteriorated |
p_chart <- qcc(defects,
sizes = sample_sizes,
type = "p",
title = "p-Chart: Incoming Batch Defect Rate (Arani QC)",
xlab = "Batch Number",
ylab = "Proportion Defective")
# Add target reference line
abline(h = 0.02, col = "purple", lty = 3, lwd = 2)
legend("topright",
legend = c("Control Limits", "Target p = 0.02"),
col = c("red", "purple"),
lty = c(1, 3),
lwd = 2,
cex = 0.8)## Batches beyond UCL:
if(length(p_violations) > 0) {
cat("Batches", paste(p_violations, collapse = ", "), "\n")
} else {
cat("None\n")
}## Batches 26, 29
## Overall:
## Total units inspected: 3750
## Total defectives: 119
## Overall proportion defective: 0.0317
## Phase 1 (Normal, Batches 1-22):
## Average proportion defective: 0.0182
## Phase 2 (Deteriorated, Batches 23-30):
## Average proportion defective: 0.069
Key Observations:
Batches 1–22 (Normal): Points fluctuate randomly around p̄ ≈ 0.02, within control limits. This represents normal supplier variation.
Batches 23–30 (Deteriorated): Multiple points exceed the UCL, signaling that the process is out of control. The supplier’s defect rate has increased significantly.
Practical Implication: Without a p-chart, Arani’s current pass/fail system would continue accepting batches even as quality deteriorates — each batch might still pass the acceptance number (c = 7), but the trend would go unnoticed. The p-chart provides early warning of supplier problems, allowing Arani to:
┌─────────────────────┐
│ What data type? │
└──────────┬──────────┘
│
┌────────────────┴────────────────┐
▼ ▼
Continuous Attribute
(measurements) (pass/fail)
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Subgroup size? │ │ Tracking what? │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
n = 4 Proportion
(daily tests) defective
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ X-bar and R │ │ p-chart │
│ Chart │ │ │
└─────────────────┘ └─────────────────┘
| Chart | Purpose | Detects |
|---|---|---|
| X-bar Chart | Monitor process mean | Shifts in average quality |
| R Chart | Monitor process variation | Increases in inconsistency |
| p-Chart | Monitor defect rate | Supplier quality deterioration |
Together, these charts would transform Arani’s quality system from reactive (catching defects after they occur) to proactive (detecting problems before many defective units are produced or shipped).
# Export X-bar/R data
write.csv(data.frame(
Day = 1:k_total,
lumen_data,
X_bar = round(x_bar, 2),
R = round(R, 2)
), "arani_xbar_r_data.csv", row.names = FALSE)
# Export p-chart data
write.csv(p_chart_df, "arani_p_chart_data.csv", row.names = FALSE)
cat("Data exported to:\n")## Data exported to:
## - arani_xbar_r_data.csv
## - arani_p_chart_data.csv
\[\bar{\bar{X}} = \frac{\sum_{i=1}^{k} \bar{X}_i}{k}\]
\[UCL_{\bar{X}} = \bar{\bar{X}} + A_2 \bar{R}\]
\[LCL_{\bar{X}} = \bar{\bar{X}} - A_2 \bar{R}\]
For n = 4: \(A_2 = 0.729\)
\[\bar{R} = \frac{\sum_{i=1}^{k} R_i}{k}\]
\[UCL_R = D_4 \bar{R}\]
\[LCL_R = D_3 \bar{R}\]
For n = 4: \(D_3 = 0\), \(D_4 = 2.282\)
\[\bar{p} = \frac{\sum_{i=1}^{k} D_i}{\sum_{i=1}^{k} n_i}\]
\[UCL_p = \bar{p} + 3\sqrt{\frac{\bar{p}(1-\bar{p})}{n}}\]
\[LCL_p = \bar{p} - 3\sqrt{\frac{\bar{p}(1-\bar{p})}{n}}\]
(LCL set to 0 if negative)
Report generated for MATH 427 - Statistical Quality Control, McGill University