3. 数据预处理基础:缺失值处理、异常值检测、数据标准化与归一化
各位同行,大家好。我是老张,在风场摸爬滚打了十来年,后来又搞了几年AI算法。今天咱们聊聊数据预处理。
很多人觉得,模型搭得好,结果就漂亮。其实不然。我见过太多项目,算法选得再牛,数据一塌糊涂,最后全白搭。说白了,数据预处理就是给模型「喂饭」。饭馊了,再好的厨师也做不出好菜。
咱们风机数据,尤其如此。SCADA系统采集的数据,那叫一个「脏」。传感器漂移、通讯中断、极端工况……各种问题。今天我就把压箱底的经验掏出来,跟大伙儿聊聊这三板斧:缺失值、异常值、标准化。
3.1 缺失值处理:别让「空」坑了模型
风机数据里,缺失值太常见了。通讯闪断、传感器故障、维护停机……都会导致数据点丢失。我刚开始做预测时,直接删掉了所有含缺失值的行。结果呢?模型训练出来,一到雷雨天气就失灵。后来才发现,我把故障前的关键数据全删了。
3.1.1 常见的缺失值处理方法
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 直接删除 | 缺失比例 < 5%,且随机分布 | 简单粗暴 | 丢失信息,破坏时序 |
| 均值/中位数填充 | 数值型特征,缺失比例不高 | 计算快,不影响分布 | 降低方差,可能引入偏差 |
| 前向/后向填充 | 时序数据,短时间缺失 | 保持趋势 | 长缺失段效果差 |
| 插值法 | 时序数据,缺失段平滑 | 拟合自然 | 计算量大 |
| 模型预测填充 | 重要特征,缺失比例高 | 精度高 | 耗时,可能过拟合 |
我个人习惯,对于风机SCADA数据,优先用前向填充。为什么?因为传感器数据变化是连续的,前一秒的值最接近下一秒。比如风速,不会突然从5m/s跳到15m/s。当然,如果缺失超过10个连续点,我会改用线性插值。
import pandas as pd
import numpy as np
# 读取风机数据
df = pd.read_csv('wind_turbine_data.csv')
# 查看缺失情况
print(df.isnull().sum())
# 前向填充(ffill)—— 我最常用的方法
df['wind_speed'] = df['wind_speed'].ffill()
# 如果缺失较多,用线性插值
df['power_output'] = df['power_output'].interpolate(method='linear')
# 标记缺失位置(可选)
df['is_missing'] = df['wind_speed'].isnull().astype(int)
3.2 异常值检测:揪出「捣乱」的数据点
异常值,说白了就是「不听话」的数据。风机运行中,传感器偶尔会抽风,比如风速突然显示99m/s,或者功率变成负数。这些数据如果不处理,模型会学歪。
我曾经遇到过一个案例:某台风机振动值突然飙升,但SCADA系统没报警。我查了原始数据,发现是传感器接线松动,导致信号跳变。如果当时用异常值检测,就能提前发现,避免误判。
3.2.1 三种常用检测方法
方法一:3σ原则(拉依达准则)
假设数据服从正态分布,超出均值±3倍标准差的值视为异常。简单有效,但要求数据分布不能太偏。
def detect_outliers_3sigma(data, threshold=3):
mean = np.mean(data)
std = np.std(data)
outliers = np.abs(data - mean) > threshold * std
return outliers
# 对风速做检测
df['wind_outlier'] = detect_outliers_3sigma(df['wind_speed'])
print(f"检测到 {df['wind_outlier'].sum()} 个异常风速点")
方法二:IQR(四分位距法)
不依赖正态分布,用Q1和Q3的差值判断。鲁棒性更强,适合偏态分布。我个人更推荐这个方法,因为风机数据往往不是正态的。
def detect_outliers_iqr(data):
Q1 = data.quantile(0.25)
Q3 = data.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
return (data < lower) | (data > upper)
df['power_outlier'] = detect_outliers_iqr(df['power_output'])
方法三:孤立森林(Isolation Forest)
这是机器学习方法,适合高维数据。它不假设分布,通过随机切割来「孤立」异常点。我一般在做多特征联合检测时用,比如同时看风速、功率、桨距角。
from sklearn.ensemble import IsolationForest
# 多特征联合检测
features = ['wind_speed', 'power_output', 'pitch_angle']
iso_forest = IsolationForest(contamination=0.05, random_state=42)
df['iso_outlier'] = iso_forest.fit_predict(df[features])
# -1 表示异常,1 表示正常
df['iso_outlier'] = df['iso_outlier'].map({1: 0, -1: 1})
3.3 数据标准化与归一化:让特征「平起平坐」
你想想看,风速范围0-30m/s,功率范围0-2000kW,温度范围-20-50℃。这些特征量纲不同,直接丢进模型,数值大的特征会主导学习过程。尤其是神经网络和距离类算法(如KNN、SVM),对尺度特别敏感。
3.3.1 归一化(Min-Max Scaling)
把数据压缩到[0,1]区间。公式很简单:(x - min) / (max - min)。适合数据有明确边界的情况,比如风速、温度。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df[['wind_speed', 'power_output']] = scaler.fit_transform(
df[['wind_speed', 'power_output']]
)
3.3.2 标准化(Z-score)
把数据变成均值为0、标准差为1的分布。公式:(x - mean) / std。适合数据可能有异常值的情况,因为它不依赖最大值最小值。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df[['vibration_x', 'vibration_y']] = scaler.fit_transform(
df[['vibration_x', 'vibration_y']]
)
3.3.3 鲁棒标准化(Robust Scaling)
用中位数和IQR代替均值和标准差。对异常值不敏感。我处理振动数据时特别喜欢用这个,因为振动信号经常有尖峰。
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
df[['vibration_z']] = scaler.fit_transform(df[['vibration_z']])
3.4 预处理流程总结
好了,咱们捋一捋。数据预处理不是随便搞搞,得有章法。我总结了一个四步流程:
- 先看数据概貌:用
df.describe()和df.info()了解缺失、分布、范围。 - 处理缺失值:时序数据用前向填充,非时序用中位数填充。
- 检测异常值:单特征用IQR,多特征用孤立森林。标记但不一定删除。
- 标准化/归一化:根据模型选择。树模型可以不标准化,但神经网络必须做。
嗯,这里要注意:预处理顺序不能乱。先标准化再做异常检测?那异常值会被「抹平」,就检测不出来了。所以一定是:缺失值 → 异常值 → 标准化。
最后说一句,数据预处理没有「万能药」。每个风场、每台机组都有自己的脾气。多看看数据分布,多试试不同方法,慢慢就有感觉了。
- 缺失值:前向填充是时序数据的首选
- 异常值:IQR法鲁棒性好,孤立森林适合多特征
- 标准化:Z-score通用,Min-Max适合有边界数据
- 顺序:缺失值 → 异常值 → 标准化,不可颠倒
公众号:蓝海资料掘金营,微信deep3321