Choosing a control chart¶
This page is task-oriented: match your data to the right chart, and override the default when you know better. mfgQC infers a variables chart from your subgroup size, but it never guesses for attribute (count) data — and it never silently switches a chart you asked for. The rules below are exactly what the code does.
If you are new to the load → spec → analysis flow, start with the
Quickstart; the reference page Control charts
documents the limit formulas and constants.
How mfgQC picks a chart when you don't¶
Call control_chart() with no kind= and mfgQC chooses a variables chart from
the subgroup sizes in your QCData. The rule is purely a function of subgroup size:
| Subgroup size | Inferred chart | kind recorded |
|---|---|---|
| every subgroup is size 1 | Individuals + Moving Range | i_mr |
| \(2 \le n \le 10\) | \(\bar{X}\)–R | xbar_r |
| \(n > 10\) | \(\bar{X}\)–S | xbar_s |
The exact thresholds, from the code
The inference is i_mr if all subgroups are size 1; otherwise xbar_r
when the (constant) subgroup size \(n \le 10\) and xbar_s when \(n > 10\). The
boundary is inclusive at 10: \(n=10\) infers \(\bar{X}\)–R, \(n=11\) infers
\(\bar{X}\)–S. The rationale is statistical — the range is an efficient spread
estimator for small subgroups, but loses efficiency as \(n\) grows, so the
sample standard deviation takes over once subgroups get large.
The inferred choice is recorded in the result and in provenance (the title reads
(inferred)), so an audit can always see that mfgQC picked the chart rather than
you. Inference requires a constant subgroup size; if sizes vary, \(\bar{X}\) charts
raise rather than average over ragged groups (the individuals path is the exception).
Control Chart: xbar_r (inferred); rules=nelson
==============================================
Xbar: CL=1.4992 UCL=1.6475 LCL=1.3509
R: CL=0.25705 UCL=0.5434 LCL=0
Out-of-control signals: none (process in control)
Assumption checks:
[PASS] independence (lag-1 autocorrelation): r=0.193, p=0.387; n=20 [low power]
Decision table: data situation → chart → call¶
| Your data | Chart | kind= |
Call |
|---|---|---|---|
| Continuous, one reading at a time | Individuals + Moving Range | i_mr (or i) |
qc.control_chart(kind="i_mr") |
| Continuous, rational subgroups, \(2 \le n \le 10\) | \(\bar{X}\)–R | xbar_r |
qc.control_chart() (inferred) |
| Continuous, rational subgroups, \(n > 10\) | \(\bar{X}\)–S | xbar_s |
qc.control_chart() (inferred) |
| Pass/fail counts, constant sample size | np-chart (number defective) | np |
qc.control_chart(kind="np", n=100) |
| Pass/fail counts, varying sample size | p-chart (proportion defective) | p |
qc.control_chart(kind="p", n="inspected") |
| Defect counts, constant area of opportunity | c-chart (defects per unit) | c |
qc.control_chart(kind="c") |
| Defect counts, varying area of opportunity | u-chart (defects per unit area) | u |
qc.control_chart(kind="u", n="area") |
| Small sustained shift you need to catch fast | EWMA | — | qc.ewma_chart(lam=0.1, L=2.7) |
| Small sustained shift, accumulate evidence | CUSUM | — | qc.cusum_chart(k=0.5, h=5) |
| Many part numbers on one chart | Short-run (standardized) | — | qc.short_run_chart(by="part", target=...) |
EWMA, CUSUM, and short-run are their own methods
EWMA, CUSUM, and the standardized short-run chart are not kind= values of
control_chart() — they are separate methods (ewma_chart, cusum_chart,
short_run_chart) because they take their own parameters (smoothing \(\lambda\)
and limit width \(L\); the reference value \(k\) and decision interval \(h\); the
part-number column). Reach for EWMA or CUSUM when a Shewhart chart is too slow
to detect a small, sustained drift — a Shewhart chart reacts only to the
current point, while these accumulate. See the small-shift note in
Control charts.
Overriding the inference¶
Pass kind= to force a specific chart. The complete set of valid strings is:
kind= |
Chart |
|---|---|
"i_mr" (alias "i") |
Individuals + Moving Range |
"xbar_r" |
\(\bar{X}\)–R |
"xbar_s" |
\(\bar{X}\)–S |
"p" |
proportion defective |
"np" |
number defective |
"c" |
count of defects |
"u" |
defects per unit |
Any other value raises ValueError. The override is honored exactly — mfgQC will
not swap an explicitly requested kind for a "better" one. When you specify a
kind the result title reads (specified) instead of (inferred).
Attribute charts are always explicit — never inferred
p, np, c, and u are only ever produced when you ask for them by name.
mfgQC infers variables charts from subgroup size, but it cannot tell from the
numbers alone whether a column of integers is a measurement (variables) or a
count of defectives/defects (attribute) — 7 could be a thickness reading or
seven rejects. The choice of attribute family also encodes a model assumption
(binomial for p/np, Poisson for c/u) that only you know. So mfgQC requires you
to declare it. For p, np (constant-\(n\) only), and u you supply the sample size
via n= — a column name for per-point sizes (giving stepped limits when they
vary), a constant int, or None to fall back to a size role.
Override worked example: force I-MR¶
Even with subgrouped continuous data you may want individuals (e.g. you don't trust
the subgrouping). Pass kind="i_mr" on a plain measure column and each row is
treated as its own size-1 subgroup:
import numpy as np, pandas as pd, mfgqc
rng = np.random.default_rng(3)
ind = pd.DataFrame({"thickness": np.round(rng.normal(12.0, 0.4, size=30), 3)})
qc = mfgqc.load(ind, measure="thickness")
print(qc.control_chart(kind="i_mr"))
Control Chart: i_mr (specified); rules=nelson
=============================================
Individual: CL=12.023 UCL=13.35 LCL=10.697
MR: CL=0.49872 UCL=1.6293 LCL=0
Out-of-control signals: 2
point 2 (dispersion): nelson_1 - one point beyond control limits
point 10 (dispersion): nelson_1 - one point beyond control limits
Assumption checks:
[PASS] independence (lag-1 autocorrelation): r=-0.144, p=0.43; n=30
The moving-range (dispersion) panel is checked independently, so a point whose moving range blows up is flagged even when the individual value stays inside its own limits.
Attribute chart example¶
A p-chart for fraction defective: name the defective-count column as the
measure and pass the inspected sample size with n=.
defects = pd.DataFrame({
"defectives": [3, 5, 2, 4, 6, 1, 3, 5, 8, 2, 4, 3, 7, 2, 5],
"inspected": [100] * 15,
})
qc = mfgqc.load(defects, measure="defectives")
print(qc.control_chart(kind="p", n="inspected"))
Control Chart: p (specified); rules=nelson
==========================================
Proportion (p): CL=0.04 UCL=0.098788 LCL=0
Out-of-control signals: none (process in control)
Assumption checks:
[PASS] dispersion (chi-square dispersion): dispersion ratio 1.04, p=0.407; n=15 [low power]
A c-chart for total defects per inspection unit needs no n= (constant area of
opportunity):
cdf = pd.DataFrame({"flaws": [7, 3, 5, 9, 4, 6, 2, 8, 5, 14, 3, 6, 4, 5, 7]})
qc = mfgqc.load(cdf, measure="flaws")
print(qc.control_chart(kind="c"))
Control Chart: c (specified); rules=nelson
==========================================
Count (c): CL=5.8667 UCL=13.133 LCL=0
Out-of-control signals: 1
point 10 (location): nelson_1 - one point beyond 3 sigma
Assumption checks:
[PASS] dispersion (chi-square dispersion): dispersion ratio 1.51, p=0.0993; n=15 [low power]
What the chart checks for you¶
Every chart does two things beyond drawing limits:
- It reports run-rule violations. The default ruleset is
rules="nelson"(passrules="western_electric"for the WE zone tests). Each signal lists the point, the panel (location or dispersion), and the rule that fired — see Run rules for the full rule set and what each one means. Signals are reported, never auto-removed. - It checks independence. Variables charts run a lag-1 autocorrelation test and
attach the result as an assumption check (the
[PASS]/[FAIL] independenceline above). Control-chart limits assume successive points are independent; if they are autocorrelated, the limits are too tight and you will see false alarms. mfgQC tells you — it does not silently widen the limits.
Before you judge capability¶
Stability first, capability second
A capability index (\(C_p\), \(C_{pk}\), \(P_{pk}\)) only means something for a process that is in statistical control. Establish stability with the control chart — confirm there are no run-rule signals and that independence holds — before you compute capability on the same data. Computing capability on an out-of-control process produces a number that does not predict future performance. Run the control chart first; only once it is clean does the capability study describe a stable process.