4、缺失值处理(上):缺失值产生原因分析、删除法(行删除、列删除)的适用场景与风险、均值/中位数填充
各位好,咱们今天聊聊缺失值处理。说实话,做风电功率预测这些年,我见过太多人在这一步翻车了。数据拿过来,一看有缺失,要么直接删掉,要么随便填个0完事。结果呢?模型训练出来,预测效果惨不忍睹。
缺失值处理不是简单的「填坑」游戏。你得先搞清楚:它为什么缺失? 这个原因直接决定了你该用什么方法。我个人的习惯是,拿到数据后第一件事不是跑模型,而是花30分钟做缺失值分析。
核心观点:缺失值处理没有银弹。每种方法都有适用场景和代价。理解「为什么缺失」比「怎么填」更重要。
4.1 缺失值产生原因分析
风电数据缺失的原因,我总结下来主要有三类。你在项目里遇到的基本跑不出这个圈。
- 传感器故障或通信中断:这是最常见的原因。风速仪坏了、数据采集器死机、光纤被挖断……我在内蒙古一个风场就遇到过,整条35kV集电线路的SCADA数据连续丢了3天。这种缺失通常是随机缺失,跟数据本身没关系。
- 设备维护或停机:风机定期检修、电网调度限电,这些时间段的数据会缺失。注意,这种缺失是非随机缺失——因为停机期间功率就是0,但数据采集系统可能直接不记录。你想想看,如果直接用删除法,模型就学不到「停机」这个正常状态了。
- 数据采集频率不一致:比如风速是10分钟一个点,但功率是15分钟一个点。对齐时间戳时,必然会产生缺失。这种属于结构性缺失,处理起来相对简单。
我建议你拿到数据后,先画一张缺失值热力图。看一眼就能知道缺失是随机分布的,还是集中在某些时间段或某些特征上。嗯,这一步很关键。
小技巧:用pandas的isnull().sum()统计缺失数量,再用missingno库画热力图。我每次必做。
4.2 删除法:行删除与列删除
删除法是最简单粗暴的方法。但简单不代表可以乱用。我见过有人因为5%的缺失率直接删了30%的数据,结果模型性能暴跌。为什么会这样?因为删除的数据可能包含了重要信息。
4.2.1 行删除(删除含缺失值的样本)
适用场景:
- 缺失率极低(通常<5%),且缺失是随机发生的
- 数据量非常大,删除少量样本不影响统计意义
- 你只是做快速探索性分析,不追求最终模型精度
风险:
- 样本量损失:如果多个特征都有缺失,行删除会连锁反应。比如你有10个特征,每个缺失2%,但分布在不同行上,最终可能删掉20%的样本。
- 引入偏差:如果缺失不是随机的(比如只有高风速时段才缺失),删除后你的数据就偏向低风速场景了。模型预测高风速时必然不准。
我曾经踩过的坑:有个项目,我直接用dropna()删掉了所有含缺失的行。结果模型在测试集上表现还行,但上线后一到台风天就崩。后来才发现,缺失值大多出现在极端天气下——删除法把最关键的样本全扔了。
4.2.2 列删除(删除含缺失值的特征)
适用场景:
- 某个特征的缺失率超过70%,基本没有保留价值
- 该特征与其他特征高度相关(比如风速和风功率密度),删除一个不影响模型
- 该特征在业务上不重要(比如某个辅助传感器的数据)
风险:
- 信息丢失:即使缺失率高,这个特征可能仍然包含有价值的信息。比如「风向」特征缺失60%,但剩下的40%数据对功率预测贡献很大。
- 破坏特征工程:你辛辛苦苦构造的交互特征,因为缺失率高直接删掉?太可惜了。
我个人习惯是:先分析,再删除。不要一上来就dropna(axis=1)。先看看这个特征缺失的模式,再决定。
4.3 均值/中位数填充
这是最常用的填充方法,也是初学者最爱用的。说白了就是用该特征的平均值或中位数去填补缺失值。简单、快速、容易实现。
什么时候用均值?什么时候用中位数?
| 数据分布 | 推荐方法 | 原因 |
|---|---|---|
| 近似正态分布 | 均值填充 | 均值能代表数据中心位置,且受极端值影响小 |
| 偏态分布(有极端值) | 中位数填充 | 中位数不受极端值影响,更稳健 |
| 存在明显异常值 | 中位数填充 | 避免异常值拉高均值,导致填充值偏离实际 |
举个例子。风速数据通常是右偏分布——大部分时间风速在3-8m/s,但偶尔会有15m/s以上的大风。如果用均值填充,均值会被大风拉高到6m/s左右。但实际缺失的那个时刻可能只有4m/s。你想想看,用6m/s去填,模型就学错了。
所以我在处理风速、功率这类偏态数据时,几乎只用中位数。均值?除非我确认数据是正态的,否则不用。
代码示例:
import pandas as pd
import numpy as np
# 读取风电数据
df = pd.read_csv('wind_power_data.csv')
# 查看缺失情况
print(df.isnull().sum())
# 均值填充
df['wind_speed_fill_mean'] = df['wind_speed'].fillna(df['wind_speed'].mean())
# 中位数填充(推荐)
df['wind_speed_fill_median'] = df['wind_speed'].fillna(df['wind_speed'].median())
# 分组填充:按季节填充中位数(更精细)
df['wind_speed_fill_group'] = df.groupby('season')['wind_speed'].transform(
lambda x: x.fillna(x.median())
)
进阶技巧:不要全局填充。风电数据有明显的季节性——冬天风速大,夏天风速小。我习惯按月份或季节分组填充中位数。这样更符合实际情况。
均值/中位数填充的最大问题是:它会降低数据的方差。你想想,所有缺失值都填成同一个数,那这个特征的分布就「瘦」了。模型学到的规律会偏向平均值附近,对极端情况的预测能力会下降。
所以我的建议是:均值/中位数填充只适合缺失率较低(<10%)的情况。如果缺失率超过20%,这个方法就不太靠谱了。下一节我们会讲更高级的方法,比如KNN填充、回归填充等。
注意:千万不要在填充前做训练集和测试集的划分!一定要先分好训练集和测试集,然后用训练集的均值/中位数去填充测试集。否则会造成数据泄露,模型评估结果虚高。
4.4 本章知识体系
下面这张图是我自己画的,把缺失值处理的核心逻辑串起来了。你可以保存下来,做项目时对照着看。
这张图的核心逻辑是:先分析原因,再评估缺失率,最后选方法。不要跳过前两步直接上手填。我见过太多人拿着数据就fillna(0),结果模型训练出来一塌糊涂。
好了,这一节的内容就到这里。缺失值处理是个大话题,今天我们只讲了删除法和均值/中位数填充。这两种方法虽然简单,但用好了能解决80%的问题。关键在于——你得知道什么时候用,什么时候不用。