system.kanoa.quality.spc.spcCheck(values, chartType, ruleSet, subgroupSize, usl, lsl, sampleSizes)
Computes SPC control limits, zone-based rule violations (Western Electric / Nelson), and - where applicable - process capability/performance indices, for one of the following chart types:
individuals- Individuals (I-MR) chart. Default. Paired with a Moving Range secondary chart.xbar- X-bar/R chart (subgroup mean + range). Requires subgroupSize 2-10. Paired with a Range secondary chart.xbar_s- X-bar/S chart (subgroup mean + sample std dev). Any subgroup size >= 2, unlike xbar (R-based), which is limited to the tabulated range n=2-10. Paired with an S secondary chart.p- Percent defective chart, variable subgroup size.np- Number defective chart, constant subgroup size.c- Number of defects chart, constant area of opportunity.u- Defects-per-unit chart, variable area of opportunity.pre_control- Pre-Control zones. Spec-limit based - no control limits, no Western Electric/Nelson rules, no capability indices; uses its own run/stop signal instead.
p/np/c/u have no secondary chart and no capability indices - there is no subgroup-internal spread statistic to chart, and no within-subgroup sigma to build Cp/Cpk from (attribute-chart limits come directly from the binomial/Poisson variance formula, not from an estimated range/std dev).
Capability is only returned for individuals/xbar/xbar_s, and only when usl and/or lsl was given: {cp, cpk, cpu, cpl, pp, ppk, ppu, ppl, mean, sigmaWithin, sigmaOverall}. cp/pp are None unless BOTH usl and lsl are given; cpk/ppk are the min() of whichever one-sided indices exist. cp/cpk use sigmaWithin (short-term, from the same R-bar/d2 or S-bar/c4 estimate the control limits are built from); pp/ppk use sigmaOverall (long-term, the plain sample std dev of every individual reading around the grand mean, ignoring subgrouping).
pre_control returns a materially different shape (zones/pairSignals/qualifyRunIndex/greenLow/greenHigh/yellowLow/yellowHigh in place of uclPoints/lclPoints/violations/secondary/capability).
Parameters
values List of Float values: raw measurements for individuals/xbar/xbar_s/pre_control (flat list, subgrouped internally for xbar/xbar_s); defect/defective count per subgroup for p/u; defect/defective count per subgroup (constant sample size/area) for np/c.
chartType String: 'individuals', 'xbar', 'xbar_s', 'p', 'np', 'c', 'u', 'pre_control'. Can be None (defaults to 'individuals').
ruleSet String: 'both', 'western_electric', 'nelson'. Can be None (defaults to 'both'). Ignored for pre_control.
subgroupSize Integer: required for xbar (2-10), xbar_s (>= 2), and np (constant n). Can be None otherwise.
usl Float: upper spec limit. Required for pre_control, optional elsewhere - if given (with or without lsl) for individuals/xbar/xbar_s, one-sided capability indices (cpu/cpk, ppu/ppk) are computed. Can be None.
lsl Float: lower spec limit. Same rules as usl, mirrored. Can be None.
sampleSizes List of Integers: required for p/u - one entry per subgroup, parallel to values. Can be None otherwise.
Returns
results Dictionary
outOfControl Boolean
chartType String
ruleSet String
mean Float
sigma Float: only when control limits are constant across points (None for p/u, where they vary by sample size - use uclPoints/lclPoints instead)
ucl / lcl Float: same constant-limit caveat as sigma
uclPoints / lclPoints List of Float values: per-point limit lists, always populated (repeated scalar for constant-limit chart types; genuinely varying point by point for p/u)
usl / lsl Float: pass-through of the input spec limits
aboveUCL / belowLCL / aboveUSL / belowLSL List of Integers: violating point indices
violations Dictionary: rule name -> violating point indices
points List of Float values
secondary Dictionary: (individuals/xbar/xbar_s only) the paired Moving-Range/Range/S chart - {label, centerLine, ucl, lcl, points}
capability Dictionary: see above
Examples
The shape of result changes noticeably by chartType - constant vs. per-point control limits, whether secondary/capability are populated, and (for pre_control) an entirely different set of fields. Each example below shows the actual computed values so you can see exactly what differs.
individuals
One reading per point, no subgrouping. sigma/ucl/lcl are single constant values; secondary is the paired Moving Range chart.
values = [100.4, 98.6, 100.2, 96.0, 109.8, 102.1, 103.2]
result = system.kanoa.quality.spc.spcCheck(values, "individuals", None, None, None, None, None)
# result["points"] = [100.4, 98.6, 100.2, 96.0, 109.8, 102.1, 103.2] (unchanged - one point per reading)
# result["mean"] = 101.471
# result["sigma"] = 4.462
# result["ucl"] = 114.858
# result["lcl"] = 88.085
# result["secondary"] = {"label": "Moving Range", "centerLine": 5.033, "ucl": 16.444, "lcl": 0.0, "points": [1.8, 1.6, 4.2, 13.8, 7.7, 1.1]}
xbar (X-bar/R, subgroup size 4)
Readings are grouped into subgroups; points become subgroup averages, not raw readings. secondary is the paired Range chart instead of Moving Range.
values = [100.4, 98.6, 100.2, 96.0, 109.8, 102.1, 103.2, 97.5, 101.0, 99.8, 100.5, 98.9]
subgroupSize = 4
result = system.kanoa.quality.spc.spcCheck(values, "xbar", None, subgroupSize, None, None, None)
# subgroups formed = [[100.4, 98.6, 100.2, 96.0], [109.8, 102.1, 103.2, 97.5], [101.0, 99.8, 100.5, 98.9]]
# result["points"] = [98.8, 103.15, 100.05] (subgroup means, not raw values)
# result["mean"] = 100.667
# result["ucl"] = 105.235
# result["lcl"] = 96.098
# result["secondary"] = {"label": "Range", "centerLine": 6.267, "ucl": 14.301, "lcl": 0.0, "points": [4.4, 12.3, 2.1]}
xbar_s (X-bar/S, subgroup size 5)
Same idea as xbar, but any subgroup size >= 2 (not capped at 10) since it uses the sample standard deviation instead of range.
values = [100.4, 98.6, 100.2, 96.0, 109.8, 102.1, 103.2, 97.5, 101.0, 99.8]
subgroupSize = 5
result = system.kanoa.quality.spc.spcCheck(values, "xbar_s", None, subgroupSize, None, None, None)
# result["points"] = [101.0, 100.72] (subgroup means)
# result["mean"] = 100.860
# result["ucl"] = 106.158
# result["lcl"] = 95.562
# result["secondary"] = {"label": "S", "centerLine": 3.712, "ucl": 7.755, "lcl": 0.0, "points": [5.253, 2.170]}
p (percent defective, sample size varies)
Attribute chart - no secondary, no capability. uclPoints/lclPoints genuinely vary per point since sampleSizes isn't constant.
values = [3, 5, 2, 8, 4] # defective counts per subgroup
sampleSizes = [50, 60, 45, 55, 50]
result = system.kanoa.quality.spc.spcCheck(values, "p", None, None, None, None, sampleSizes)
# result["points"] = [0.06, 0.083, 0.044, 0.145, 0.08] (proportion defective per subgroup)
# result["mean"] = 0.0846
# result["uclPoints"] = [0.203, 0.192, 0.209, 0.197, 0.203] (different per point - sample size varies)
# result["lclPoints"] = [0.0, 0.0, 0.0, 0.0, 0.0]
np (number defective, constant sample size)
subgroupSize is the constant sample size here, not a grouping count - points stays as the raw defective counts, and control limits are constant (a single ucl/lcl, same as individuals/xbar).
values = [3, 5, 2, 8, 4] # defective counts, each out of a subgroup of 50
subgroupSize = 50
result = system.kanoa.quality.spc.spcCheck(values, "np", None, subgroupSize, None, None, None)
# result["points"] = [3, 5, 2, 8, 4] (raw counts, unchanged)
# result["mean"] = 4.4
# result["sigma"] = 2.003
# result["ucl"] = 10.410
# result["lcl"] = 0.0
c (number of defects, constant area of opportunity)
values = [3, 5, 2, 8, 4, 6] # defect counts, one inspection unit each
result = system.kanoa.quality.spc.spcCheck(values, "c", None, None, None, None, None)
# result["points"] = [3, 5, 2, 8, 4, 6]
# result["mean"] = 4.667
# result["sigma"] = 2.160
# result["ucl"] = 11.147
# result["lcl"] = 0.0
u (defects per unit, area of opportunity varies)
Same as c, but sampleSizes carries the varying area/unit count, so points becomes a rate and the limits vary per point.
values = [3, 5, 2, 8, 4] # defect counts
areas = [10, 12, 9, 11, 10] # units inspected per subgroup
result = system.kanoa.quality.spc.spcCheck(values, "u", None, None, None, None, areas)
# result["points"] = [0.3, 0.417, 0.222, 0.727, 0.4] (defects per unit)
# result["mean"] = 0.4231
# result["uclPoints"] = [1.04, 0.986, 1.074, 1.011, 1.04]
# result["lclPoints"] = [0.0, 0.0, 0.0, 0.0, 0.0]
pre_control
A materially different return shape - zones/pairSignals/qualifyRunIndex/greenLow/greenHigh/yellowLow/yellowHigh in place of uclPoints/lclPoints/violations/secondary/capability. Requires both usl and lsl.
values = [99, 101, 98, 102, 100, 106, 107]
usl, lsl = 110.0, 90.0
result = system.kanoa.quality.spc.spcCheck(values, "pre_control", None, None, usl, lsl, None)
# result["greenLow"] = 95.0 (middle 50% of the 90-110 tolerance)
# result["greenHigh"] = 105.0
# result["zones"] = ["green", "green", "green", "green", "green", "yellow-high", "yellow-high"]
# the first five points qualify the process (5 consecutive green); the last two
# both landing in yellow-high is what pairSignals flags as a shift, not a random blip