第1章:数据采集与预处理——打好预测模型的地基
各位同学,大家好。我是老张,在风电行业摸爬滚打了十来年。今天咱们开始聊《风电发电量预测模型构建实战》的第一章。
说实话,很多人一上来就急着调模型、跑算法,觉得数据预处理是体力活,没啥技术含量。我当年也这么想,结果呢?模型跑出来一塌糊涂,回头一查,数据里全是坑。嗯,从那以后,我养成了一个习惯:花70%的时间搞数据,30%的时间调模型。你想想看,地基没打好,房子能稳吗?
1.1 SCADA系统——风电场的"黑匣子"
SCADA,全称是数据采集与监视控制系统。说白了,它就是风电场的"黑匣子",每台风机的一举一动都被它记录在案。
我个人习惯把SCADA系统比作飞机的飞行数据记录仪。风速、转速、桨距角、发电机温度、有功功率……这些参数每时每刻都在产生。我记得刚入行那会儿,师傅跟我说:"老张,SCADA里的数据就是金矿,但得你会挖。"
SCADA系统主要采集三类数据:
- 环境参数:风速、风向、气温、气压、湿度
- 运行参数:发电机转速、桨距角、机舱温度、齿轮箱油温
- 电气参数:有功功率、无功功率、电压、电流、频率
1.2 数据采集频率与字段
SCADA系统的采集频率,不同厂家差别挺大。常见的有:
| 采集频率 | 适用场景 | 数据量(单台风机/天) |
|---|---|---|
| 1秒/次 | 故障诊断、高频分析 | 约86,400条 |
| 10秒/次 | 功率预测、性能评估 | 约8,640条 |
| 1分钟/次 | 日常监控、报表统计 | 约1,440条 |
做发电量预测,我个人建议用10秒级数据。为什么?1秒级数据太密,噪声大;1分钟级数据又太粗,会丢失风速快速变化时的细节。你想想看,风速从5m/s飙到12m/s可能就几十秒的事,1分钟采一次根本抓不住。
关键字段一般包括:
timestamp:时间戳,精确到秒wind_speed:风速(m/s)wind_direction:风向(°)rotor_speed:叶轮转速(rpm)pitch_angle:桨距角(°)active_power:有功功率(kW)nacelle_temp:机舱温度(℃)
1.3 数据清洗——别让脏数据毁了你的模型
数据清洗,说白了就是"去伪存真"。SCADA数据里常见的坑,我一个个说。
缺失值处理
SCADA系统偶尔会"断片",比如通讯中断、传感器故障,都会导致数据缺失。处理方式有三种:
- 直接删除:缺失比例小于5%时,简单粗暴有效
- 均值/中位数填充:用前后时刻的平均值补上
- 插值法:线性插值或样条插值,更平滑
代码示例(Python):
import pandas as pd
import numpy as np
# 读取SCADA数据
df = pd.read_csv('scada_data.csv', parse_dates=['timestamp'])
# 检查缺失值
print(df.isnull().sum())
# 方法1:删除缺失比例小于5%的列
df = df.dropna(thresh=len(df)*0.95, axis=1)
# 方法2:线性插值填充(适用于短时间缺失)
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')
# 方法3:连续缺失超过10个点,删除整段
df['gap_flag'] = df['wind_speed'].isna().astype(int)
df['gap_group'] = df['gap_flag'].ne(df['gap_flag'].shift()).cumsum()
gap_lengths = df.groupby('gap_group')['gap_flag'].sum()
long_gaps = gap_lengths[gap_lengths > 10].index
df = df[~df['gap_group'].isin(long_gaps)]
异常值处理
异常值,就是那些"离谱"的数据。比如风速显示-5m/s,或者功率突然飙到2000kW(额定功率才1500kW)。
我常用的方法:
- 3σ原则:超出均值±3倍标准差的数据,视为异常
- IQR方法:四分位距法,超出Q1-1.5IQR或Q3+1.5IQR的算异常
- 业务规则:比如风速不能为负,功率不能超过额定值1.2倍
# 3σ原则检测异常值
def detect_outliers_3sigma(df, column):
mean = df[column].mean()
std = df[column].std()
lower = mean - 3 * std
upper = mean + 3 * std
return df[(df[column] >= lower) & (df[column] <= upper)]
# IQR方法
def detect_outliers_iqr(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
return df[(df[column] >= lower) & (df[column] <= upper)]
# 业务规则过滤
df = df[(df['wind_speed'] >= 0) & (df['wind_speed'] <= 35)]
df = df[df['active_power'] <= df['rated_power'] * 1.2]
1.4 数据标准化与归一化
为什么要做标准化?你想想看,风速是0-30m/s,功率是0-1500kW,温度是-20-50℃。这些特征的量纲差太多了。如果不处理,模型会天然地"偏爱"数值大的特征。
两种主流方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | \( z = \frac{x - \mu}{\sigma} \) | 数据近似正态分布,或存在异常值 |
| Min-Max归一化 | \( x' = \frac{x - x_{min}}{x_{max} - x_{min}} \) | 数据有明确边界,如功率、角度 |
我个人习惯:风速、温度这类近似正态分布的,用Z-score;功率、桨距角这类有物理边界的,用Min-Max。
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score标准化
scaler_std = StandardScaler()
df['wind_speed_std'] = scaler_std.fit_transform(df[['wind_speed']])
# Min-Max归一化
scaler_mm = MinMaxScaler(feature_range=(0, 1))
df['active_power_norm'] = scaler_mm.fit_transform(df[['active_power']])
好了,第一章的内容就到这里。数据预处理这块,看似琐碎,但每一步都关系到最终模型的成败。记住:好的数据预处理,能让你的模型事半功倍;反之,再牛的算法也救不了脏数据。