4、数据预处理基础:缺失值处理、异常值检测、数据标准化与归一化
各位同行,大家好。今天我们来聊聊数据预处理。
说实话,在厂务运维这个行当里,我见过太多人一上来就急着建模型、跑算法。结果呢?数据质量不过关,模型跑出来全是垃圾。我自己就吃过这个亏——有一次做冷却塔的故障预测,数据里有一半的传感器值都是空的,模型愣是给我预测出个「完美运行」,结果第二天就跳了报警。
所以,数据预处理不是可有可无的步骤。它是地基。地基不稳,楼盖得再高也得塌。
核心观点: 数据预处理占整个故障诊断工作量的 60% 以上。别嫌麻烦,这一步省下的时间,后面会加倍还给你。
4.1 缺失值处理
厂务系统的传感器,说句不好听的,三天两头出毛病。不是通讯断了,就是探头脏了,数据直接给你来个空值。你想想看,如果不管这些空值,模型会怎么想?它会以为「哦,这里没数据,那设备肯定没运行」,这不就乱套了吗?
我个人习惯,拿到数据第一件事就是看缺失率。用 df.isnull().sum() / len(df) 扫一眼,心里就有数了。
4.1.1 删除法
如果某列缺失超过 70%,我建议直接删掉。别心疼。留着也是噪声。但要注意——删除前先确认这个特征是不是关键参数。比如冷却水温度传感器坏了,你不能把整个温度特征删了,那后面还诊断个啥?
我的经验: 对于时间序列数据,如果连续缺失超过 5 个时间点,用插值法补出来的数据基本不可信。这时候我宁愿把这段数据整体截掉。
4.1.2 填充法
常用的填充方式有三种:
- 均值/中位数填充: 简单粗暴,适合数据分布比较稳定的场景。比如环境温度,波动不大,用均值填没问题。
- 前向/后向填充: 适合缓慢变化的参数,比如水箱液位。用前一个时刻的值来补,逻辑上说得通。
- 插值法: 线性插值、样条插值。我个人在振动数据上用得比较多,因为振动信号变化快,用均值填会失真。
# 示例:用前向填充处理冷却水温度缺失
import pandas as pd
df['cooling_temp'].fillna(method='ffill', inplace=True)
# 示例:线性插值
df['vibration'].interpolate(method='linear', inplace=True)
避坑指南: 我曾经在一个项目中,用均值填充了压力传感器的缺失值。结果模型训练出来,对压力突变完全不敏感。后来才发现,均值把那些「异常但真实」的波动给抹平了。所以,填充前一定要分析缺失的机制——是随机缺失,还是因为设备故障导致的系统性缺失?
4.2 异常值检测
异常值,说白了就是「看着不对劲」的数据。比如冷却塔出水温度突然飙到 80 度,这明显不合理。但问题来了——有些异常是传感器误报,有些却是设备故障的前兆。怎么区分?
4.2.1 3σ 原则
假设数据服从正态分布,超过均值 ± 3 倍标准差的值,就认为是异常。这个方法简单,但有个前提——数据得是正态分布。厂务数据里,像环境温度、湿度这类参数还行,但像能耗、流量这些,往往偏态分布,用 3σ 就不太灵了。
4.2.2 IQR 箱线图法
这个方法不依赖正态分布。用四分位数来定义异常:
- 下界:Q1 - 1.5 * IQR
- 上界:Q3 + 1.5 * IQR
超出这个范围的值,标记为异常。我个人比较喜欢这个方法,因为它对数据分布不敏感,适用面广。
# IQR 异常检测示例
Q1 = df['power'].quantile(0.25)
Q3 = df['power'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['power'] < lower_bound) | (df['power'] > upper_bound)]
4.2.3 DBSCAN 聚类法
这个方法更高级一点。它基于密度聚类,把那些「孤零零」的点揪出来。我在做离心式冷水机组的故障诊断时用过一次,效果不错。但要注意——DBSCAN 对参数敏感,需要调参。
关键提醒: 检测出异常值后,不要直接删掉。先问问自己:这个异常是传感器故障,还是设备真的出了问题?我建议建立一个「异常日志」,记录每次异常的原因和处理方式。时间长了,这就是你的故障知识库。
4.3 数据标准化与归一化
为什么要做这一步?你想想看,温度是 20-30 度,压力是 0.5-1.0 MPa,振动是 0.01-0.1 mm/s。这些量纲都不一样,直接扔进模型,模型会天然地「偏爱」数值大的特征。说白了,就是不公平。
4.3.1 Z-score 标准化
公式很简单:z = (x - μ) / σ。处理后数据均值为 0,标准差为 1。适合数据本身近似正态分布的场景。我在做 PCA 降维前,一定会先做 Z-score 标准化。
4.3.2 Min-Max 归一化
公式:x' = (x - min) / (max - min)。把数据压缩到 [0, 1] 区间。适合有明确上下界的参数,比如温度、湿度。但要注意——如果数据里有极端异常值,Min-Max 会把正常数据压缩到很小范围,反而不好。
# Z-score 标准化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_scaled = scaler.fit_transform(df[['temp', 'pressure', 'vibration']])
# Min-Max 归一化
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df_norm = scaler.fit_transform(df[['temp', 'pressure', 'vibration']])
我的习惯: 对于神经网络模型,我一般用 Min-Max 归一化。对于 SVM、KNN 这类基于距离的模型,用 Z-score 标准化更多。没有绝对的对错,多试试就知道了。
4.4 预处理流程总结
好了,我们把整个流程串起来。拿到原始数据后,我的标准操作是:
- 第一步: 检查缺失率,决定删除还是填充。
- 第二步: 用 IQR 或 3σ 检测异常值,记录到日志。
- 第三步: 根据模型需求,选择标准化或归一化。
- 第四步: 可视化检查——画个箱线图、分布图,看看处理后的数据是否合理。
这一步做完,你的数据才算「能用」。别嫌麻烦,我见过太多人跳过预处理,结果模型上线后天天误报,最后被领导叫去喝茶。嗯,咱们还是老老实实把地基打牢吧。
一句话总结: 数据预处理不是技术活,是良心活。你在这上面花的时间,模型会加倍回报你。