数据预处理:风电功率预测的基石

大家好,我是老张。在风电功率预测这个行当摸爬滚打了十来年,我越来越觉得:数据预处理做得好,模型就成功了一半。这话一点都不夸张。

你想想看,风机采集到的原始数据,那叫一个「脏」。传感器故障、通信中断、极端天气干扰……各种问题层出不穷。如果直接拿这些数据去训练模型,结果可想而知——预测值忽高忽低,调度部门天天找你喝茶。

今天这一章,我们就来聊聊数据预处理的三个核心环节:缺失值处理、异常值检测与剔除、时间戳对齐与重采样。这些都是我每天在项目中反复操练的内容,希望能帮你少走些弯路。

核心观点:数据预处理不是「洗菜」,而是「炼钢」。好的预处理能提升模型精度5%-15%,这个提升幅度,有时候比换一个更复杂的模型还要大。

数据预处理知识体系 数据预处理 缺失值处理 向前填充 插值法 异常值检测与剔除 3-sigma原则 箱线图法 时间戳对齐与重采样 对齐 重采样 目标:构建干净、连续、对齐的时间序列数据

3.1 缺失值处理:别让「空」数据毁了你的模型

风电数据里,缺失值是最常见的问题。传感器偶尔抽风、通信链路不稳定、维护期间停机……都会导致数据出现空洞。我个人习惯,拿到数据第一件事就是统计缺失率。

小技巧:df.isnull().sum() / len(df) 快速查看每列的缺失比例。如果某列缺失超过50%,我通常会直接扔掉——补出来的数据也不靠谱。

3.1.1 向前填充(Forward Fill)

向前填充,说白了就是用上一个有效值来填补当前的缺失值。这个方法在风电场景下特别实用——因为风速、功率这些物理量,短时间内变化不会太剧烈。

我在项目中遇到过这样的情况:某台风机因为通信故障,连续缺失了5分钟的数据。用向前填充补上后,模型预测误差只增加了不到2%。但如果缺失时间超过15分钟,我就不建议用这个方法了。

import pandas as pd

# 向前填充
df['power'].fillna(method='ffill', inplace=True)

# 限制最大填充步数(防止过度填充)
df['power'].fillna(method='ffill', limit=3, inplace=True)

注意:如果数据开头就是缺失值,向前填充会失效。这时候我一般会结合向后填充一起用,或者直接用插值法。

3.1.2 插值法(Interpolation)

插值法比向前填充更「聪明」一些。它会根据缺失点前后的数据趋势,估算出一个合理的值。常用的有线性插值和样条插值。

嗯,这里要注意:插值法不是万能的。如果缺失段前后数据波动很大,插出来的值可能反而会引入噪声。我曾经在一个风速剧烈变化的场景里用过样条插值,结果补出来的数据比真实值偏高了20%。后来我改用了线性插值,效果反而更好。

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

# 时间插值(考虑时间间隔)
df['wind_speed'].interpolate(method='time', inplace=True)
方法 适用场景 优点 缺点
向前填充 短时间缺失(≤3个时间点) 简单、快速、物理意义明确 长时间缺失会引入偏差
线性插值 数据变化平缓 计算量小、结果平滑 对突变不敏感
样条插值 数据有周期性或趋势 拟合效果好 容易过拟合、计算量大

3.2 异常值检测与剔除:把「坏数据」揪出来

风电数据里的异常值,有时候比缺失值更可怕。缺失值至少你知道它缺失了,但异常值会「伪装」成正常数据,悄悄地把模型带偏。

你想想看,如果风速传感器被冻住了,输出值一直卡在0.5m/s,模型会以为「哦,原来风速一直这么小」,然后预测功率也跟着偏低。等到实际风速起来的时候,预测就完全跟不上节奏了。

3.2.1 3-sigma原则

3-sigma原则,就是假设数据服从正态分布,把超出均值±3倍标准差的数据视为异常。这个方法简单粗暴,但很有效。

我个人习惯,先用这个方法做一轮快速筛查。不过要注意——风电功率数据往往不是严格的正态分布,尤其是功率曲线两端,数据分布会偏。所以3-sigma只能作为参考,不能完全依赖。

import numpy as np

# 计算均值和标准差
mean = df['power'].mean()
std = df['power'].std()

# 标记异常值
df['is_outlier_3sigma'] = (np.abs(df['power'] - mean) > 3 * std)

# 剔除异常值
df_clean = df[~df['is_outlier_3sigma']]

经验之谈:3-sigma的阈值可以适当调整。在风电场景下,我经常用2.5倍标准差作为阈值,因为风速的波动本身就比较大,3倍可能会漏掉一些明显的异常。

3.2.2 箱线图法

箱线图法不依赖正态分布假设,它用四分位数来定义异常。具体来说,把低于Q1-1.5×IQR或高于Q3+1.5×IQR的数据视为异常。

我曾经在一个海上风电项目里,用箱线图法发现了一批「幽灵数据」——某台风机在夜间功率输出异常偏高,但风速数据却很正常。后来排查发现,是SCADA系统的时钟出了问题,把白天的数据错误地打上了夜间的时间戳。箱线图法帮我省了好几天的排查时间。

# 计算四分位数
Q1 = df['power'].quantile(0.25)
Q3 = df['power'].quantile(0.75)
IQR = Q3 - Q1

# 定义异常阈值
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 标记并剔除异常值
df['is_outlier_box'] = (df['power'] < lower_bound) | (df['power'] > upper_bound)
df_clean = df[~df['is_outlier_box']]

建议:两种方法结合使用。先用3-sigma做快速过滤,再用箱线图法做精细筛查。我一般会保留被两种方法同时标记为异常的数据,单独分析后再决定是否剔除。

3.3 时间戳对齐与重采样:让数据「步调一致」

风电场的设备来自不同厂家,采样频率也不一样。风速仪可能每1秒采一次,功率表每5秒采一次,温度传感器每10秒采一次。如果不做对齐,模型根本没法用。

我记得刚入行的时候,有一次直接把不同频率的数据丢进模型,结果训练出来的模型预测误差高达30%。后来才发现,是时间戳没对齐,模型学到的全是「错位」的关系。

3.3.1 时间戳对齐

对齐的核心思路是:选定一个基准时间频率,把所有数据都映射到这个频率上。我一般会用10分钟作为基准——这是风电行业的标准时间粒度,也是电网调度常用的单位。

# 将时间列设为索引
df.set_index('timestamp', inplace=True)

# 对齐到10分钟频率
df_aligned = df.resample('10T').mean()

3.3.2 重采样策略

重采样不是简单地取平均。不同场景要用不同的策略:

  • 风速数据:用均值重采样。风速是连续变量,均值能反映整体趋势。
  • 风向数据:用中位数或角度均值。风向是环形变量,普通均值会出问题。
  • 功率数据:用均值或总和。看你是要平均功率还是总发电量。
  • 状态信号:用众数或最大值。比如风机是否运行,取众数更合理。
# 不同列用不同重采样策略
df_resampled = df.resample('10T').agg({
    'wind_speed': 'mean',
    'wind_direction': lambda x: np.median(x),  # 风向用中位数
    'power': 'mean',
    'status': lambda x: x.mode()[0] if not x.mode().empty else np.nan
})

避坑指南:重采样后一定要检查数据量。如果原始数据是1秒频率,重采样到10分钟,数据量会减少到原来的1/600。如果原始数据本身就有大量缺失,重采样后可能会产生新的缺失值——这时候需要回到第一步,重新做缺失值处理。

好了,数据预处理的三个核心环节就讲到这里。这些方法看起来简单,但真正用好需要大量的实践积累。我建议你找一份真实的风电数据,亲手跑一遍上面的代码,感受一下不同方法的效果差异。

记住一句话:数据预处理不是一次性工作,而是一个迭代优化的过程。每次跑完模型,回头看看预处理环节有没有可以改进的地方,慢慢你就会形成自己的「预处理心法」。


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