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 本章知识体系

下面这张图是我自己画的,把缺失值处理的核心逻辑串起来了。你可以保存下来,做项目时对照着看。

缺失值处理决策流程 检测到缺失值 分析缺失原因 评估缺失率 缺失率 < 5% 行删除 或 均值/中位数填充 缺失率 5%-20% 中位数填充 或 分组填充 缺失率 > 20% 列删除 或 高级填充方法 验证填充效果,评估模型影响

这张图的核心逻辑是:先分析原因,再评估缺失率,最后选方法。不要跳过前两步直接上手填。我见过太多人拿着数据就fillna(0),结果模型训练出来一塌糊涂。

好了,这一节的内容就到这里。缺失值处理是个大话题,今天我们只讲了删除法和均值/中位数填充。这两种方法虽然简单,但用好了能解决80%的问题。关键在于——你得知道什么时候用,什么时候不用。

专注资料整理