数据预处理(上):数据清洗与标准化

大家好,我是老张。在风电行业摸爬滚打了十几年,又搞了几年AI算法,今天咱们聊聊数据预处理。

说实话,很多刚入行的朋友一上来就急着调模型、跑算法。我当年也犯过这个错。结果呢?模型跑出来效果差得离谱,查了半天才发现是数据本身有问题。嗯,从那以后,我养成了一个习惯——花70%的时间做数据预处理,30%的时间建模调参。

数据预处理,说白了就是给模型喂「干净饭」。你想想看,风机SCADA系统采集的数据,哪个不是带着噪声、缺失、异常?直接扔给模型,它学出来的东西能靠谱吗?

核心观点:数据预处理的质量,直接决定了故障诊断模型的性能上限。模型再牛,也救不了垃圾数据。

数据预处理知识体系 数据预处理 数据清洗 标准化与归一化 数据重采样 缺失值处理 异常值检测 Z-score 标准化 Min-Max 归一化 降采样 升采样 三大核心步骤:清洗 → 标准化 → 重采样 缺一不可,顺序不能乱

一、数据清洗——把脏数据洗干净

数据清洗,我习惯把它分成两大块:缺失值处理和异常值处理。这两块搞定了,数据基本就「能用」了。

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),最后重采样(统一时间尺度)。这个顺序别搞反了,不然会出问题。

下一节咱们接着聊数据预处理的「下半场」——特征工程。到时候我会讲讲怎么从原始数据里挖出有用的特征,让模型「开窍」。


专注资料整理