数据采集与预处理:SCADA系统介绍、数据清洗、缺失值处理、异常值检测
各位同学,咱们今天聊聊风电功率预测里最基础、也最容易被忽视的一环——数据采集与预处理。
说实话,我见过太多项目,算法模型选得漂漂亮亮,结果一跑就崩。为什么?数据太脏。你想想看,风机天天在野外风吹日晒,传感器偶尔抽个风,通信线路断个几秒,这都是家常便饭。所以,数据预处理不是锦上添花,而是地基。
1. SCADA系统——风电场的数据神经
SCADA,全称是数据采集与监视控制系统。说白了,它就是风电场的"神经中枢"。每台风机上几十个传感器,把风速、风向、转速、桨距角、有功功率、无功功率……一股脑儿全传回来。
我个人习惯,拿到SCADA数据后,第一件事不是看算法,而是先看采样频率。大部分风场的SCADA系统是10分钟一个点,一天144个点。但有些老旧风场,采样间隔可能不固定,甚至跳变。我在内蒙古一个项目上就遇到过,某台风机下午3点到4点之间数据全丢了,原因是通信光缆被羊啃了……嗯,你没听错,羊啃的。
SCADA核心参数一览:
| 参数名称 | 单位 | 典型范围 | 说明 |
|---|---|---|---|
| 平均风速 | m/s | 0~30 | 10min平均 |
| 最大风速 | m/s | 0~50 | 10min内极大值 |
| 有功功率 | kW | 0~额定功率 | 核心预测目标 |
| 桨距角 | ° | 0~90 | 变桨控制用 |
| 发电机转速 | rpm | 0~2000 | 双馈/直驱不同 |
2. 数据清洗——把"脏东西"筛出去
数据清洗,听着简单,做起来全是坑。我总结了三类最常见的"脏数据":
- 重复数据:同一时间戳出现两条记录。原因可能是通信重传、数据库写入异常。处理方式:按时间戳去重,保留第一条或最后一条。
- 格式问题:时间戳格式不统一,有的用"2024-01-15 10:00:00",有的用"2024/01/15 10:00"。数值列里混入字符串,比如风速字段出现"---"。
- 噪声数据:传感器受电磁干扰,数值突然跳变。比如风速从5m/s瞬间跳到50m/s,然后又回来。
我的小技巧:清洗前先做一次数据概览。用 df.info() 看各列非空数量,用 df.describe() 看统计分布。如果某列最大值是风速100m/s,那基本可以断定有问题——17级台风也就60m/s左右。
import pandas as pd
import numpy as np
# 读取SCADA数据
df = pd.read_csv('wind_farm_scada.csv', parse_dates=['timestamp'])
# 去重:按时间戳去重,保留第一条
df = df.drop_duplicates(subset=['timestamp'], keep='first')
# 格式统一:确保数值列是float类型
for col in ['wind_speed', 'power', 'pitch_angle']:
df[col] = pd.to_numeric(df[col], errors='coerce')
# 时间排序
df = df.sort_values('timestamp').reset_index(drop=True)
3. 缺失值处理——别让"空"毁了你的模型
缺失值,说白了就是数据里出现了"窟窿"。为什么会这样?传感器故障、通信中断、数据入库时异常……原因很多。我建议先看看缺失比例,再决定怎么补。
缺失比例与策略:
| 缺失比例 | 推荐策略 | 说明 |
|---|---|---|
| < 5% | 插值填充 | 线性插值或前向填充 |
| 5% ~ 20% | 插值 + 模型填充 | 用相邻时间窗口均值,或用KNN |
| > 20% | 删除该段数据 | 缺失太多,填充反而引入偏差 |
我个人最常用的是线性插值。原因很简单——风速和功率在短时间内是连续变化的,线性插值既简单又合理。但要注意,如果缺失段太长(比如连续缺失2小时以上),线性插值就不靠谱了。我曾经在甘肃一个风场,某台风机连续缺失了6个小时的数据,我试了各种插值方法,结果模型预测误差直接翻倍。最后狠心把那6小时数据全删了,效果反而更好。
# 线性插值填充缺失值
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')
df['power'] = df['power'].interpolate(method='linear')
# 前向填充(适用于短时间缺失)
df['pitch_angle'] = df['pitch_angle'].fillna(method='ffill')
# 删除缺失比例过高的列或行
df = df.dropna(thresh=len(df) * 0.8, axis=1) # 删除缺失>20%的列
注意:千万不要在缺失值处理前直接训练模型。我曾经见过一个新手,数据里缺失了30%,他直接跑模型,结果模型学到的全是"怎么处理空值",而不是真正的功率预测规律。嗯,那叫一个惨。
4. 异常值检测——揪出"捣乱分子"
异常值,就是那些明显偏离正常范围的数据点。比如风速10m/s时,功率应该是额定功率的60%左右,结果你看到功率为0或者直接爆表。这种数据不处理,模型会被带偏。
我常用的方法有三种:
- 3σ原则:假设数据服从正态分布,超出均值±3倍标准差的值视为异常。简单粗暴,但对非正态分布效果一般。
- IQR方法:四分位距法。小于Q1-1.5×IQR或大于Q3+1.5×IQR的视为异常。对偏态分布更鲁棒。
- 功率曲线校验:这是风电领域特有的方法。根据风速-功率理论曲线,判断实际点是否偏离太远。比如风速12m/s时,功率应该在额定功率附近,如果只有10%,那肯定是异常。
# IQR方法检测异常值
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return outliers
# 功率曲线校验(简化版)
def power_curve_check(df):
# 理论功率曲线:风速与功率的对应关系
# 这里用简单阈值:风速>3m/s且<25m/s时,功率应>0
invalid = df[(df['wind_speed'] > 3) & (df['wind_speed'] < 25) & (df['power'] == 0)]
return invalid
# 标记异常值,不直接删除,先看看
df['is_outlier'] = False
outliers = detect_outliers_iqr(df, 'power')
df.loc[outliers.index, 'is_outlier'] = True
处理异常值的建议:
- 先标记,别急着删。有些"异常"可能是真实工况,比如风机限功率运行。
- 结合业务逻辑判断。风速低于切入风速(通常3m/s)时功率为0,这是正常的。
- 异常值可以替换为NaN,然后走缺失值处理流程。
好了,数据采集与预处理这部分就聊到这儿。记住一句话:数据质量决定了模型效果的上限。你算法再牛,数据不行,一切都是白搭。下一章咱们聊聊特征工程,看看怎么从这些清洗好的数据里挖出更多有价值的信息。
公众号:蓝海资料掘金营,微信deep3321