4、数据预处理基础:缺失值处理、异常值检测、数据标准化与归一化
各位同行,咱们直接进入正题。
数据预处理,说白了就是给数据“洗澡”。你从SCADA系统或者CMS系统里拉出来的原始数据,基本没法直接用。我干了这么多年风电运维,见过太多因为数据没处理好,导致诊断模型“翻车”的案例。今天咱们就把这“洗澡”的四个关键步骤捋清楚。
核心逻辑:原始数据 → 缺失值处理 → 异常值剔除 → 数据变换 → 干净可用的数据集
4.1 缺失值处理——别让“空”坑了你
风机数据里出现缺失值,太常见了。传感器掉线、通讯中断、存储故障,随便一个原因就能给你留下一串NaN。我个人习惯,拿到数据第一件事就是跑个df.isnull().sum(),看看哪些列“缺”得厉害。
处理策略就三种:
- 直接删除:如果缺失比例小于5%,而且数据量够大,直接删行。简单粗暴,但有效。
- 填充法:连续型数据用均值或中位数填充。我建议用中位数,因为均值容易被异常值带偏。你想想看,风速传感器偶尔跳个60m/s,均值直接废了。
- 前向/后向填充:时间序列数据,比如10分钟平均风速,用前一个值填充后一个空值,很合理。
我的经验:千万别用0去填充缺失值!我曾经有个徒弟,把缺失的功率数据全填了0,结果模型训练出来,预测功率全是负数……嗯,那画面太美我不敢看。
# 缺失值处理示例
import pandas as pd
import numpy as np
# 生成示例数据
data = {'风速': [5.2, 6.1, np.nan, 4.8, 7.3, np.nan, 5.5],
'功率': [1200, 1450, 1100, np.nan, 1700, 1300, 1250]}
df = pd.DataFrame(data)
# 查看缺失情况
print(df.isnull().sum())
# 中位数填充
df['风速'].fillna(df['风速'].median(), inplace=True)
# 前向填充
df['功率'].fillna(method='ffill', inplace=True)
print(df)
4.2 异常值检测——揪出“捣乱分子”
异常值,就是那些明显偏离正常范围的数据点。比如风速突然飙到80m/s,或者发电机温度在0℃以下还显示150℃。这些数据不处理,模型学到的全是“错误经验”。
我常用的方法有两个,都是统计学里的经典套路。
4.2.1 3σ原则(拉依达准则)
这个原则假设数据服从正态分布。均值±3倍标准差之外的数据,就是异常值。说白了,概率小于0.3%的事件,咱们就当它没发生过。
# 3σ原则检测异常值
def detect_outliers_3sigma(data_series):
mean = data_series.mean()
std = data_series.std()
lower_bound = mean - 3 * std
upper_bound = mean + 3 * std
outliers = data_series[(data_series < lower_bound) | (data_series > upper_bound)]
return outliers
# 示例
wind_speed = [5.2, 6.1, 4.8, 7.3, 5.5, 62.0, 5.0] # 62.0是异常值
outliers = detect_outliers_3sigma(pd.Series(wind_speed))
print("异常值:", outliers.tolist())
注意:3σ原则对数据分布有要求。如果数据本身偏态严重,比如振动信号,用这个方法会误杀很多正常点。我遇到过这种情况,后来改用IQR了。
4.2.2 IQR(四分位距法)
IQR不依赖正态分布,鲁棒性更好。它用Q1(25%分位数)和Q3(75%分位数)来定义正常范围。低于Q1-1.5×IQR或高于Q3+1.5×IQR的,就是异常值。
# IQR法检测异常值
def detect_outliers_iqr(data_series):
Q1 = data_series.quantile(0.25)
Q3 = data_series.quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data_series[(data_series < lower_bound) | (data_series > upper_bound)]
return outliers
# 示例
vibration_data = [0.5, 0.6, 0.4, 0.7, 0.5, 3.2, 0.6] # 3.2是异常值
outliers = detect_outliers_iqr(pd.Series(vibration_data))
print("异常值:", outliers.tolist())
对比总结:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 3σ原则 | 数据近似正态分布 | 计算简单,理论成熟 | 对偏态数据不友好 |
| IQR法 | 任意分布,尤其是偏态数据 | 鲁棒性强,不受极端值影响 | 对样本量有一定要求 |
4.3 数据标准化与归一化——让不同量纲的数据“对话”
风机的数据,量纲五花八门。风速是m/s,温度是℃,振动是mm/s,功率是kW。如果不做变换,直接扔进模型,数值大的特征会主导学习过程。说白了,就是“以大欺小”。
4.3.1 Z-score标准化
把数据变成均值为0、标准差为1的分布。公式很简单:z = (x - μ) / σ。我一般用这个方法,因为它保留了原始数据的分布形态。
from sklearn.preprocessing import StandardScaler
# Z-score标准化
data = [[5.2, 1200], [6.1, 1450], [4.8, 1100], [7.3, 1700]]
scaler = StandardScaler()
scaled_data = scaler.fit_transform(data)
print(scaled_data)
4.3.2 Min-Max归一化
把数据压缩到[0,1]区间。公式:x_norm = (x - min) / (max - min)。这个方法对异常值敏感,但如果你知道数据的上下限(比如风速0-30m/s),用起来很直观。
from sklearn.preprocessing import MinMaxScaler
# Min-Max归一化
data = [[5.2, 1200], [6.1, 1450], [4.8, 1100], [7.3, 1700]]
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(data)
print(scaled_data)
我的建议:如果后续要用PCA、SVM这类对尺度敏感的算法,优先用Z-score。如果是神经网络,Min-Max更常见。但记住,标准化参数(均值、标准差、min、max)一定要从训练集计算,然后应用到测试集。我见过有人把整个数据集一起标准化,结果模型在线上部署时直接崩了——因为新数据的min/max跟训练集不一样。
4.4 实战避坑指南
- 顺序问题:先处理缺失值,再做异常值检测。因为缺失值会影响均值和标准差的计算。
- 不要盲目删除:异常值不一定是错误。比如风机切出风速时的功率骤降,那是正常行为。我曾经把这种“异常”删了,结果模型学不会高风速段的特性。
- 标准化后检查:做完标准化,随手画个直方图看看分布。如果数据还是偏态严重,考虑做对数变换或Box-Cox变换。
好了,数据预处理这块就聊到这儿。这些基础打牢了,后面的诊断模型才能站得住脚。