数据预处理:数据清洗、对齐、重采样与标准化
数据预处理这事儿,我做了十年风电预测,一直觉得它是整个项目里最「脏」的活。但恰恰是这步,决定了模型的上限。你想想看,数据质量不行,再牛的算法也是白搭。
今天咱们就聊聊数据预处理的几个核心环节。我按自己的习惯,把它们分成四块:清洗、对齐、重采样、标准化。每一块都有坑,我踩过不少,今天一并告诉你。
1. 数据清洗:异常值与缺失值
先说异常值。风电数据里,异常值太常见了。比如风速突然跳到 50m/s,或者功率变成负数。这些明显不合理的数据,必须处理掉。
异常值检测方法,我个人常用三种:
- 3σ 原则:数据服从正态分布时,超出均值±3倍标准差的值视为异常。简单粗暴,但有效。
- IQR 方法:用四分位距判断。低于 Q1-1.5×IQR 或高于 Q3+1.5×IQR 的,直接标记。
- 业务规则:比如风速不能超过风机切出风速,功率不能超过额定功率。这个最靠谱。
重要提醒:异常值不一定要删除。有时候异常值代表真实事件(比如极端天气),直接删掉反而丢失信息。我建议先标记,再根据业务判断。
再说缺失值。风电数据缺失的原因很多:传感器故障、通信中断、维护停机。处理方式有几种:
- 删除法:缺失比例小于 5% 时,直接删掉对应行。简单,但别滥用。
- 填充法:均值填充、中位数填充、前向填充(ffill)、后向填充(bfill)。
- 插值法:线性插值、样条插值。时间序列数据用这个效果不错。
- 模型预测:用其他特征预测缺失值。这个最复杂,但精度最高。
# 我常用的缺失值处理代码
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('wind_power.csv', parse_dates=['time'])
# 检查缺失率
missing_rate = df.isnull().sum() / len(df)
print('缺失率:\n', missing_rate)
# 处理异常值:风速超过30m/s视为异常
df.loc[df['wind_speed'] > 30, 'wind_speed'] = np.nan
# 缺失值填充:用前后均值插值
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')
df['power'] = df['power'].interpolate(method='linear')
# 如果连续缺失超过3个点,用前向填充
df = df.fillna(method='ffill', limit=3)
我的经验:我曾经遇到一个项目,数据缺失率高达 30%。一开始我用均值填充,结果模型效果很差。后来发现,缺失时段往往是风机停机维护,功率本来就是 0。改成用 0 填充后,模型精度提升了 15%。所以,一定要结合业务场景。
2. 时间序列对齐
风电数据通常来自多个传感器,采样频率可能不一样。比如风速仪每 10 秒采一次,功率表每 1 分钟采一次。直接合并会出问题。
对齐的核心思路:统一时间戳。我一般这么做:
- 确定目标时间频率(比如 10 分钟一个点)。
- 把所有数据重采样到这个频率上。
- 用插值或聚合方法填充缺失的时间点。
# 时间对齐示例
# 假设有两个数据源:风速(10秒间隔)、功率(1分钟间隔)
wind_df = pd.read_csv('wind_speed.csv', parse_dates=['time'])
power_df = pd.read_csv('power.csv', parse_dates=['time'])
# 设置时间索引
wind_df.set_index('time', inplace=True)
power_df.set_index('time', inplace=True)
# 重采样到10分钟
wind_10min = wind_df.resample('10T').mean()
power_10min = power_df.resample('10T').mean()
# 合并对齐后的数据
aligned_df = pd.merge(wind_10min, power_10min, left_index=True, right_index=True, how='inner')
注意:对齐时要注意时区问题。我见过有人把 UTC 时间和本地时间混在一起,结果模型训练出来全是错的。建议统一用 UTC 时间,或者明确标注时区。
3. 重采样
重采样说白了就是改变数据的时间粒度。风电预测中,常见的有:
- 降采样:从高频到低频(比如 1 分钟 → 10 分钟)。用均值、中位数或最大值聚合。
- 升采样:从低频到高频(比如 1 小时 → 10 分钟)。用插值或填充。
我个人习惯用 10 分钟粒度做短期预测,1 小时粒度做中期预测。为什么?因为 10 分钟能捕捉到风速的快速变化,而 1 小时更适合看趋势。
# 重采样示例
# 降采样:1分钟数据 -> 10分钟均值
df_10min = df.resample('10T').mean()
# 升采样:1小时数据 -> 10分钟线性插值
df_hourly = df.resample('1H').mean()
df_10min_up = df_hourly.resample('10T').interpolate(method='linear')
避坑指南:我曾经在重采样时用了「最大值」聚合,结果模型预测值总是偏高。后来发现,最大值会放大极端风速的影响。对于功率预测,均值聚合通常更稳定。
4. 数据标准化与归一化
这一步很多人会忽略,但我觉得特别重要。不同特征的量纲不一样:风速是 0-30 m/s,功率是 0-1500 kW,温度是 -10-40°C。如果不做处理,模型会偏向数值大的特征。
两种常用方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| 标准化(Z-score) | x' = (x - μ) / σ | 数据近似正态分布,或使用 SVM、线性回归等模型 |
| 归一化(Min-Max) | x' = (x - min) / (max - min) | 数据有明确边界,或使用神经网络、深度学习 |
# 标准化与归一化代码
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# 标准化
scaler_std = StandardScaler()
df['wind_speed_std'] = scaler_std.fit_transform(df[['wind_speed']])
# 归一化
scaler_mm = MinMaxScaler(feature_range=(0, 1))
df['power_norm'] = scaler_mm.fit_transform(df[['power']])
重要:标准化和归一化的参数(均值、标准差、最小值、最大值)必须用训练集计算,然后应用到验证集和测试集。千万不能在整个数据集上统一计算,否则会造成数据泄露。
嗯,这里要注意一点:如果你用深度学习模型(比如 LSTM),归一化到 [0,1] 或 [-1,1] 效果更好。如果是树模型(比如 XGBoost),标准化与否影响不大,但归一化有时反而会降低性能。
知识体系总览
下面这张图,是我自己总结的数据预处理流程。你可以把它当作一个检查清单:
数据预处理这步,我建议你花 60% 的时间。模型调参可能只占 20%,但数据质量决定了那 80% 的上限。你想想看,如果数据本身就有问题,模型再复杂也救不回来。
最后一个小建议:每次做完预处理,都画一张图看看。把原始数据和预处理后的数据叠在一起,一眼就能看出问题。我习惯用 matplotlib 的 subplot 对比显示,效果很直观。
公众号:蓝海资料掘金营,微信deep3321