数据采集与预处理:SCADA数据、数据清洗、异常值处理
各位同学,咱们今天聊聊数据采集和预处理。说实话,这部分工作看着不起眼,但我在项目里吃过不少亏。你想想看,模型建得再漂亮,数据是脏的,结果全是白搭。所以这一章,我把自己踩过的坑、总结的经验,都摊开来跟你讲。
SCADA数据:风机的“黑匣子”
SCADA系统,说白了就是风机的“行车记录仪”。它每秒都在记录风速、功率、桨距角、转速这些参数。我刚开始接触时,觉得数据量太大,随便抽一点用就行。结果模型精度惨不忍睹。
后来我学乖了。SCADA数据有几个关键特征:
- 时间戳:必须精确到秒,而且时区要统一。我遇到过某风场用UTC,另一个用北京时间,合并数据时差点崩溃。
- 采样频率:通常是10分钟平均值。但注意,有些老风机是1分钟甚至更粗的粒度。我个人习惯先检查采样间隔,不一致的话要重采样。
- 字段完整性:风速、有功功率、桨距角、发电机转速、机舱温度——这五个字段缺一不可。缺了任何一个,后面的功率曲线建模都别想做好。
数据清洗:把“垃圾”筛出去
数据清洗,听起来简单,做起来全是细节。我总结了一套“三步走”流程,你可以直接拿去用。
第一步:处理缺失值
风机停机、传感器故障、通信中断——这些都会导致数据缺失。我的处理原则是:
- 连续缺失不超过3个点:用线性插值填充。比如风速从5m/s跳到7m/s,中间缺了一个点,我取平均值6m/s。
- 连续缺失超过10个点:直接删除这段数据。为什么?因为插值会引入太多不确定性,反而污染模型。
- 整段缺失超过1小时:标记为“停机时段”,单独处理。
第二步:剔除重复数据
SCADA系统偶尔会重复记录。比如同一时刻出现两条一模一样的数据。我遇到过最夸张的一次,一个风场有3%的数据是重复的。
处理方法很简单:按时间戳去重,保留第一条。但注意,如果时间戳相同但数值不同,说明传感器冲突了。这时候我建议两条都删掉,因为无法判断哪个是对的。
第三步:处理时间戳异常
时间戳跳跃、倒流、或者出现未来时间——这些我都见过。我的做法是:
- 时间戳倒流:直接删除该点及之后的数据,直到时间戳恢复正常。
- 时间戳跳跃超过采样间隔的2倍:标记为“数据中断”,单独分析原因。
- 未来时间戳:通常是系统时钟错误,直接删除。
异常值处理:揪出“捣乱分子”
异常值处理,是数据预处理里最考验经验的环节。我把它分成两类:物理异常和统计异常。
物理异常:一眼就能看出来的问题
比如风速显示-5m/s,或者功率超过额定功率的1.5倍。这些明显违反物理规律的数据,直接删除就行。
我常用的物理阈值:
| 参数 | 合理范围 | 异常处理 |
|---|---|---|
| 风速 | 0 ~ 35 m/s | 超出直接删除 |
| 有功功率 | 0 ~ 额定功率×1.1 | 超出直接删除 |
| 桨距角 | 0° ~ 90° | 超出直接删除 |
| 发电机转速 | 0 ~ 额定转速×1.2 | 超出直接删除 |
统计异常:用数据说话
物理异常好处理,统计异常就有点头疼了。比如风速10m/s时,功率突然掉到0。这可能是传感器故障,也可能是风机切出。怎么区分?
我常用的方法是“3σ原则”加“局部异常因子”:
- 3σ原则:计算每个风速bin内的功率均值和标准差。功率超出均值±3σ的,标记为异常。这个方法简单粗暴,但有效。
- 局部异常因子(LOF):如果数据点周围邻居很少,说明它是孤立的异常点。这个方法更精细,但计算量大。我一般只在关键分析时用。
举个例子,风速在8-9m/s这个区间,正常功率应该在200-300kW。如果出现一个点功率是500kW,那基本可以确定是异常。我会先查原始日志,确认是传感器问题后直接删除。
数据预处理流程总览
说了这么多,我画了一张流程图,帮你把整个流程串起来。你一看就明白了。
这张图把流程串起来了。你从原始数据开始,先处理缺失值,再剔除重复数据,最后处理异常值。每一步都有对应的处理方法,别跳步。
代码示例:快速上手
光说不练假把式。我给你一段Python代码,可以直接跑。这是我项目里常用的预处理函数,你拿去改改就能用。
import pandas as pd
import numpy as np
def preprocess_scada(df):
"""
SCADA数据预处理函数
df: 包含'风速', '有功功率', '时间戳'的DataFrame
"""
# 1. 缺失值处理
df = df.interpolate(method='linear', limit=3) # 连续缺失≤3插值
df = df.dropna(thresh=len(df.columns)-2) # 缺失太多直接删
# 2. 重复数据剔除
df = df.drop_duplicates(subset=['时间戳'])
# 3. 物理异常剔除
df = df[(df['风速'] >= 0) & (df['风速'] <= 35)]
df = df[(df['有功功率'] >= 0) & (df['有功功率'] <= 1500*1.1)]
# 4. 统计异常剔除(3σ原则)
for wind_bin in range(0, 36, 1):
mask = (df['风速'] >= wind_bin) & (df['风速'] < wind_bin+1)
bin_data = df.loc[mask, '有功功率']
if len(bin_data) > 10:
mean = bin_data.mean()
std = bin_data.std()
df = df.drop(mask & (abs(df['有功功率'] - mean) > 3*std))
return df
好了,数据预处理这部分就讲到这里。记住一句话:数据质量决定模型上限。你花80%的时间做预处理,剩下的20%建模,结果一定比反过来好。我在项目里验证过无数次了。
公众号:蓝海资料掘金营,微信deep3321