数据预处理实战:缺失值处理、异常值检测、数据标准化与归一化

大家好,我是老张。在风电运维这个行当摸爬滚打了十几年,今天咱们来聊聊数据预处理。说实话,很多刚入行的朋友总觉得算法模型才是核心,预处理不过是“洗洗数据”而已。我当年也这么想,直到有一次,一个看似完美的预测模型,因为没处理好缺失值,在台风天直接“翻车”了……嗯,从那以后,我再也不敢小看这一步了。

你想想看,风机SCADA系统每天产生的数据量,动辄几十万条。但现实是,传感器会坏、通信会断、数据会丢。如果直接拿这些“脏数据”去训练模型,结果可想而知。说白了,数据预处理就是给AI模型“喂饭”前的洗菜切菜环节——菜不干净,再好的厨子也白搭。

核心观点:数据预处理占整个AI项目工作量的60%-80%。模型选得再好,数据质量不行,一切都是空中楼阁。

一、缺失值处理:别让“空洞”毁了你的模型

先说说缺失值。我在项目中遇到过最典型的情况:某风场3号风机,连续一周的振动数据全是NaN。排查后发现,是振动传感器接线松了。这种问题在老旧风场特别常见。

处理缺失值,我个人习惯分三步走:

  1. 诊断缺失模式——是随机缺失,还是系统缺失?
  2. 评估缺失比例——超过30%的列,直接考虑删除
  3. 选择填充策略——根据业务场景定

常用填充方法对比

方法 适用场景 我的经验
均值/中位数填充 数据波动小,缺失率低 风速、功率这类平稳参数可以用
前向填充(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']]
)

四、知识体系总览

下面这张图,是我梳理的数据预处理核心流程。你可以把它当作操作手册——每次拿到新数据,按这个步骤走一遍,基本不会出大问题。

数据预处理核心流程 原始SCADA数据 缺失值处理 删除缺失列/行 填充缺失值 异常值检测 3σ原则 IQR四分位法 标准化 / 归一化 Min-Max Z-score 干净可用的数据

这张图把整个流程串起来了。你从原始数据出发,先处理缺失值,再揪出异常点,最后统一量纲。每一步都有多种方法可选,具体用哪个,得看你的数据长什么样。

我的建议:别想着一步到位。数据预处理是个迭代过程——先快速清洗一遍,跑个简单模型看看效果,再回头优化清洗策略。这样效率最高。

好了,关于数据预处理的核心内容就这些。记住一句话:垃圾进,垃圾出。花在数据清洗上的每一分钟,都会在模型效果上得到回报。下次拿到新数据,别急着调参,先把数据洗干净再说。

专注资料整理