Spaces:
Sleeping
Sleeping
Commit
·
33d5431
1
Parent(s):
0f4c3d4
Enhanced SPC Control
Browse files- .gitignore +1 -0
- SPC_System/spc_engine.py +107 -130
.gitignore
CHANGED
|
@@ -4,3 +4,4 @@ test.ipynb
|
|
| 4 |
*.xlsx
|
| 5 |
*.json
|
| 6 |
charts/*.png
|
|
|
|
|
|
| 4 |
*.xlsx
|
| 5 |
*.json
|
| 6 |
charts/*.png
|
| 7 |
+
charts_compare/*.png
|
SPC_System/spc_engine.py
CHANGED
|
@@ -1,145 +1,122 @@
|
|
| 1 |
-
import json
|
| 2 |
import pandas as pd
|
| 3 |
-
import numpy as np
|
| 4 |
import matplotlib.pyplot as plt
|
|
|
|
| 5 |
import os
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
file = input("Select the file to analyze or type in the file name (Should be .xlsx)")
|
| 9 |
-
INPUT_EXCEL = file
|
| 10 |
-
|
| 11 |
-
df = pd.read_excel(INPUT_EXCEL)
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
################DICTIONARY BASED ON THE PARAMETER###########################
|
| 15 |
-
import json
|
| 16 |
-
|
| 17 |
-
df_new = df.head(3)
|
| 18 |
-
##To drop the extra columns
|
| 19 |
-
df_new = df_new.dropna(axis=1, how='all')
|
| 20 |
-
df_param = df_new.drop(["T_TIME","SITE_NUM"],axis=1)
|
| 21 |
-
parameter = list(df_param.columns)
|
| 22 |
-
|
| 23 |
-
##Convert the parameter to dictionary
|
| 24 |
-
new_dict = {param:{"values":[]} for param in parameter}
|
| 25 |
-
for prm in parameter:
|
| 26 |
-
temp = dict(zip(df_new['SITE_NUM'],df_new[prm]))
|
| 27 |
-
new_dict[prm].update(temp)
|
| 28 |
-
|
| 29 |
-
for prm in parameter:
|
| 30 |
-
df = df.dropna(axis=1, how='all')
|
| 31 |
-
__temp = df[prm].values[4:].tolist()
|
| 32 |
-
new_dict[prm]['values'].extend(__temp)
|
| 33 |
-
|
| 34 |
-
######To extract the Limits##############
|
| 35 |
-
df_new = df.head(3)
|
| 36 |
-
##To drop the extra columns
|
| 37 |
-
df_new = df_new.dropna(axis=1, how='all')
|
| 38 |
-
df_param = df_new.drop(["T_TIME","SITE_NUM"],axis=1)
|
| 39 |
-
parameter = list(df_param.columns)
|
| 40 |
-
|
| 41 |
-
##Convert the parameter to dictionary
|
| 42 |
-
new_dict = {param:{"values":[]} for param in parameter}
|
| 43 |
-
for prm in parameter:
|
| 44 |
-
temp = dict(zip(df_new['SITE_NUM'],df_new[prm]))
|
| 45 |
-
new_dict[prm].update(temp)
|
| 46 |
-
|
| 47 |
-
for prm in parameter:
|
| 48 |
-
df = df.dropna(axis=1, how='all')
|
| 49 |
-
__temp = df[prm].values[4:].tolist()
|
| 50 |
-
new_dict[prm]['values'].extend(__temp)
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
#TODO: No need to store json. If in case if we want to maintian history then we can store them
|
| 54 |
-
# json_data = json.dumps(new_dict)
|
| 55 |
-
# print(json_data)
|
| 56 |
-
|
| 57 |
-
# with open("output.json", "w") as f:
|
| 58 |
-
# json.dump(new_dict, f, indent=4)
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
#############Plotting###############
|
| 63 |
-
|
| 64 |
-
data = new_dict
|
| 65 |
-
|
| 66 |
-
##TODO: To plot for all the paramter. But need to check the way to handle it in the front end.
|
| 67 |
-
for param in parameter:
|
| 68 |
-
|
| 69 |
-
values = data[param]["values"]
|
| 70 |
limit_l = data[param]["LimitL"]
|
| 71 |
limit_u = data[param]["LimitU"]
|
| 72 |
unit = data[param]["Unit"]
|
| 73 |
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
std =
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
plt.
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
plt.ylabel(f"Value ({unit})")
|
| 105 |
plt.grid(True)
|
| 106 |
plt.legend()
|
| 107 |
-
|
| 108 |
plt.tight_layout()
|
| 109 |
-
# plt.show()
|
| 110 |
-
# chart_path = f"./charts/control_chart_{param}.png"
|
| 111 |
|
| 112 |
-
#
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
# Create folder if not exists
|
| 116 |
-
os.makedirs(folder, exist_ok=True)
|
| 117 |
-
|
| 118 |
-
# File path
|
| 119 |
-
chart_path = f"{folder}/control_chart_{param}.png"
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
plt.savefig(chart_path, dpi=300, bbox_inches='tight')
|
| 123 |
plt.close()
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
####line plot
|
| 127 |
-
# x_axis = range(1, len(values)+1)
|
| 128 |
-
# plt.figure(figsize=(10,5))
|
| 129 |
-
# plt.plot(x_axis,values, marker='o', linestyle='-', label='Measurements')
|
| 130 |
-
# plt.axhline(mean, color='green', linestyle='--', label='Mean')
|
| 131 |
-
# plt.axhline(UCL, color='red', linestyle='-.', label='UCL (Mean + 3σ)')
|
| 132 |
-
# plt.axhline(LCL, color='red', linestyle='--', label='LCL (Mean - 3σ)')
|
| 133 |
-
# plt.axhline(limit_l, color='orange', linestyle=':', label='Lower Spec Limit')
|
| 134 |
-
# plt.axhline(limit_u, color='orange', linestyle=':', label='Upper Spec Limit')
|
| 135 |
-
|
| 136 |
-
# plt.xticks(x_axis)
|
| 137 |
-
# plt.title(f"SPC Chart - {param} ({unit})")
|
| 138 |
-
# plt.xlabel("Sample Index")
|
| 139 |
-
# plt.ylabel(f"Value ({unit})")
|
| 140 |
-
# plt.legend()
|
| 141 |
-
# plt.grid(True)
|
| 142 |
-
# plt.tight_layout()
|
| 143 |
-
# plt.show()
|
| 144 |
-
|
| 145 |
-
|
|
|
|
|
|
|
| 1 |
import pandas as pd
|
|
|
|
| 2 |
import matplotlib.pyplot as plt
|
| 3 |
+
import numpy as np
|
| 4 |
import os
|
| 5 |
|
| 6 |
+
# --------- Load three datasets ----------
|
| 7 |
+
df_t1 = pd.read_excel("tester#1.xlsx")
|
| 8 |
+
df_t2 = pd.read_excel("tester#2.xlsx")
|
| 9 |
+
df_gold = pd.read_excel("Golden_Data.xlsx")
|
| 10 |
+
|
| 11 |
+
# --------- Get parameter list ----------
|
| 12 |
+
df_new = df_gold.head(3).dropna(axis=1, how="all")
|
| 13 |
+
df_param = df_new.drop(["T_TIME", "SITE_NUM"], axis=1)
|
| 14 |
+
parameters = list(df_param.columns)
|
| 15 |
+
|
| 16 |
+
# --------- Build master dictionary ----------
|
| 17 |
+
data = {
|
| 18 |
+
param: {
|
| 19 |
+
"Tester1": pd.to_numeric(df_t1[param].iloc[3:], errors="coerce").dropna().tolist(),
|
| 20 |
+
"Tester2": pd.to_numeric(df_t2[param].iloc[3:], errors="coerce").dropna().tolist(),
|
| 21 |
+
"Golden": pd.to_numeric(df_gold[param].iloc[3:], errors="coerce").dropna().tolist(),
|
| 22 |
+
"LimitL": df_new[param].iloc[1],
|
| 23 |
+
"LimitU": df_new[param].iloc[2],
|
| 24 |
+
"Unit": df_new[param].iloc[0]
|
| 25 |
+
}
|
| 26 |
+
for param in parameters
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
# --------- Plot comparison SPC ----------
|
| 30 |
+
os.makedirs("./charts_compare", exist_ok=True)
|
| 31 |
+
|
| 32 |
+
for param in parameters:
|
| 33 |
+
|
| 34 |
+
t1 = np.array(data[param]["Tester1"])
|
| 35 |
+
t2 = np.array(data[param]["Tester2"])
|
| 36 |
+
gold = np.array(data[param]["Golden"])
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
limit_l = data[param]["LimitL"]
|
| 39 |
limit_u = data[param]["LimitU"]
|
| 40 |
unit = data[param]["Unit"]
|
| 41 |
|
| 42 |
+
# Golden reference control band
|
| 43 |
+
mean = gold.mean()
|
| 44 |
+
t1_mean = t1.mean()
|
| 45 |
+
t2_mean = t2.mean()
|
| 46 |
+
std = gold.std(ddof=1)
|
| 47 |
+
UCL = mean + 3 * std
|
| 48 |
+
LCL = mean - 3 * std
|
| 49 |
+
|
| 50 |
+
# ---- Fix max length ----
|
| 51 |
+
max_len = max(len(t1), len(t2), len(gold))
|
| 52 |
+
xaxis = np.arange(1, max_len + 1)
|
| 53 |
+
|
| 54 |
+
# ---- Correlation ----
|
| 55 |
+
min_len_t1 = min(len(t1), len(gold))
|
| 56 |
+
min_len_t2 = min(len(t2), len(gold))
|
| 57 |
+
|
| 58 |
+
corr_t1 = np.corrcoef(t1[:min_len_t1], gold[:min_len_t1])[0,1]
|
| 59 |
+
corr_t2 = np.corrcoef(t2[:min_len_t2], gold[:min_len_t2])[0,1]
|
| 60 |
+
corr_t1_t2 = np.corrcoef(t1[:min_len_t1], t2[:min_len_t2])[0,1]
|
| 61 |
+
|
| 62 |
+
# ---- CPK (based on Golden dataset) ----
|
| 63 |
+
cpk = min((mean - limit_l) / (3 * std), (limit_u - mean) / (3 * std))
|
| 64 |
+
|
| 65 |
+
plt.figure(figsize=(10,6))
|
| 66 |
+
|
| 67 |
+
# --- Plot all datasets ---
|
| 68 |
+
plt.plot(range(1, len(t1)+1), t1, marker='o', label="Tester #1")
|
| 69 |
+
plt.plot(range(1, len(t2)+1), t2, marker='o', label="Tester #2")
|
| 70 |
+
plt.plot(range(1, len(gold)+1), gold, marker='o', label="Gold Reference", linewidth=3)
|
| 71 |
+
|
| 72 |
+
# --- SPC lines ---
|
| 73 |
+
plt.axhline(mean, linestyle='--', color='black', label=f"Mean (Gold)")
|
| 74 |
+
plt.axhline(UCL, linestyle='-.', color='red', label="UCL (Mean + 3σ)")
|
| 75 |
+
plt.axhline(LCL, linestyle='-.', color='red', label="LCL (Mean – 3σ)")
|
| 76 |
+
|
| 77 |
+
# --- Spec Limits ---
|
| 78 |
+
plt.axhline(limit_l, linestyle=':', color='orange', label="Lower Spec")
|
| 79 |
+
plt.axhline(limit_u, linestyle=':', color='orange', label="Upper Spec")
|
| 80 |
+
|
| 81 |
+
# ---- Fix X-axis ----
|
| 82 |
+
# plt.xticks(np.arange(1, max_len + 1, step=1))
|
| 83 |
+
# plt.xlim(1, max_len)
|
| 84 |
+
|
| 85 |
+
# ---- Fix X-axis with whole numbers + padding ----
|
| 86 |
+
max_len = max(len(t1), len(t2), len(gold))
|
| 87 |
+
|
| 88 |
+
plt.xticks(np.arange(1, max_len + 1, 1)) # whole numbers
|
| 89 |
+
plt.xlim(0, max_len + 1) # space before 1 and after last point
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
# ---- Add CPK + Correlation text box ----
|
| 93 |
+
textstr = (
|
| 94 |
+
f"Gold Mean = {mean:.4f}\n"
|
| 95 |
+
f"Std Dev = {std:.4f}\n"
|
| 96 |
+
f"CPK = {cpk:.4f}\n"
|
| 97 |
+
f"Corr T1–Gold = {corr_t1:.4f}\n"
|
| 98 |
+
f"Corr T2–Gold = {corr_t2:.4f}\n"
|
| 99 |
+
f"Corr T1-T2 = {corr_t1_t2:.4f}\n"
|
| 100 |
+
f"Tester#1 Mean = {t1_mean:.4f}\n"
|
| 101 |
+
f"Tester#2 Mean = {t2_mean:4f}\n"
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
plt.gca().text(
|
| 105 |
+
0.02, 0.98, textstr,
|
| 106 |
+
transform=plt.gca().transAxes,
|
| 107 |
+
fontsize=10,
|
| 108 |
+
verticalalignment='top',
|
| 109 |
+
bbox=dict(boxstyle="round,pad=0.4", facecolor="white", alpha=0.8)
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
# Labels
|
| 113 |
+
plt.title(f"SPC Comparison - {param} ({unit})")
|
| 114 |
+
plt.xlabel("Sample Index")
|
| 115 |
plt.ylabel(f"Value ({unit})")
|
| 116 |
plt.grid(True)
|
| 117 |
plt.legend()
|
|
|
|
| 118 |
plt.tight_layout()
|
|
|
|
|
|
|
| 119 |
|
| 120 |
+
# Save chart
|
| 121 |
+
plt.savefig(f"./charts_compare/SPC_compare_{param}.png", dpi=300)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
plt.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|