SPC控制图入门:用Python替代Excel
SPC控制图入门:用Python替代Excel
一、问题背景:15分钟一张图,20张就是5小时
工艺工程师老张每天早上做SPC控制图,流程如下:
1. 从MES导出CSV —— 5分钟
2. 在Excel里插入折线图 —— 5分钟
3. 手动算均值+3σ线,画上去 —— 3分钟
4. 肉眼找异常点 —— 2分钟
5. 20张图做完 = 5小时,一上午就没了
而且Excel有个致命问题:控制限忘了更新。用了测试阶段的数据当基准,过了两个月才发现不对。客户投诉说"你们的质量监控是摆设"。
老张来找我:"能不能用Python一键生成?"
我说能。20行代码的事。
学完这一篇,你能做到:
1. 用numpy算控制图的均值和3σ限
2. 用matplotlib画出专业控制图
3. 程序自动标出异常点,不用肉眼看
---
二、技术原理:控制图只有3条线
SPC控制图看起来复杂,拆开看就3条线:
| 线条 | 含义 | 怎么算 |
|------|------|--------|
| CL(中心线) | 数据均值 | `np.mean(data)` |
| UCL(上控制限) | 均值 + 3×标准差 | `mean + 3*std` |
| LCL(下控制限) | 均值 - 3×标准差 | `mean - 3*std` |
为什么是"子组"(Subgroup)?
FAB里不是取每个单片Wafers的数据做控制图,而是5片一组取均值。为什么呢?
单片波动太大了。 一片1249.8、下一片1252.1,差值2.3——但这可能只是正常的测量噪声,不代表过程有问题。
5片一组取均值:均值把随机波动抵消了,能看出过程的真实变化。
好比看一个人体温:单次测量36.5℃和37.2℃差别不大,但连续5天都在37.2℃以上——这才说明有问题。
```python
import numpy as np
模拟100个数据点(5片一组,共20组)
np.random.seed(42)
raw_data = np.random.normal(1250, 3, 100)
重新排列成20组×5片
subgroups = raw_data.reshape(20, 5)
每组算均值(这就是X-bar)
xbars = np.mean(subgroups, axis=1)
print(f"20个子组的均值:\n{xbars.round(2)}")
每组算极差(max - min)
ranges = np.ptp(subgroups, axis=1)
print(f"20个子组的极差:\n{ranges.round(2)}")
```
为什么这样写? `reshape(20, 5)` 把一维数组变成20行5列的表格——Python里叫"多维数组"。`axis=1` 表示"沿着行方向算",就是每组的5个数据算一个均值。`np.ptp` 是"peak to peak",就是 `max - min`,用来衡量组内波动。
---
三、实战案例:20行代码出完整控制图
3.1 先画X-bar图(看均值变化)
```python
import numpy as np
import matplotlib.pyplot as plt
数据准备
np.random.seed(42)
raw = np.random.normal(1250, 3, 100)
subgroups = raw.reshape(20, 5)
xbars = np.mean(subgroups, axis=1)
算控制限
cl = np.mean(xbars) # 中心线 = 均值的均值
std = np.std(xbars) # 子组均值的标准差
ucl = cl + 3 * std # 上控制限
lcl = cl - 3 * std # 下控制限
画图
plt.figure(figsize=(12, 5))
x = range(1, 21) # 子组编号1-20
画折线和数据点
plt.plot(x, xbars, 'o-', color='#1976D2', linewidth=2, markersize=6, label='子组均值')
画控制线
plt.axhline(y=ucl, color='red', linestyle='--', linewidth=2, label=f'UCL={ucl:.2f}')
plt.axhline(y=cl, color='green', linestyle='-', linewidth=2, label=f'CL={cl:.2f}')
plt.axhline(y=lcl, color='red', linestyle='--', linewidth=2, label=f'LCL={lcl:.2f}')
标出异常点(超出控制限的)
for i, val in enumerate(xbars):
if val > ucl or val < lcl:
plt.plot(i+1, val, 'ro', markersize=10, zorder=5)
plt.title('膜厚X-bar控制图', fontsize=14)
plt.xlabel('子组序号')
plt.ylabel('膜厚均值 (Å)')
plt.legend(loc='upper right')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
```
为什么这样写? `axhline` 画水平线,三条线(UCL/CL/LCL)加上图就是完整的X-bar控制图。最后的 `for` 循环找出所有超出控制限的点,用 `'ro'`(红色圆点)标记出来,一目了然。
3.2 再画R图(看波动变化)
X-bar图看均值,R图看波动。均值正常不代表波动正常。 比如设备老化时,均值可能稳定在1250,但波动从±3σ变成了±6σ——R图能捕捉到这种隐患。
```python
R图:监控组内波动
ranges = np.ptp(subgroups, axis=1)
r_cl = np.mean(ranges) # 极差的均值
r_ucl = r_cl * 2.114 # 子组5时的D4常数
r_lcl = r_cl * 0 # 子组5时的D3常数=0
plt.figure(figsize=(12, 4))
plt.plot(x, ranges, 'o-', color='#FF9800', linewidth=2, markersize=6, label='子组极差')
plt.axhline(y=r_ucl, color='red', linestyle='--', linewidth=2, label=f'UCL={r_ucl:.2f}')
plt.axhline(y=r_cl, color='green', linestyle='-', linewidth=2, label=f'CL={r_cl:.2f}')
plt.title('膜厚R控制图', fontsize=14)
plt.xlabel('子组序号')
plt.ylabel('极差 (Å)')
plt.legend(); plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
```
为什么R图要乘以2.114? 这是统计学的常数(D4),因为极差的分布不是正态分布,直接用3-sigma会算偏。D4常数根据子组大小查表得到,子组5时是2.114。这不是可以随便调的——国标GB/T 4091和汽车行业IATF 16949都规定了这些常数。
3.3 封装成函数
```python
def make_spc_chart(data, subgroup_size=5):
"""
一键生成X-bar + R控制图
参数:
data: numpy数组,原始数据
subgroup_size: 每组的样本数(默认5片一组)
返回:
dict: 控制限和异常信息
"""
# 重组数据
n_groups = len(data) // subgroup_size
groups = data[:n_groups * subgroup_size].reshape(n_groups, subgroup_size)
# 计算统计量
xbars = np.mean(groups, axis=1)
rs = np.ptp(groups, axis=1)
# X-bar控制限
cl = np.mean(xbars)
xbar_std = np.std(xbars)
x_ucl = cl + 3 * xbar_std
x_lcl = cl - 3 * xbar_std
# 找出异常点
anomalies = []
for i, val in enumerate(xbars):
if val > x_ucl or val < x_lcl:
anomalies.append({'group': i+1, 'value': val})
return {
'center': cl,
'ucl': x_ucl,
'lcl': x_lcl,
'total_groups': n_groups,
'anomaly_count': len(anomalies),
'in_control': len(anomalies) == 0,
'anomalies': anomalies
}
使用
result = make_spc_chart(raw)
print(f"控制状态: {'✓ 受控' if result['in_control'] else '⚠ 失控'}")
print(f"异常点数: {result['anomaly_count']}")
if result['anomalies']:
for a in result['anomalies']:
print(f" 子组{a['group']}: 值{a['value']:.2f}超出控制限")
```
为什么这样写? 封装成函数后,每天只需导入数据、调用 `make_spc_chart()`,5分钟的工作变成1秒。函数还返回了业务需要的核心信息——"是否受控"和"哪些组异常",可以直接集成到邮箱报警等后续流程。
---
四、效果对比
| 维度 | Excel手动版 | Python自动版 | 提升 |
|------|-----------|-------------|------|
| 每张控制图耗时 | 15分钟 | 0.5秒 | 1800倍 |
| 20张图总耗时 | 5小时 | 10秒 | 全自动化 |
| 异常点检测 | 人工眼找 | 程序自动标记 | 零遗漏 |
| 控制限更新 | 手动(容易忘) | 每次自动重算 | 100%准确 |
| 异常发现延迟 | 2-6小时 | 实时 | 及时止损 |
| 新人培训成本 | 2小时教Excel操作 | 30秒导入数据 | 极低 |
关键认知:Excel做SPC最大的问题不是慢,而是不可靠。人工操作总是会漏(老张用的还是两个月前的控制限)。用Python后,每次运行都是最新数据、最新控制限,想出错都难。
---
五、自己动手
```python
练习:用你自己的数据做控制图
import numpy as np
import matplotlib.pyplot as plt
1. 把数据贴进来(替换为真实工艺数据)
my_data = [
1248.5, 1251.2, 1249.8, 1250.1, 1252.0, # 子组1
1247.5, 1250.8, 1249.1, 1251.5, 1248.2, # 子组2
1250.3, 1249.6, 1251.0, 1248.9, 1250.7, # 子组3
]
2. 转成numpy数组
data = np.array(my_data)
3. 用上面的make_spc_chart函数检测
✏️ 添加:调用make_spc_chart,打印控制限和异常状态
4. ✏️ 思考:子组大小改成3会怎样?改成10呢?
```
思考题:
1. 样本量多少合适?子组大小一般5-10,太小组内噪声大,太大掩盖短期波动
2. 控制限多久更新一次?我的建议:每次新测25组数据后更新一次,至少每月一次
3. 控制图只有3条线吗?还有1σ、2σ的辅助线(Rule 2-8需要),但新手先学会3线就够了
---
六、常见误区
新手常犯的错误
- 子组大小不一致:一组5片下一组3片——每组的均值标准差不一致,控制限不准。固定大小
- 数据混了不同产品:不同型号的厚度目标值不同,不能混在一起做控制图。同产品同工站做一张图
- 控制限一次用到黑:设备换parts、换材料后,旧控制限不适用。要重新计算
- 忽略R图:X-bar图看起来正常,但R图可能已经报警。两个图要一起看
数据量参考
| 阶段 | 最少数据量 | 说明 |
|------|-----------|------|
| 初始建限 | 20个子组(100个数据) | 过程稳定期数据 |
| 日常监控 | 每个子组5个数据 | 每天或每班次一组 |
| 重新建限 | 设备维护/换型后 | 重新收集20组 |
---
> 💬 你们FAB还在用Excel做SPC吗?换Python最大的阻力是什么?评论区聊
> 📚 收藏+点赞,下一篇讲自动生成日报用得上 👆





