数据预处理与清洗:让原始数据变得可靠

各位工程师朋友,大家好。今天我们来聊聊数据预处理与清洗。说实话,我在风电行业摸爬滚打这些年,见过太多因为数据没处理好导致模型翻车的案例。你想想看,风机载荷预测模型再牛,喂进去的是脏数据,出来的结果能信吗?

我个人习惯,拿到原始数据后,第一件事不是急着建模,而是先花 60% 的时间做数据清洗。这步省了,后面全是坑。

数据预处理与清洗 缺失值处理 插值法 前向填充 异常值检测 3σ原则 IQR方法 归一化与标准化 Min-Max归一化 Z-score标准化 时间序列重采样

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

风机SCADA数据里,缺失值太常见了。传感器故障、通信中断、存储异常……原因五花八门。我遇到过最夸张的一次,某风场连续3天的风速数据全是NaN,排查后发现是采集卡接触不良。

处理缺失值,我常用的方法就两种:

1. 插值法

说白了就是用已知数据去「猜」缺失的值。线性插值适合变化平缓的变量,比如环境温度。但风速这种波动大的,我建议用样条插值。

import pandas as pd
import numpy as np

# 线性插值
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')

# 样条插值(二阶)
df['wind_speed'] = df['wind_speed'].interpolate(method='spline', order=2)
我的经验:连续缺失超过5个点,插值结果就不太靠谱了。这时候我宁愿用前向填充,或者直接标记为无效。

2. 前向填充

这个方法简单粗暴——用上一个有效值填充后面的缺失值。适合传感器短暂掉线的情况。比如叶片角度传感器偶尔丢一两个数据点,用前向填充问题不大。

# 前向填充
df['blade_angle'] = df['blade_angle'].fillna(method='ffill')

# 限制最多填充3个连续缺失
df['blade_angle'] = df['blade_angle'].fillna(method='ffill', limit=3)
注意:前向填充不能滥用。如果缺失段太长,填充出来的数据会引入偏差。我曾经见过有人用前向填充补了半小时的数据,结果模型预测的载荷完全偏离实际。

二、异常值检测:揪出那些「离谱」的数据点

异常值是什么?就是那些明显不符合物理规律的数据。比如风速突然从10m/s跳到50m/s,或者发电机转速变成负数。这些数据不处理,模型学到的全是错误规律。

1. 3σ原则

这个方法假设数据服从正态分布。超出均值±3倍标准差的值,视为异常。简单好用,但有个前提——数据不能有太多离群点,否则均值和标准差本身就被污染了。

def detect_outliers_3sigma(data, col):
    mean = data[col].mean()
    std = data[col].std()
    lower = mean - 3 * std
    upper = mean + 3 * std
    outliers = data[(data[col] < lower) | (data[col] > upper)]
    return outliers

# 检测风速异常
outliers = detect_outliers_3sigma(df, 'wind_speed')
print(f"检测到 {len(outliers)} 个异常值")

实际案例:某次我处理一个风场的数据,用3σ检测发现大量风速异常。后来发现是台风过境期间的真实数据,不是传感器故障。所以,异常值不一定是「坏」数据,关键看上下文。

2. IQR方法

IQR(四分位距法)对数据分布不那么敏感,更稳健。它用Q1和Q3的差值来界定异常范围。我个人更偏爱这个方法,尤其是处理载荷数据时。

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
    outliers = data[(data[col] < lower) | (data[col] > upper)]
    return outliers

# 检测叶片弯矩异常
outliers = detect_outliers_iqr(df, 'blade_moment')
避坑指南:我曾经用IQR处理塔筒振动数据,发现异常值特别多。后来一查,原来是风机在特定工况下(比如变桨动作)振动本来就大。所以,建议按工况分组后再做异常检测。

三、数据归一化与标准化:让不同量纲的数据「对话」

风速单位是m/s,载荷单位是kN·m,温度单位是℃。这些数据直接扔进模型,数值大的特征会主导学习过程。归一化和标准化就是解决这个问题的。

1. Min-Max归一化

把数据缩放到[0,1]区间。适合数据有明确边界的情况,比如风向角度(0-360°)。

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
df[['wind_speed', 'power']] = scaler.fit_transform(df[['wind_speed', 'power']])

2. Z-score标准化

把数据变成均值为0、标准差为1的分布。适合数据可能有异常值的情况,因为它不依赖最大值最小值。

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
df[['blade_moment', 'tower_acceleration']] = scaler.fit_transform(
    df[['blade_moment', 'tower_acceleration']]
)
注意:归一化和标准化要在划分训练集/测试集之后做。先拆分,再用训练集的参数去变换测试集。否则会造成数据泄露,模型评估结果虚高。

四、时间序列重采样:统一数据的时间步长

风机SCADA数据采集频率经常不一致。有的传感器10Hz,有的1Hz,还有的按分钟记录。重采样就是把它们对齐到统一的时间频率上。

我常用的重采样策略:

重采样方式 适用场景 示例
降采样(聚合) 高频数据转低频 10Hz → 1Hz,取均值
升采样(插值) 低频数据转高频 1分钟 → 10秒,线性插值
对齐到整点 多源数据时间戳对齐 统一到整分钟时刻
# 降采样:10Hz数据聚合为1Hz(取均值)
df_resampled = df.resample('1S').mean()

# 升采样:1分钟数据插值为10秒
df_resampled = df.resample('10S').interpolate(method='linear')

# 对齐到整分钟
df_resampled = df.resample('1T', label='right', closed='right').mean()
我的习惯:做载荷预测时,我通常把数据重采样到1Hz。这个频率既能捕捉到主要的动态特性,又不会产生太多噪声。当然,如果你做的是高频振动分析,那可能需要10Hz甚至更高。

好了,数据预处理这块就聊到这儿。这些方法看着简单,但真正用好需要大量实践。记住一句话:数据清洗花的时间,会在模型效果上加倍回报。


公众号:蓝海资料掘金营,微信deep3321