Capability¶
Normal-theory process capability answers one question: given the spec limits, how much of the tolerance does the process consume? mfgQC reports the full index family — \(C_p\), \(C_{pk}\) (with \(C_{pu}\)/\(C_{pl}\)), \(P_p\), \(P_{pk}\), and \(C_{pm}\) — names the sigma estimator it used, attaches confidence intervals because small-sample point estimates are overconfident, and checks the assumptions that make the numbers meaningful in the first place.
This page pins every formula and estimator to mfgqc/capability.py. The default
method="normal" never transforms your data; for skewed processes see
Non-normal capability.
qc = mfgqc.load(df, measure="width", subgroup="lot", subgroup_size=5).spec(lower=1.0, upper=2.0, target=1.5)
cap = qc.capability()
1. The formulas mfgQC computes¶
Let \(\mu\) be the sample mean, \(\hat\sigma_{\text{within}}\) the short-term (within-subgroup) sigma, \(\hat\sigma_{\text{overall}}\) the long-term (ordinary sample) sigma, and \(T\) the target. With lower/upper spec limits \(\text{LSL}\)/\(\text{USL}\):
Within-subgroup indices (_indices, computed at \(\hat\sigma_{\text{within}}\)):
Overall (performance) indices — the same formulas evaluated at \(\hat\sigma_{\text{overall}}\):
Taguchi index \(C_{pm}\) — a two-sided index that penalizes being off-target, built on the overall sigma about the target:
Which indices appear depends on the spec you set
\(C_p\), \(P_p\), and \(C_{pm}\) require both limits. With only one limit set, mfgQC
reports the relevant one-sided index (\(C_{pu}\) or \(C_{pl}\)) and folds it straight
into \(C_{pk}\); the two-sided indices come back n/a. \(C_{pm}\) additionally requires
target= in .spec(...). This mirrors _indices returning None for any index
whose limit is absent.
2. The sigma estimator mfgQC selects¶
The within/overall distinction is the heart of capability and the reason mfgQC reports both families instead of one number:
- Within-subgroup (\(C_p\)/\(C_{pk}\)) estimates the process's inherent short-term spread — the variation between consecutive parts, with shift and drift between subgroups filtered out. It answers "what is this process capable of?"
- Overall (\(P_p\)/\(P_{pk}\)) uses the ordinary sample standard deviation across all data, so it captures everything — short-term noise plus every shift, drift, and tool change over the study. It answers "what did the customer actually receive?"
When \(C_{pk} \gg P_{pk}\), your process is stable in the short run but wandering over time — a hunt-for-special-causes signal, not a capable process.
_within_sigma picks the within estimator from the subgroup structure and records the
choice in sigma_used:
| Subgroup structure | Estimator | sigma_used label |
|---|---|---|
| Equal subgroups, size \(n \ge 2\) | \(\hat\sigma_{\text{within}} = \dfrac{\bar R}{d_2(n)}\) | within (R-bar/d2) |
| Individuals (\(n = 1\)) | \(\hat\sigma_{\text{within}} = \dfrac{\overline{MR}}{d_2(2)}\) | within (MR-bar/d2) |
| Unequal subgroup sizes | pooled within-subgroup SD (below) | within (pooled) |
| No usable subgroups | falls back to overall | overall |
where \(\bar R\) is the mean subgroup range, \(\overline{MR}\) the mean moving range of the
individuals series, and \(d_2\) is the bias-correction constant from
mfgqc/constants.py (e.g. \(d_2(2)=1.128\), \(d_2(5)=2.326\); Montgomery, Appendix VI). The
pooled estimator is the usual degrees-of-freedom-weighted root mean square of the
per-subgroup variances:
over subgroups \(j\) with \(n_j \ge 2\).
\(P_p\)/\(P_{pk}\) always use the overall sample SD
Regardless of subgroup structure, the performance indices are computed at
\(\hat\sigma_{\text{overall}} = s\) (values.std(ddof=1)). They never use a
within estimator. The within and overall families are reported side by side and
never conflated — this is correctness watch-list item #1 in the source.
The result object carries sigma_within, sigma_overall, and the string sigma_used
so a report builder can show exactly which estimator drove \(C_{pk}\). Consume these from
summary()/to_dict(), never by parsing the report text.
3. Confidence intervals¶
Small-\(n\) capability point estimates are biased toward looking better than the
process is. mfgQC reports normal-theory CIs (default 95%, alpha=0.05) so you read a
\(C_{pk}\) as a range, not a false-precision decimal. From _capability_cis:
\(C_p\) — exact \(\chi^2\) interval (Montgomery eq. 8.19):
\(C_{pk}\) — large-sample normal approximation (Montgomery eq. 8.21): the half-width factor is
When CIs are omitted
CIs require \(n \ge 2\), and the \(C_{pk}\) interval additionally requires
\(\widehat{C_{pk}} \neq 0\). Confidence intervals are computed for the normal method
only. Non-normal methods (boxcox, clements, johnson) return None and the
report prints CI: n/a (non-normal method) rather than fabricating a normal-theory
interval — see Non-normal capability.
4. Assumptions — and which ones mfgQC checks¶
| Assumption | Why it matters | Checked by mfgQC? |
|---|---|---|
| Normality | \(C_p\)/\(C_{pk}\) map to fraction-defective only if the data are normal | Yes — Anderson-Darling, reported |
| Process in statistical control | a capability index on an unstable process is meaningless | No — you must establish it first with a control chart |
| Adequate subgroup count | \(\bar R / d_2\) needs enough subgroups to be stable | Yes — flags fewer than 25 |
Normality (checked, reported). mfgQC runs an Anderson-Darling test
(check_normality) and reports the verdict at \(\alpha = 0.05\). The binary
passed/failed comes from the direct AD test; alongside it mfgQC reports an
est. Cpk impact magnitude — the relative shift in \(C_{pk}\) between the normal method
and an auto-fit non-normal method (_cpk_shift). That context tells you whether the
non-normality actually moves the number you care about, but it never flips the
verdict: grossly non-normal data fail even if the coincidental \(C_{pk}\) shift is
small. If it fails, mfgQC recommends a non-normal method — it does not silently switch.
See Reading the assumption report.
Establish statistical control before capability
mfgQC does not test for statistical control inside capability(). A capability
index computed on an out-of-control process is not interpretable — the within-sigma
estimate is contaminated and the fraction-defective projection is fiction. Run a
control chart first, confirm there are no out-of-control
signals, and only then compute capability.
Subgroup sufficiency (checked, reported). When a within estimator is in use, mfgQC counts the subgroups and fails the check below 25, with the recommendation "Only N subgroups; >=25 recommended for a stable within-sigma estimate." It still computes the index — it warns, it does not refuse.
5. Worked example¶
Twenty subgroups of five, spec \([1.0, 2.0]\) with target \(1.5\):
import numpy as np, pandas as pd, mfgqc
rng = np.random.default_rng(7)
df = pd.DataFrame({
"width": np.round(rng.normal(1.52, 0.12, size=100), 3),
"lot": np.repeat(np.arange(1, 21), 5), # 20 subgroups of 5
})
qc = (mfgqc.load(df, measure="width", subgroup="lot", subgroup_size=5)
.spec(lower=1.0, upper=2.0, target=1.5))
cap = qc.capability()
print(cap.report())
Process Capability (method=normal)
==================================
n = 100 mean = 1.4992
sigma (within) = 0.1105
sigma (overall) = 0.10556
Cp/Cpk sigma = within (R-bar/d2)
Cp = 1.508 95% CI (1.3, 1.72)
Cpk = 1.506 95% CI (1.29, 1.73) (Cpu=1.51, Cpl=1.506)
Pp = 1.579 Ppk = 1.576 (Ppu=1.581, Ppl=1.576)
Cpm = 1.579
Assumption checks:
[PASS] normality (Anderson-Darling): AD=0.301, p=0.572; est. Cpk impact 5.0%; n=100
[FAIL] subgroup_sufficiency (subgroup count >= 25): subgroup count 20; n=20
Recommendations:
- Only 20 subgroups; >=25 recommended for a stable within-sigma estimate.
Reading it:
- The estimator is named. \(C_p\)/\(C_{pk}\) used
within (R-bar/d2)— equal subgroups of 5, so \(\hat\sigma_{\text{within}} = \bar R / d_2(5)\) with \(d_2(5)=2.326\). - Both families show. \(C_{pk}=1.506\) (short-term) vs \(P_{pk}=1.576\) (long-term). They are close here because the process is stable.
- CIs are wide. \(C_{pk}=1.506\) with a 95% CI of \((1.29,\,1.73)\) — even with 100 measurements the index is only pinned to one decimal.
- The guardrails fired. Normality passes; subgroup count (20) is flagged below 25. mfgQC tells you and recommends a fix; it does not alter the calculation.
The flat dashboard dict is available without parsing text:
cap.summary()
# {'method': 'normal', 'n': 100, 'mean': 1.4992..., 'sigma_within': 0.11051...,
# 'sigma_overall': 0.10556..., 'Cp': 1.50813..., 'Cp_CI_low': 1.29824...,
# 'Cp_CI_high': 1.71768..., 'Cpk': 1.50581..., 'Cpk_CI_low': 1.28613...,
# 'Cpk_CI_high': 1.72549..., 'Pp': 1.57883..., 'Ppk': 1.57640...,
# 'Cpm': 1.57879..., 'confidence': 95, 'normality_passed': True}
Other subgroup structures¶
The same call adapts the estimator to the data — verified output:
INDIVIDUALS (subgroup_size=1) sigma_used: within (MR-bar/d2)
sigma_within=0.55286 sigma_overall=0.57708 Cpk=1.1884 Ppk=1.1385
UNEQUAL subgroups sigma_used: within (pooled)
sigma_within=0.32545 sigma_overall=0.30076
Note that in the individuals case the within and overall sigmas differ even though both
summarize the same series: within (MR-bar/d2) uses the moving range
(\(\overline{MR}/d_2(2)\), \(d_2(2)=1.128\)), filtering point-to-point variation only.
6. Source standard¶
mfgQC's capability indices, the \(\bar R/d_2\) and \(\overline{MR}/d_2\) within-sigma estimators, the \(d_2\) constants, and both confidence-interval formulas (eq. 8.19 for \(C_p\), eq. 8.21 for \(C_{pk}\)) are pinned to Montgomery, Introduction to Statistical Quality Control — mfgQC's primary build oracle for SPC and capability. See the Bibliography.
See also¶
- Non-normal capability — Box-Cox, Clements/percentile, and
Johnson methods for skewed processes (where CIs read
n/a). - Control charts — establish statistical control before you compute capability.
- Reading the assumption report — what the normality and subgroup-sufficiency checks mean and how to act on them.
- API reference —
CapabilityResultfields,summary(),to_dict(),view(). - Bibliography — the cited sources.