数据预处理实战:缺失值处理、异常值检测、数据标准化与归一化
大家好,我是老张。在风电运维这个行当摸爬滚打了十几年,今天咱们来聊聊数据预处理。说实话,很多刚入行的朋友总觉得算法模型才是核心,预处理不过是“洗洗数据”而已。我当年也这么想,直到有一次,一个看似完美的预测模型,因为没处理好缺失值,在台风天直接“翻车”了……嗯,从那以后,我再也不敢小看这一步了。
你想想看,风机SCADA系统每天产生的数据量,动辄几十万条。但现实是,传感器会坏、通信会断、数据会丢。如果直接拿这些“脏数据”去训练模型,结果可想而知。说白了,数据预处理就是给AI模型“喂饭”前的洗菜切菜环节——菜不干净,再好的厨子也白搭。
核心观点:数据预处理占整个AI项目工作量的60%-80%。模型选得再好,数据质量不行,一切都是空中楼阁。
一、缺失值处理:别让“空洞”毁了你的模型
先说说缺失值。我在项目中遇到过最典型的情况:某风场3号风机,连续一周的振动数据全是NaN。排查后发现,是振动传感器接线松了。这种问题在老旧风场特别常见。
处理缺失值,我个人习惯分三步走:
- 诊断缺失模式——是随机缺失,还是系统缺失?
- 评估缺失比例——超过30%的列,直接考虑删除
- 选择填充策略——根据业务场景定
常用填充方法对比
| 方法 | 适用场景 | 我的经验 |
|---|---|---|
| 均值/中位数填充 | 数据波动小,缺失率低 | 风速、功率这类平稳参数可以用 |
| 前向填充(ffill) | 时序数据,短时间缺失 | 温度、压力等缓变信号首选 |
| 插值法 | 非线性变化,缺失段不长 | 振动信号用线性插值效果还行 |
| 模型预测填充 | 重要参数,缺失率较高 | 比如用风速预测功率,再反推缺失值 |
小技巧:对于风速、功率这类强相关参数,我建议用“同工况均值填充”——按风速区间分组,取组内均值。比全局均值靠谱得多。
# 代码示例:缺失值处理(Python)
import pandas as pd
import numpy as np
# 读取SCADA数据
df = pd.read_csv('wind_turbine_data.csv')
# 检查缺失情况
print(df.isnull().sum())
# 前向填充(适合时序数据)
df['temperature'].fillna(method='ffill', inplace=True)
# 同工况均值填充(风速区间)
wind_bins = [0, 3, 6, 9, 12, 15, 20, 25]
df['wind_speed_bin'] = pd.cut(df['wind_speed'], bins=wind_bins)
df['power'] = df.groupby('wind_speed_bin')['power'].transform(
lambda x: x.fillna(x.mean())
)
二、异常值检测:揪出那些“不听话”的数据点
异常值检测,说白了就是找“坏点”。风机运行中,传感器偶尔会抽风——比如风速突然飙到100m/s,或者功率在额定值附近剧烈抖动。这些异常点如果不处理,模型会被带偏。
我曾经遇到过一个案例:某风场连续3个月的发电量预测偏差很大。排查到最后,发现是功率传感器在特定温度下会输出异常值。嗯,这种“软故障”最难抓。
三种常用检测方法
- 3σ原则:适合正态分布的数据。超出均值±3倍标准差的值,视为异常。
- 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
# 标记异常
df['is_anomaly'] = (df['power'] < lower_bound) | (df['power'] > upper_bound)
# 处理方式:删除或替换为边界值
df.loc[df['is_anomaly'], 'power'] = np.clip(
df.loc[df['is_anomaly'], 'power'],
lower_bound,
upper_bound
)
三、数据标准化与归一化:让所有特征“平起平坐”
为什么需要标准化?你想想看,风速是0-30m/s,功率是0-2000kW,振动加速度是0-10g。这些量纲完全不同的特征,直接扔进模型,数值大的特征会主导学习过程。说白了,就是“以大欺小”。
我个人习惯这样选:
- Min-Max归一化:适合有明确上下界的参数,比如风速、功率。把数据压缩到[0,1]区间。
- Z-score标准化:适合数据分布未知,或者存在异常值的情况。处理后均值为0,标准差为1。
- Robust标准化:用中位数和IQR代替均值和标准差。对异常值不敏感,我特别喜欢用在振动数据上。
避坑指南:我曾经犯过一个错——先拆分训练集/测试集,再对整个数据集做标准化。结果导致数据泄露,模型评估虚高。正确的做法是:先拆分,再用训练集的参数去变换测试集。
# 代码示例:标准化与归一化
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler
# 1. Min-Max归一化
scaler_minmax = MinMaxScaler()
df[['wind_speed', 'power']] = scaler_minmax.fit_transform(
df[['wind_speed', 'power']]
)
# 2. Z-score标准化
scaler_std = StandardScaler()
df[['temperature', 'vibration']] = scaler_std.fit_transform(
df[['temperature', 'vibration']]
)
# 3. Robust标准化(推荐用于振动信号)
scaler_robust = RobustScaler()
df['vibration'] = scaler_robust.fit_transform(
df[['vibration']]
)
四、知识体系总览
下面这张图,是我梳理的数据预处理核心流程。你可以把它当作操作手册——每次拿到新数据,按这个步骤走一遍,基本不会出大问题。
这张图把整个流程串起来了。你从原始数据出发,先处理缺失值,再揪出异常点,最后统一量纲。每一步都有多种方法可选,具体用哪个,得看你的数据长什么样。
我的建议:别想着一步到位。数据预处理是个迭代过程——先快速清洗一遍,跑个简单模型看看效果,再回头优化清洗策略。这样效率最高。
好了,关于数据预处理的核心内容就这些。记住一句话:垃圾进,垃圾出。花在数据清洗上的每一分钟,都会在模型效果上得到回报。下次拿到新数据,别急着调参,先把数据洗干净再说。