数据预处理基础:缺失值处理、异常值检测、数据归一化与标准化方法

各位同行,大家好。今天我们来聊聊数据预处理。说实话,在风电场雷击故障诊断这个领域,我踩过的坑有一半都出在数据预处理上。你想想看,风机SCADA系统采集的数据,那叫一个「脏」——传感器漂移、通信中断、雷击瞬间的电磁干扰……这些都会让原始数据变得乱七八糟。

我个人的习惯是:拿到数据后,先别急着建模。先花70%的时间把数据洗干净。为什么?因为「垃圾进,垃圾出」。你模型再牛,喂进去的是脏数据,出来的结果也是废的。

核心观点:数据预处理不是「锦上添花」,而是「雪中送炭」。在雷击故障诊断中,预处理做得好,模型准确率能提升20%以上。

数据预处理知识体系 数据预处理 缺失值处理 删除法 均值/中位数填充 插值法 KNN填充 异常值检测 3σ原则 箱线图法 DBSCAN聚类 孤立森林 归一化与标准化 Min-Max归一化 Z-score标准化 Robust标准化 MaxAbs归一化 目标:干净、一致、可建模的数据

一、缺失值处理——别让「空」坑了你

雷击故障数据里,缺失值太常见了。我记得有一次,某风场的数据集里,雷电流幅值这一列有将近30%的缺失。原因很简单——雷击瞬间传感器被强电磁场干扰,直接「死机」了。

处理缺失值,我一般分三步走:

  1. 先诊断——看看缺失是随机的,还是系统性的
  2. 再决策——缺失比例低就删,比例高就补
  3. 最后动手——选对填充方法

我的经验:如果某列缺失超过50%,我建议直接扔掉。别心疼,留着反而是噪音。但如果缺失在20%以内,优先用插值法,尤其是时间序列数据。

常用的方法有这些:

方法 适用场景 缺点
删除法 缺失比例<5%,且随机缺失 样本量减少,可能丢失信息
均值/中位数填充 数值型特征,缺失比例低 会降低方差,影响模型
前向/后向填充 时间序列数据(如风速、功率) 对突变不敏感
线性插值 连续变化的数据 计算量稍大
KNN填充 特征间相关性强的场景 数据量大时很慢

代码实现其实很简单。我习惯用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=['雷电流幅值'])

# 方法2:均值填充
df['风速'].fillna(df['风速'].mean(), inplace=True)

# 方法3:前向填充(时间序列常用)
df['有功功率'].fillna(method='ffill', inplace=True)

# 方法4:线性插值(我最推荐)
df['雷电流幅值'] = df['雷电流幅值'].interpolate(method='linear')

避坑指南:我曾经犯过一个错误——对雷击故障标记(0/1标签)也做了均值填充。结果模型训练出来,预测值全是小数,根本没法用。记住:分类标签的缺失,要么删,要么用众数填充,千万别用均值!

二、异常值检测——揪出「捣乱分子」

风电场的数据里,异常值特别「狡猾」。有些是传感器故障导致的,比如风速突然飙到100m/s——这明显不合理。但有些异常值,其实是雷击故障的真实信号,比如电压骤降、电流尖峰。

所以我的原则是:先理解业务,再动手检测。别一上来就把所有「离群点」都干掉,那可能会把故障特征也删掉了。

常用的检测方法:

  • 3σ原则:假设数据服从正态分布,超出均值±3倍标准差的就是异常。简单粗暴,但有效。
  • 箱线图法:用四分位数来界定异常。不受极端值影响,更稳健。
  • DBSCAN聚类:基于密度的聚类,把稀疏区域的点标记为异常。适合形状复杂的数据。
  • 孤立森林:随机森林的变体,专门用于异常检测。高维数据表现不错。

我个人的习惯是:先用箱线图快速扫一遍,再用孤立森林做精细检测。双保险。

from sklearn.ensemble import IsolationForest
import matplotlib.pyplot as plt

# 箱线图快速查看
df.boxplot(column=['风速', '有功功率', '雷电流幅值'])
plt.show()

# 孤立森林检测
model = IsolationForest(contamination=0.05, random_state=42)
df['异常标记'] = model.fit_predict(df[['风速', '有功功率', '雷电流幅值']])

# 查看异常样本
anomalies = df[df['异常标记'] == -1]
print(f"检测到 {len(anomalies)} 个异常样本")

一个小技巧:对于雷电流幅值这种特征,我通常会结合物理知识设定上下限。比如,雷电流幅值一般在10kA到200kA之间,超出这个范围的,基本可以判定为传感器异常。这种「业务规则+算法」的组合,效果最好。

三、数据归一化与标准化——让特征「平起平坐」

你想想看,风速的单位是m/s,数值在0-30之间;有功功率的单位是kW,数值在0-2000之间。如果直接丢进模型,模型会「偏心」——它会更关注数值大的特征,而忽略数值小的特征。

归一化和标准化,说白了就是给所有特征「统一度量衡」。我常用的方法有:

方法 公式 输出范围 适用场景
Min-Max归一化 (x - min) / (max - min) [0, 1] 数据有明确边界,如温度、风速
Z-score标准化 (x - μ) / σ 无固定范围,均值为0 数据近似正态分布,如功率波动
Robust标准化 (x - median) / IQR 无固定范围 数据有较多异常值
MaxAbs归一化 x / |max| [-1, 1] 稀疏数据或中心在0附近

嗯,这里要注意:归一化和标准化,一定要先拆分训练集和测试集,再分别做。我见过有人把整个数据集一起归一化,然后才拆分——这会导致数据泄露,模型评估结果虚高。

from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler
from sklearn.model_selection import train_test_split

# 拆分数据
X = df[['风速', '有功功率', '雷电流幅值']]
y = df['雷击故障标记']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 方法1:Min-Max归一化
scaler_minmax = MinMaxScaler()
X_train_scaled = scaler_minmax.fit_transform(X_train)
X_test_scaled = scaler_minmax.transform(X_test)  # 注意:用训练集的参数

# 方法2:Z-score标准化
scaler_std = StandardScaler()
X_train_std = scaler_std.fit_transform(X_train)
X_test_std = scaler_std.transform(X_test)

# 方法3:Robust标准化(抗异常值)
scaler_robust = RobustScaler()
X_train_robust = scaler_robust.fit_transform(X_train)
X_test_robust = scaler_robust.transform(X_test)

避坑指南:我曾经在某个项目里,对雷击故障标签也做了归一化。结果模型输出变成了0.3、0.7这种小数,完全没法解释。记住:只对特征做归一化/标准化,标签(目标变量)不要动。除非你做的是回归任务,而且标签量级差异很大,那可以考虑对标签也做标准化,但预测完要记得反变换回来。

四、实战中的「组合拳」

在实际的风电项目中,我通常会把这三个步骤串起来,形成一个预处理流水线:

  1. 第一步:缺失值处理——先用线性插值填充时间序列特征,再用均值填充其他数值特征
  2. 第二步:异常值检测——用孤立森林标记异常,然后结合业务知识判断是保留还是删除
  3. 第三步:标准化——用RobustScaler,因为风电数据里异常值比较多,RobustScaler更抗造

举个例子,有一次某风场的数据集里,雷电流幅值出现了几个「0」值。按理说,雷电流不可能是0。我查了一下,发现是传感器在雷击瞬间被烧毁了,导致后续记录的全是0。这种情况,我直接把这些样本标记为「传感器故障」,从训练集中剔除。你看,这就是业务知识的重要性。

总结一下:数据预处理没有「万能公式」,关键是根据数据特点和你对业务的理解,灵活组合各种方法。多试、多看、多验证——这才是资深工程师的做法。

专注资料整理