-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspc_inferences.py
More file actions
85 lines (76 loc) · 5.14 KB
/
spc_inferences.py
File metadata and controls
85 lines (76 loc) · 5.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import numpy as np
def generate_inferences(stats: dict, cap: dict, USL: float, LSL: float,
normality: dict = None) -> dict:
bullets = []
narrative_parts = []
cpk = cap.get("Cpk")
cp = cap.get("Cp")
ppk = cap.get("Ppk")
# Rule 1 — Capability (Cpk)
if cpk is None:
bullets.append({"icon": "ℹ️", "text": "Capability could not be calculated — check spec limits"})
narrative_parts.append("Capability could not be assessed (spec limits may equal control limits).")
elif cpk >= 1.33:
bullets.append({"icon": "✅", "text": f"Process is capable — Cpk = {cpk:.3f} meets the 1.33 benchmark"})
narrative_parts.append(f"This process is capable with a Cpk of {cpk:.3f}, meeting the 1.33 benchmark.")
elif cpk >= 1.0:
bullets.append({"icon": "⚠️", "text": f"Process is marginally capable — Cpk = {cpk:.3f} (target ≥ 1.33)"})
narrative_parts.append(f"This process is marginally capable (Cpk = {cpk:.3f}), falling short of the 1.33 target.")
else:
bullets.append({"icon": "❌", "text": f"Process is not capable — Cpk = {cpk:.3f}, defects are likely"})
narrative_parts.append(f"This process is not capable (Cpk = {cpk:.3f}), and defects are likely.")
# Rule 2 — Centering (Cp vs Cpk)
if cp is None or cpk is None:
bullets.append({"icon": "ℹ️", "text": "Centering could not be assessed — spec limits required"})
narrative_parts.append("Centering could not be assessed.")
elif cp - cpk > 0.10:
bullets.append({"icon": "⚠️", "text": f"Process is off-center — Cp ({cp:.3f}) is notably higher than Cpk ({cpk:.3f}), mean is shifted toward one spec limit"})
narrative_parts.append(f"The process mean is shifted off-center (Cp = {cp:.3f} vs Cpk = {cpk:.3f}).")
else:
bullets.append({"icon": "✅", "text": "Process is well-centered within spec limits"})
narrative_parts.append("The process is well-centered within spec limits.")
# Rule 3 — Stability (out-of-control points on X-bar chart)
xbar = np.asarray(stats.get("xbar", []))
UCL = stats.get("UCL_xbar", float("inf"))
LCL = stats.get("LCL_xbar", float("-inf"))
if len(xbar) == 0:
bullets.append({"icon": "ℹ️", "text": "Stability could not be assessed"})
narrative_parts.append("Stability could not be assessed.")
else:
ooc = int(np.sum((xbar > UCL) | (xbar < LCL)))
if ooc == 0:
bullets.append({"icon": "✅", "text": "Process is stable — no points outside control limits"})
narrative_parts.append("No out-of-control points were detected on the control chart.")
elif ooc <= 2:
bullets.append({"icon": "⚠️", "text": f"{ooc} point(s) outside control limits — investigate for assignable causes"})
narrative_parts.append(f"{ooc} point(s) outside control limits suggest occasional instability worth investigating.")
else:
bullets.append({"icon": "❌", "text": f"{ooc} points outside control limits — process is unstable"})
narrative_parts.append(f"{ooc} points outside control limits indicate the process is unstable.")
# Rule 4 — Long-term drift (Cpk vs Ppk)
if cpk is None or ppk is None:
bullets.append({"icon": "ℹ️", "text": "Long-term drift could not be assessed"})
narrative_parts.append("Long-term drift could not be assessed.")
elif cpk - ppk > 0.15:
bullets.append({"icon": "⚠️", "text": f"Long-term performance (Ppk = {ppk:.3f}) is notably worse than short-term (Cpk = {cpk:.3f}) — process may have drifted over time"})
narrative_parts.append(f"Long-term performance (Ppk = {ppk:.3f}) is notably worse than short-term capability (Cpk = {cpk:.3f}), suggesting the process may have drifted.")
else:
bullets.append({"icon": "✅", "text": f"Short-term and long-term performance are consistent (Cpk = {cpk:.3f}, Ppk = {ppk:.3f})"})
narrative_parts.append(f"Short and long-term performance are consistent (Cpk = {cpk:.3f}, Ppk = {ppk:.3f}).")
# Rule 5 — Normality (Anderson-Darling)
if normality is None:
bullets.append({"icon": "ℹ️", "text": "Normality not assessed — need ≥8 values"})
narrative_parts.append("Normality could not be assessed.")
elif normality["is_normal"]:
bullets.append({"icon": "✅", "text":
f"Data is normal — Anderson-Darling A² = {normality['A2']:.3f}, p = {normality['p_value']:.3f} ≥ 0.05"})
narrative_parts.append(
f"Data passes the Anderson-Darling normality test (A² = {normality['A2']:.3f}, p = {normality['p_value']:.3f})."
)
else:
bullets.append({"icon": "⚠️", "text":
f"Data is non-normal — Anderson-Darling A² = {normality['A2']:.3f}, p = {normality['p_value']:.3f} < 0.05; Cp/Cpk unreliable, use Pp/Ppk"})
narrative_parts.append(
f"Data fails the Anderson-Darling normality test (A² = {normality['A2']:.3f}, p = {normality['p_value']:.3f}); short-term Cp/Cpk should be interpreted with caution."
)
return {"bullets": bullets, "narrative": " ".join(narrative_parts)}