4. 良率数据清洗与预处理实战
高速光模块的良率数据来自多个环节:光芯片测试、耦合工序、老化(Burn-in)、终测(Final Test)、外观检查等。这些数据往往由不同设备、不同班次、不同格式导出,直接拿来做良率统计或根因分析,结论很容易被脏数据带偏。数据清洗与预处理的目标,就是把“能用的数据”变成“可信、可对齐、可追溯的数据集”,为后续的良率建模、SPC 监控和失效分析提供统一底座。
一、先明确数据对象与字段口径
以一条 400G QSFP-DD 光模块产线为例,典型良率数据表包含以下字段:batch_id(批次号)、sn(模块序列号)、station(工站)、test_time(测试时间)、tx_power_dbm(发射光功率)、rx_sens_dbm(接收灵敏度)、temp_c(测试温度)、result(PASS/FAIL)、fail_code(失效代码)。清洗前必须先确认单位、量纲和判定阈值,例如发射光功率以 dBm 为单位,接收灵敏度以 dBm 为单位,温度以 ℃ 为单位。口径不统一,后面的异常值判断就会失真。
二、异常值识别:不能只看 3σ
光模块参数常呈非正态分布,直接用 3σ 会误杀正常的高性能样本。实战中建议组合使用三类方法:
- 物理边界法:根据器件规格设定硬边界,如
tx_power_dbm合理范围设为 [-2, 6] dBm,超出即判为采集异常或单位错误。 - 统计法:用 IQR(四分位距)识别离群点,Q1-1.5×IQR 与 Q3+1.5×IQR 之外视为可疑;对近似正态的参数可辅以 Z-score。
- 业务规则法:同一 SN 在同一工站重复出现、测试时间早于上一工站、FAIL 却无 fail_code,都属于逻辑异常。
识别出异常后不要直接删除,应先打标签。例如新增 outlier_flag 列,标记为 physical、statistical、logic,保留原始值以便追溯。只有确认是采集错误时才剔除,若是真实但极端的良品,应保留并单独分析。
三、缺失值处理:先判断缺失机制
缺失值分三类:完全随机缺失(MCAR)、随机缺失(MAR)、非随机缺失(MNAR)。光模块数据中,rx_sens_dbm 缺失往往是因为该工站未测或测试中断,属于 MNAR,不能简单用均值填充。处理策略如下:
- 缺失比例 < 5% 且为 MCAR:可删除对应行,或按工站分组用中位数填充。
- 缺失比例 5%~30%:按
station+temp_c分组,用组内中位数填充,减少温度带来的偏差。 - 缺失比例 > 30% 或关键判定字段缺失:不填充,标记为
missing_flag,在良率统计中单独列出。
result 和 fail_code 是良率判定的核心字段,缺失时不能猜测填充,必须回查原始记录或标记为“数据不可用”,否则会直接污染良率分母。四、数据对齐:时间、SN 与工站三重对齐
同一模块会经过多个工站,每个工站产生一条记录。数据对齐要解决三件事:
- SN 对齐:以
sn为主键,将各工站记录横向合并为一行,形成“模块级”宽表。 - 时间对齐:确保
test_time按工站顺序递增,若出现倒序,说明时间戳异常或数据串批,需要标记。 - 批次对齐:同一
batch_id内的模块应共享相同的物料批次、设备编号和工艺参数,若不一致,需拆分批次或标记为混批。
对齐后建议生成一张“模块级良率宽表”,每行一个 SN,列包含各工站的关键参数和最终结果。这样后续按批次、按工站、按温度区间统计良率时,口径才一致。
五、批次追溯:从良率异常反查到具体环节
批次追溯的核心是建立“批次—工站—设备—物料—时间”的关联链。当某批次良率突然下降时,可以按以下路径反查:先锁定异常批次,再对比该批次在各工站的 FAIL 分布,找出 FAIL 集中的工站;然后查该工站当班使用的设备编号、校准记录和物料批次;最后与相邻正常批次对比,确认是设备漂移、物料差异还是工艺参数偏移。追溯结果应记录到追溯表中,字段包括 batch_id、station、equipment_id、material_lot、fail_code、fail_count、suspected_cause,便于后续闭环。
六、Python/Pandas 实操要点
下面给出一段可直接参考的清洗骨架代码,重点展示异常标记、缺失填充和对齐合并的写法。示例参数为教学假设,实际使用时请替换为产线真实阈值。
import pandas as pd
import numpy as np
# 1. 读取多工站数据
df = pd.read_csv("module_yield_raw.csv", parse_dates=["test_time"])
# 2. 异常值标记:物理边界 + IQR
df["outlier_flag"] = ""
df.loc[(df["tx_power_dbm"] < -2) | (df["tx_power_dbm"] > 6), "outlier_flag"] = "physical"
q1, q3 = df["rx_sens_dbm"].quantile([0.25, 0.75])
iqr = q3 - q1
low, high = q1 - 1.5 * iqr, q3 + 1.5 * iqr
df.loc[(df["rx_sens_dbm"] < low) | (df["rx_sens_dbm"] > high), "outlier_flag"] = "statistical"
# 3. 缺失值处理:按工站分组中位数填充
df["rx_sens_dbm"] = df.groupby("station")["rx_sens_dbm"].transform(
lambda s: s.fillna(s.median())
)
df["missing_flag"] = df["rx_sens_dbm"].isna()
# 4. 数据对齐:以 SN 为主键,按工站透视
wide = df.pivot_table(
index=["sn", "batch_id"],
columns="station",
values=["tx_power_dbm", "rx_sens_dbm", "result"],
aggfunc="last"
)
wide.columns = ["_".join(col) for col in wide.columns]
wide = wide.reset_index()
# 5. 批次追溯:统计各批次各工站 FAIL 数
fail_stat = (df[df["result"] == "FAIL"]
.groupby(["batch_id", "station", "fail_code"])
.size()
.reset_index(name="fail_count")
.sort_values("fail_count", ascending=False))
运行后应重点检查三件事:outlier_flag 是否集中在某些工站或设备;missing_flag 为 True 的记录是否集中在特定时间段;fail_stat 中 fail_count 最高的批次与工站组合,是否与设备校准记录或物料批次变更时间吻合。若吻合,即可进入根因确认;若不吻合,说明清洗口径或对齐逻辑仍需修正。
数据清洗不是一次性动作,而是伴随良率分析持续迭代的过程。每发现一类新的异常模式,就应把它转化为一条明确的清洗规则,并记录到数据质量日志中。只有这样,后续的良率对比、趋势监控和根因定位才建立在同一套可信数据之上。