4. 缺失值处理策略:别让数据缺口毁了你的模型
做风电数据清洗这么多年,我最大的感触就是——数据永远不会完美。你从SCADA系统拉下来的数据,十有八九带着各种“窟窿”。今天我们就聊聊怎么补这些窟窿。
我个人习惯把缺失值分成三类,处理方式完全不同。你想想看,要是把停机时段的数据和传感器故障的数据混在一起处理,那模型不跑偏才怪。
4.1 缺失值类型:先搞清楚“为什么缺”
我刚开始做风电项目时,看到缺失值就一股脑用均值填充。结果呢?模型在限电时段预测得一塌糊涂。后来才明白,缺失的原因决定了处理的方法。
4.1.1 随机缺失(MCAR)
说白了就是“纯属意外”。比如传感器偶尔抽风,或者通信丢包。这种缺失跟数据本身没关系,处理起来最简单。
- 特征:缺失位置没有规律,像随机撒点
- 常见场景:单个传感器瞬时故障、网络波动
- 处理方式:直接删除或简单插值都行
4.1.2 非随机缺失(MNAR)
这才是真正头疼的。缺失的原因跟数据值本身有关。比如风速太高导致传感器损坏,或者温度过低设备停摆。
- 特征:缺失集中在某些特定工况下
- 常见场景:极端天气下传感器失效、设备老化导致数据丢失
- 处理方式:需要结合工况信息,不能简单插值
4.2 插值方法:怎么补才靠谱?
搞清楚了缺失类型,接下来就是动手补数据。我常用的方法有三种,各有各的适用场景。
4.2.1 线性插值
最简单也最直观。用缺失点前后两个有效值,拉一条直线算中间值。
import pandas as pd
import numpy as np
# 线性插值示例
df['power_linear'] = df['active_power'].interpolate(method='linear')
# 我个人习惯限制最大插值间隔
df['power_limited'] = df['active_power'].interpolate(
method='linear',
limit_area='inside',
limit=5 # 最多连续插5个点
)
适用场景:短时间内的随机缺失,比如1-2个采样点。时间太长就不准了,毕竟风速变化不是线性的。
4.2.2 前向填充
用上一个有效值填充后面的缺失。说白了就是“假设数据没变”。
# 前向填充
df['power_ffill'] = df['active_power'].fillna(method='ffill')
# 限电时段我常用这个
df.loc[df['curtailment_flag'] == 1, 'power'] = df['power'].fillna(method='ffill')
适用场景:停机时段、限电时段。因为在这些时段里,功率确实应该保持在一个稳定值(比如0或者限电上限)。
4.2.3 KNN插值
这个方法比较高级。找跟缺失点最相似的K个邻居,用它们的值来估算。
from sklearn.impute import KNNImputer
# 注意:KNN插值需要先标准化
imputer = KNNImputer(n_neighbors=5, weights='distance')
# 选取相关特征进行插值
features = ['wind_speed', 'rotor_speed', 'pitch_angle', 'temp_ambient']
df_imputed = pd.DataFrame(
imputer.fit_transform(df[features]),
columns=features
)
适用场景:复杂工况下的缺失,尤其是多个传感器同时失效时。KNN能综合考虑风速、转速、桨距角等多个维度的信息。
| 方法 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| 线性插值 | 简单快速 | 假设线性变化 | 短时随机缺失 |
| 前向填充 | 保持稳态 | 忽略变化趋势 | 停机/限电时段 |
| KNN插值 | 多维信息利用 | 计算量大 | 复杂工况缺失 |
4.3 风电场景下的特殊处理
嗯,这里要注意。风电数据有两个特殊场景,处理方式跟常规工业数据完全不同。
4.3.1 停机时段
风机停机时,功率、转速这些值就是0。但你不能简单地把缺失值填0,因为停机本身就是一个状态。
# 我的处理流程
# 1. 先识别停机时段
df['is_stopped'] = (df['active_power'] < 0.01) & (df['rotor_speed'] < 0.1)
# 2. 停机时段内的缺失,填0
df.loc[df['is_stopped'], 'active_power'] = df.loc[df['is_stopped'], 'active_power'].fillna(0)
# 3. 但要注意:停机前后的数据不要插值跨越
# 我曾经犯过这个错,把停机前后的数据连起来插值,结果补出了负功率
4.3.2 限电时段
限电比停机更麻烦。风机还在转,但功率被人为限制在一个上限以下。这时候的缺失值,不能简单填0,也不能用正常时段的插值。
# 限电时段的处理
# 1. 标记限电时段
df['is_curtailed'] = (df['available_power'] - df['active_power']) > 0.05 * df['rated_power']
# 2. 限电时段内,用前向填充更合理
df.loc[df['is_curtailed'], 'active_power'] = df.loc[
df['is_curtailed'], 'active_power'
].fillna(method='ffill')
# 3. 如果限电时段开始时有缺失,用限电上限填充
limit = df.loc[df['is_curtailed'], 'active_power'].max()
df.loc[df['is_curtailed'], 'active_power'] = df.loc[
df['is_curtailed'], 'active_power'
].fillna(limit)
我的经验:限电时段的缺失,用前向填充效果最好。因为限电指令一旦下发,功率会稳定在一个值附近,不会剧烈波动。
4.4 知识体系总览
下面这张图,是我梳理的缺失值处理整体流程。你照着这个思路走,基本不会出大问题。
最后说一句,缺失值处理没有银弹。我见过太多人拿着一两种方法走天下,结果在不同项目里反复踩坑。你最好在自己的数据集上多试几种方法,对比效果再定。
嗯,今天就聊到这儿。记住:数据清洗不是体力活,是技术活。把缺失值处理好了,后面的建模工作能省一半力气。