第3章:数据采集与预处理——SCADA数据采集原理、数据清洗、标准化与归一化
各位好,我是老张。在风电行业摸爬滚打了十几年,今天咱们聊聊数据预处理。
说实话,很多刚入行的朋友,一上来就急着建模型。结果呢?模型跑出来一塌糊涂。为什么?因为数据没处理好。我见过太多这样的案例了。数据预处理,说白了就是给模型喂“干净饭”。你喂它馊掉的饭菜,它怎么可能给你好好干活?
核心观点:数据预处理占整个建模工作量的60%以上。别嫌烦,这一步省了,后面全是坑。
3.1 SCADA数据采集原理
SCADA系统,全称是监控与数据采集系统。咱们风场里,每台风机上都装着几十个传感器。风速、转速、温度、振动、功率……这些数据,每秒钟都在产生。
采集频率一般是10分钟一次。为什么是10分钟?我个人习惯是,这个频率既能捕捉到设备的动态变化,又不会产生太多冗余数据。你想想看,如果每秒采集一次,一台风机一天就是86400条数据。一个风场几十台风机,数据量太大了,存储和处理都是问题。
SCADA采集的数据类型,我整理了一下:
| 数据类型 | 典型参数 | 采集频率 |
|---|---|---|
| 环境参数 | 风速、风向、环境温度、气压 | 10分钟 |
| 运行参数 | 发电机转速、桨距角、偏航角度 | 10分钟 |
| 温度参数 | 齿轮箱油温、发电机绕组温度、轴承温度 | 10分钟 |
| 振动参数 | 主轴振动、齿轮箱振动 | 1秒或更高 |
| 电气参数 | 有功功率、无功功率、电压、电流 | 10分钟 |
嗯,这里要注意。振动参数是高频采集的,因为它能反映齿轮的早期故障。我在项目中遇到过,有一次齿轮箱的振动数据突然出现了一个微小的尖峰,当时没在意。结果三个月后,齿轮箱直接报废了。从那以后,我对振动数据格外敏感。
3.2 数据清洗——缺失值处理
数据采集回来了,但问题来了。传感器会坏,通信会断,数据会丢。这就是缺失值。
缺失值怎么处理?我一般分三种情况:
- 少量缺失(<5%):直接删除。省事,不影响大局。
- 中等缺失(5%-20%):用插值法。线性插值、均值插值都行。
- 大量缺失(>20%):这个特征就别用了。强行补全只会引入噪声。
举个例子,风速数据缺失了。你可以用前后两个时刻的风速取平均。但如果是温度数据,我建议用同一天同一时刻的历史均值。为什么?因为温度有日周期规律。你想想看,中午的温度和凌晨的温度能一样吗?
我的小技巧:对于连续缺失超过3个时间点的数据,我建议直接标记为“不可用”。因为插值法在长段缺失时,误差会累积,反而误导模型。
# 缺失值处理示例(Python伪代码)
import pandas as pd
import numpy as np
# 读取SCADA数据
df = pd.read_csv('wind_turbine_data.csv')
# 检查缺失率
missing_rate = df.isnull().sum() / len(df)
# 处理策略
for col in df.columns:
if missing_rate[col] < 0.05:
# 少量缺失,直接删除
df.dropna(subset=[col], inplace=True)
elif missing_rate[col] < 0.2:
# 中等缺失,线性插值
df[col].interpolate(method='linear', inplace=True)
else:
# 大量缺失,删除该特征
df.drop(columns=[col], inplace=True)
3.3 数据清洗——异常值处理
异常值,说白了就是“离谱的数据”。比如风速显示100m/s,这明显不对。台风来了也没这么夸张。
异常值怎么找?我常用两种方法:
- 3σ原则:数据在均值±3倍标准差之外,视为异常。
- 箱线图法:数据在上下四分位数之外1.5倍IQR,视为异常。
我曾经犯过一个错误。有一次,齿轮箱油温数据出现了几个异常高的点。我直接用3σ原则把它们删了。结果后来发现,那其实是齿轮箱开始过热的前兆。删掉之后,模型完全没学到这个规律。
避坑指南:异常值不一定是“坏数据”。它可能是故障的前兆。处理前,一定要先判断:这是传感器噪声,还是真实的物理异常?我建议先标记,不要直接删除。
# 异常值检测示例
from scipy import stats
# 3σ原则
z_scores = np.abs(stats.zscore(df['wind_speed']))
df['wind_speed_clean'] = np.where(z_scores > 3, np.nan, df['wind_speed'])
# 箱线图法
Q1 = df['gear_oil_temp'].quantile(0.25)
Q3 = df['gear_oil_temp'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['gear_oil_temp_clean'] = df['gear_oil_temp'].clip(lower_bound, upper_bound)
3.4 数据标准化与归一化
数据清洗完了,但还有一个问题。不同特征的量纲不一样。风速是0-30m/s,温度是-20到80℃,功率是0-2000kW。这些数据直接扔进模型,量纲大的特征会主导模型训练。
解决办法就是标准化或归一化。
- 归一化(Min-Max Scaling):把数据缩放到[0,1]区间。公式:X' = (X - X_min) / (X_max - X_min)
- 标准化(Z-score Scaling):把数据变成均值为0,标准差为1。公式:X' = (X - μ) / σ
我个人习惯是:如果数据分布比较均匀,用归一化。如果数据有异常值,用标准化。为什么?因为归一化对异常值敏感,一个极端值会把其他数据都压缩到很小的区间。
重要提醒:标准化和归一化的参数(均值、标准差、最大值、最小值),一定要用训练集计算,然后应用到测试集。千万不能用全量数据计算。否则会造成数据泄露,模型评估结果会虚高。
# 标准化与归一化示例
from sklearn.preprocessing import MinMaxScaler, StandardScaler
# 归一化
scaler_minmax = MinMaxScaler()
df_scaled = scaler_minmax.fit_transform(df[['wind_speed', 'power']])
# 标准化
scaler_std = StandardScaler()
df_standardized = scaler_std.fit_transform(df[['wind_speed', 'power']])
3.5 本章知识体系
下面这张图,是我自己画的。它把本章的知识结构串起来了。你一看就明白:数据预处理不是孤立的一步,而是一个环环相扣的流程。
嗯,这张图你看懂了吗?从上到下,SCADA采集→缺失值处理→异常值处理→标准化/归一化。每一步都有对应的策略。我建议你把这个流程记下来,以后做项目直接套用。
好了,数据预处理就讲到这里。记住一句话:数据质量决定模型上限。你花80%的时间在数据上,模型自然给你好结果。