Bayesian capability: deciding from a small sample¶
You have twelve parts off a new process and a decision to make: ship the lot or hold it? The requirement is a performance index of at least 1.33. Classical capability gives you one number with a confidence interval so wide it is hard to act on. This walkthrough takes the same twelve parts and answers the question a release meeting actually asks: what is the probability this process meets the requirement?
Every cell runs. Open it in Colab and drop in your own measurements.
What "Bayesian" means here, and why it helps¶
Most quality training teaches one meaning of the word probability: the long-run frequency of an event over many repetitions. This is the basis of the p-values, confidence intervals, and capability indices in common use. It is sound, and it answers a specific question. It is not always the question you have.
Consider a 95% confidence interval on Cpk. It is commonly read as "there is a 95% chance the true Cpk lies in this range." That reading is wrong. In the frequentist account the true Cpk is one fixed number; it either lies in the interval or it does not, and no probability attaches to it. The 95% refers to the procedure: if the study were repeated many times, 95% of the intervals constructed this way would contain the true value. The sentence "the probability this process is capable is 0.93" is not available in that framework.
The Bayesian account makes that sentence available. It treats the quantity you do not know -- the true Cpk, the true defect rate -- as uncertain, and represents that uncertainty as a probability distribution. You state what was known before the measurements (the prior), the data revise it, and the result is a posterior distribution for the quantity of interest. From the posterior you read the statement directly: given these parts, the probability that Cpk meets the requirement is 0.93. The accompanying credible interval carries the meaning the confidence interval is often mistaken for -- a 95% credible interval is a range that contains the true value with probability 0.95, given the data and the prior.
| Frequentist | Bayesian (this layer) | |
|---|---|---|
| The true Cpk is | one fixed, unknown number | an uncertain quantity, described by a distribution |
| A 95% interval is | a procedure: 95% of repeated studies would cover the true value | a range containing the true value with probability 0.95, given the data |
| The result you report | a point index and an interval | a probability that the process conforms |
| With a small sample | the interval is wide and hard to act on | the uncertainty is stated directly and can enter a decision |
| Prior knowledge (history, a qualified line) | not used | included through the prior, and recorded |
This matters in three situations common in manufacturing:
- Small runs. With a dozen parts, a confidence interval on Cpk is wide enough to be hard to act on. The posterior gives a probability of conformance that can enter a decision directly.
- Existing knowledge. A qualified sister process, a validated material, or prior production data is information about the process. The prior lets you use it; the frequentist estimate uses only the current sample.
- The question of interest. "What is the probability this lot conforms?" is a question about the unknown quantity, which is what a posterior describes. This layer answers it without translating through a p-value.
The two accounts are not in opposition. Given a prior that expresses no prior knowledge and a large sample, the posterior and the classical estimate agree. The Bayesian layer is most useful where the classical estimate is weakest: small samples, and cases where a direct probability of conformance is what the decision requires. The rest of this walkthrough applies it to a ship-or-hold decision on twelve parts.
Install it first (skip this if mfgQC is already in your environment):
!pip install mfgqc
1. The twelve parts¶
No DataFrame needed here: the Bayesian capability entry point takes a plain list of
measurements. These are twelve widths, spec limits [1.0, 2.0], target 1.5.
import numpy as np
from mfgqc.bayes import capability_from_values
y = [1.51, 1.546, 1.477, 1.403, 1.455, 1.391,
1.517, 1.671, 1.451, 1.436, 1.569, 1.553]
len(y)
12
2. Run it¶
capability_from_values fits a Normal process with a conjugate Bayesian model and returns
a result object, exactly like the rest of mfgQC. The one required extra is seed=, which
makes the Monte Carlo step reproducible: the same seed and the same data always give the
same answer.
cap = capability_from_values(y, lower=1.0, upper=2.0, target=1.5, seed=1)
print(cap.report())
Bayesian Capability (noninformative) ==================================== n = 12 mean = 1.4982 s (overall) = 0.07956 mu 95% credible interval = (1.4477, 1.5488) Pp = 2.095 Ppk point = 2.088 Ppk 95% credible interval = (1.15, 2.88) P(Ppk >= 1.33) = 0.931 +/- 0.0008 (MC) ppm point = 0 Assumption checks: [PASS] small_sample (n >= 8): n=12; n=12 [low power] [PASS] normality (Anderson-Darling): AD=0.225, p=0.768; skew 0.614; n=12 [low power]
Read the report top to bottom:
Ppk point = 2.088is the single-number estimate, the same value classical capability would give.Ppk 95% credible interval = (1.15, 2.88)is the uncertainty. Twelve parts do not pin the index down, and the posterior reports that range rather than a single decimal.P(Ppk >= 1.33) = 0.931is the headline: given these twelve parts, there is a 93% probability the true process clears the requirement.- The assumption checks run just as they do in the classical layer, flagged
[low power]because twelve points cannot test much.
3. Turn the posterior into a number you can act on¶
Two methods read straight off the posterior. prob(...) gives the probability of
clearing a threshold (with its Monte Carlo error), and interval(...) gives a credible
interval at any level you like.
prob, mc_error = cap.prob("ppk", 1.33)
print(f"P(Ppk >= 1.33) = {prob:.3f} (+/- {mc_error:.4f} Monte Carlo)")
low, high = cap.interval("ppk", 0.90)
print(f"90% credible interval for Ppk = ({low:.2f}, {high:.2f})")
P(Ppk >= 1.33) = 0.931 (+/- 0.0008 Monte Carlo) 90% credible interval for Ppk = (1.27, 2.72)
That first line is the whole point of the Bayesian layer: a probability of conformance, not a point index you have to interpret. You can wire it straight into a decision rule.
THRESHOLD = 0.90 # ship only if we are at least 90% sure the process is capable
decision = "SHIP" if prob >= THRESHOLD else "HOLD"
print(f"{prob:.0%} sure the process is capable -> {decision}")
93% sure the process is capable -> SHIP
4. Fold in what you already know¶
Suppose this is not a brand-new process. A qualified, closely related line ran at a mean
of 1.50 with a standard deviation near 0.11. That is real prior information, and throwing
it away is wasteful. A NormalPrior carries it in. The strength parameters k0 and nu0
are an equivalent prior sample size: k0=20 says "trust the prior mean about as much as
twenty parts."
from mfgqc.bayes.priors import NormalPrior
prior = NormalPrior(mu0=1.50, k0=20.0, nu0=20.0, s20=0.11 ** 2)
cap_prior = capability_from_values(y, lower=1.0, upper=2.0, target=1.5,
prior=prior, seed=1)
print(cap_prior.report())
Bayesian Capability (informative) ================================= n = 12 mean = 1.4982 s (overall) = 0.07956 mu 95% credible interval = (1.4638, 1.5349) Pp = 2.095 Ppk point = 2.088 Ppk 95% credible interval = (1.22, 2.06) P(Ppk >= 1.33) = 0.922 +/- 0.00085 (MC) ppm point = 0 prior = normal Assumption checks: [FAIL] prior_weight (prior weight w = k0/(k0+n)): prior=0.625; n=12 [PASS] prior_data_conflict (prior predictive t on ybar): prior=0.0436, p=0.966; n=12 [PASS] small_sample (n >= 8): n=12; n=12 [low power] [PASS] normality (Anderson-Darling): AD=0.225, p=0.768; skew 0.614; n=12 [low power] Recommendations: - The prior contributes 62% of the posterior; the report is prior dominated. Widen the prior or collect more data.
The credible interval tightened (the prior adds information), and two new guardrails appear:
prior_weightflags that the prior is driving 62% of the result. With only twelve parts against ak0=20prior, the prior is doing most of the work, and mfgQC says so rather than letting you present a prior-dominated number as if it were data. If you want the parts to speak louder, weaken the prior (lowerk0) or collect more.prior_data_conflictchecks whether the parts agree with the prior. Herep=0.97, so they are consistent; a small value here would be a red flag that the prior and the process disagree, and you should stop and reconcile them before trusting the number.
A prior is a recorded input, not a hidden adjustment. Its parameters, its strength, and both checks are stored in the result's provenance.
5. It is auditable¶
Like every mfgQC result, a Bayesian result carries a tamper-evident provenance digest. Two runs on the same data and seed produce the same digest; change one measurement and it changes. That is what lets a Bayesian number, prior and all, survive an audit.
digest = cap.provenance_digest()
print("verifies:", cap.verify_provenance(digest))
again = capability_from_values(y, lower=1.0, upper=2.0, target=1.5, seed=1)
print("reproducible:", again.provenance_digest() == digest)
verifies: True reproducible: True
Where to go next¶
- The Bayesian analytics reference has the formulas, the conjugate engine, and the rest of the layer: attributes (pass/fail and defect rates), comparing two processes, sample-size assurance, guardbanding for gauge error, Phase-1 monitoring, and hierarchical pooling of small groups.
- The Capability reference covers the classical $C_p$/$C_{pk}$/$P_{pk}$ layer this mirrors.
- The audit workflow shows the provenance model end to end.