数据预处理(上):数据清洗与标准化
大家好,我是老张。在风电行业摸爬滚打了十几年,又搞了几年AI算法,今天咱们聊聊数据预处理。
说实话,很多刚入行的朋友一上来就急着调模型、跑算法。我当年也犯过这个错。结果呢?模型跑出来效果差得离谱,查了半天才发现是数据本身有问题。嗯,从那以后,我养成了一个习惯——花70%的时间做数据预处理,30%的时间建模调参。
数据预处理,说白了就是给模型喂「干净饭」。你想想看,风机SCADA系统采集的数据,哪个不是带着噪声、缺失、异常?直接扔给模型,它学出来的东西能靠谱吗?
核心观点:数据预处理的质量,直接决定了故障诊断模型的性能上限。模型再牛,也救不了垃圾数据。
一、数据清洗——把脏数据洗干净
数据清洗,我习惯把它分成两大块:缺失值处理和异常值处理。这两块搞定了,数据基本就「能用」了。
1. 缺失值处理
风机SCADA系统采集数据时,经常因为传感器故障、通信中断、存储异常等原因产生缺失值。你打开数据一看,一堆NaN,怎么办?
我个人习惯先看缺失比例。如果某个特征缺失超过80%,直接扔掉。别犹豫,补了也是瞎猜。
| 缺失比例 | 处理方法 | 适用场景 |
|---|---|---|
| < 5% | 删除该行 / 均值填充 | 数据量大,缺失随机 |
| 5% - 20% | 中位数填充 / 插值法 | 数据有趋势,不宜用均值 |
| 20% - 50% | 模型预测填充 | 特征间相关性较强 |
| > 50% | 直接删除该特征 | 保留意义不大 |
我的经验:风电数据中,风速、功率、转速这些关键参数,缺失超过10%就要警惕了。我曾经处理过一个风场的数据,发现某台风机连续3天的风速数据全是NaN,后来一查,是风速仪被冻住了。这种情况,直接删除那几天的数据比强行填充更靠谱。
代码实现其实很简单,用pandas几行就搞定:
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('wind_turbine_data.csv')
# 查看缺失情况
print(df.isnull().sum())
# 方法1:删除缺失行(缺失比例小)
df_clean = df.dropna(subset=['wind_speed', 'power'])
# 方法2:均值填充
df['wind_speed'].fillna(df['wind_speed'].mean(), inplace=True)
# 方法3:前向填充(适合时序数据)
df['temperature'].fillna(method='ffill', inplace=True)
# 方法4:插值法
df['power'].interpolate(method='linear', inplace=True)
2. 异常值检测与处理
异常值,说白了就是那些「离谱」的数据点。比如风速突然飙到100m/s,或者功率为负值。这些数据要么是传感器故障,要么是通信错误。
我常用的方法有三种:
- 3σ原则:数据服从正态分布时,超出均值±3倍标准差的值视为异常。简单粗暴,适合快速筛查。
- 箱线图法:用四分位数判断,不受极端值影响。我比较喜欢这个方法,因为风电数据往往不是严格正态的。
- 领域知识法:比如风速超过切出风速(通常25m/s)就停机了,功率不可能超过额定功率的1.2倍。这些规则比任何统计方法都靠谱。
注意:别把「正常波动」当成异常。风机在启停机、变桨过程中,数据波动很大。我曾经犯过这个错,把变桨过程中的数据全删了,结果模型学不到启停机的特征。后来重新补数据,浪费了两周时间。
# 3σ法检测异常
def detect_outliers_3sigma(data, col):
mean = data[col].mean()
std = data[col].std()
lower = mean - 3 * std
upper = mean + 3 * std
return data[(data[col] < lower) | (data[col] > upper)]
# 箱线图法
def detect_outliers_iqr(data, col):
Q1 = data[col].quantile(0.25)
Q3 = data[col].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
return data[(data[col] < lower) | (data[col] > upper)]
# 处理异常:替换为边界值
df['wind_speed'] = df['wind_speed'].clip(lower=0, upper=25)
二、数据标准化与归一化
为什么要做标准化?你想想看,风速是0-25m/s,功率是0-2000kW,温度是-20到50℃。这些特征的量纲完全不同。如果不做处理,模型会天然地「偏爱」数值大的特征。
我常用的两种方法:
| 方法 | 公式 | 输出范围 | 适用场景 |
|---|---|---|---|
| Z-score 标准化 | z = (x - μ) / σ | 均值0,标准差1 | 数据近似正态分布,有异常值 |
| Min-Max 归一化 | x' = (x - min) / (max - min) | [0, 1] | 数据有明确边界,无异常值 |
我的建议:风电故障诊断中,我一般先用Z-score。因为风电数据经常有异常值,Min-Max会被异常值「拉偏」。如果数据比较干净,再用Min-Max也不迟。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score 标准化
scaler_std = StandardScaler()
df[['wind_speed', 'power', 'temperature']] = scaler_std.fit_transform(
df[['wind_speed', 'power', 'temperature']]
)
# Min-Max 归一化
scaler_mm = MinMaxScaler()
df[['wind_speed', 'power', 'temperature']] = scaler_mm.fit_transform(
df[['wind_speed', 'power', 'temperature']]
)
三、数据重采样
风电SCADA系统的采样频率五花八门。有的风场是1秒采一次,有的是10秒,还有的是1分钟。不同风机、不同时间段,采样频率可能都不一样。
重采样,就是把不同频率的数据统一到同一个时间尺度上。我一般分两种情况:
- 降采样:从高频到低频。比如把1秒数据降采样到10分钟均值。好处是数据量减少,噪声被平滑。坏处是会丢失高频信息。
- 升采样:从低频到高频。比如把10分钟数据插值到1分钟。好处是时间分辨率提高。坏处是「无中生有」,引入了不确定性。
我的经验:做故障诊断时,我通常用10分钟均值数据。为什么?因为风机的机械响应时间在分钟级别,1秒数据里大部分是湍流噪声,反而会干扰模型。当然,如果你要做叶片结冰检测这种需要高频信号的,那就得用原始高频数据了。
# 假设数据索引是时间戳
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)
# 降采样:10分钟均值
df_10min = df.resample('10T').mean()
# 降采样:1小时中位数(抗异常值)
df_1h = df.resample('1H').median()
# 升采样:线性插值到1分钟
df_1min = df.resample('1T').interpolate(method='linear')
# 升采样:前向填充
df_1min_ffill = df.resample('1T').ffill()
嗯,到这里,数据预处理的「上半场」就差不多了。总结一下:先清洗(缺失值、异常值),再标准化(Z-score或Min-Max),最后重采样(统一时间尺度)。这个顺序别搞反了,不然会出问题。
下一节咱们接着聊数据预处理的「下半场」——特征工程。到时候我会讲讲怎么从原始数据里挖出有用的特征,让模型「开窍」。