3. 数据清洗与预处理:缺失值处理、异常值检测、数据标准化与归一化
各位同行,咱们接着聊。上一章我们把原始数据捞上来了,但说实话,工业现场的数据,那叫一个「脏」。传感器偶尔抽风、通讯丢包、人为误操作……这些数据要是直接扔进模型,结果绝对让你哭。我见过太多人,数据没洗干净就开始分析,最后得出一个「设备寿命还有三年」的结论,结果下个月就坏了。
所以,数据清洗与预处理,不是可有可无的步骤。它是整个预防性维护的基石。今天我就把这块硬骨头,掰开了揉碎了讲给你听。
3.1 缺失值处理:别让「空」坑了你
工业数据里,缺失值太常见了。传感器断电、网络抖动、存储故障……都会导致数据点丢失。我个人习惯,拿到数据第一件事就是检查缺失率。
处理缺失值,主要有三种思路:
- 直接删除:简单粗暴。如果缺失值占比很小(比如不到1%),而且随机分布,删了就删了。我在处理振动数据时经常这么干,因为缺失几个采样点对整体趋势影响微乎其微。
- 填充:用均值、中位数、众数填充。但要注意——如果是时间序列数据,用前向填充(ffill)或后向填充(bfill)更合理。举个例子,温度传感器每10秒上报一次,中间丢了一两个点,用前一个值填充,比用整体均值靠谱得多。
- 插值:线性插值、多项式插值、样条插值。我个人偏爱线性插值,简单且有效。尤其是设备缓慢退化过程中的数据,线性插值基本能还原真实趋势。
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('sensor_data.csv')
# 检查缺失值
print(df.isnull().sum())
# 前向填充(适合时间序列)
df['temperature'].fillna(method='ffill', inplace=True)
# 线性插值(适合缓慢变化的数据)
df['vibration'].interpolate(method='linear', inplace=True)
# 如果缺失太多,直接删除该行
df.dropna(subset=['pressure'], inplace=True)
3.2 异常值检测:揪出「捣乱分子」
异常值,说白了就是那些明显偏离正常范围的数据点。可能是传感器故障、通讯干扰,也可能是设备真的出问题了。所以,异常值不一定是坏事——它可能是故障的前兆。
我常用的方法有三种:
- 3σ 原则:假设数据服从正态分布,超出均值±3倍标准差的值视为异常。简单,但要求数据分布不能太偏。
- IQR 方法:四分位距法。用 Q1 - 1.5*IQR 和 Q3 + 1.5*IQR 作为上下界。这个方法对偏态分布更鲁棒。我个人更常用这个。
- 箱线图可视化:画个箱线图,异常点一目了然。我每次做数据探索,必先画箱线图。
# IQR 方法检测异常值
Q1 = df['vibration'].quantile(0.25)
Q3 = df['vibration'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常值
df['is_outlier'] = (df['vibration'] < lower_bound) | (df['vibration'] > upper_bound)
# 查看异常值
print(df[df['is_outlier']])
3.3 数据标准化与归一化:让不同量纲的数据「对话」
你想想看,温度是几十度,振动是几个毫米/秒,压力是几百千帕。这些数据量纲不同,数值范围天差地别。如果直接扔进模型,数值大的特征会主导结果,数值小的特征就被淹没了。
所以,我们需要让它们站在同一起跑线上。常用的方法有两种:
| 方法 | 公式 | 适用场景 | 特点 |
|---|---|---|---|
| Z-score 标准化 | z = (x - μ) / σ | 数据近似正态分布,有异常值 | 不改变数据分布形状,保留异常信息 |
| Min-Max 归一化 | x' = (x - min) / (max - min) | 数据有明确边界,如0-100% | 将数据压缩到[0,1]区间,受异常值影响大 |
我个人习惯:如果后续要用到距离计算的算法(比如KNN、SVM),优先用Z-score标准化。如果数据本身有物理上下界(比如电机负载率0-100%),用Min-Max归一化更直观。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score 标准化
scaler_std = StandardScaler()
df[['temperature', 'vibration', 'pressure']] = scaler_std.fit_transform(
df[['temperature', 'vibration', 'pressure']]
)
# Min-Max 归一化
scaler_mm = MinMaxScaler()
df[['temperature', 'vibration', 'pressure']] = scaler_mm.fit_transform(
df[['temperature', 'vibration', 'pressure']]
)
3.4 实战中的「组合拳」
说了这么多,实际项目中怎么用?我一般按这个流程走:
- 第一步:概览数据。用 df.info()、df.describe() 快速了解数据全貌。
- 第二步:处理缺失值。先看缺失率,再选方法。时间序列用前向填充或插值。
- 第三步:检测异常值。画箱线图,用IQR方法标记。但先别删,留着分析。
- 第四步:标准化/归一化。根据算法需求选择方法。
- 第五步:回头处理异常值。结合业务知识,判断是剔除还是保留。
嗯,这套流程我用了好几年,基本没出过大的偏差。当然,每个项目都有它的特殊性,灵活调整才是王道。
数据清洗这件事,说白了就是「磨刀不误砍柴工」。你花80%的时间把数据洗干净,后面20%的时间建模分析,结果往往比反过来要好得多。我见过太多人急着建模,结果被脏数据坑得团团转。记住:垃圾进,垃圾出。数据干净了,预防性维护才能真正落地。
公众号:蓝海资料掘金营,微信deep3321