4. 缺失值处理(下):删除、填充与插值

好,咱们接着聊缺失值处理。上一节我们讲了怎么识别和统计缺失值,这一节直接上硬菜——怎么处理它们。

我个人习惯把缺失值处理方法分成三大类:删除法填充法插值法。每种方法都有它的适用场景,选错了可能会让模型效果大打折扣。我在项目中就吃过这个亏,后面会跟大家细说。

4.1 删除缺失值:最简单,但别乱用

删除缺失值,说白了就是把有缺失的行或列直接扔掉。这个方法最粗暴,但也最危险。

⚠️ 警告: 删除数据前,一定要确认缺失比例。如果缺失超过30%,删除可能会导致样本量不足,模型直接废掉。

什么时候适合删除?我总结了几种情况:

  • 缺失比例极低(<5%):比如10000条数据里只有几十条有缺失,删了不影响大局
  • 缺失完全随机:数据缺失跟其他变量没关系,删了不会引入偏差
  • 样本量充足:删完还有足够的数据训练模型

代码实现很简单:

import pandas as pd
import numpy as np

# 创建示例数据
df = pd.DataFrame({
    '订单量': [100, 200, np.nan, 150, 300],
    '销售额': [5000, 8000, 6000, np.nan, 12000],
    '库存天数': [30, 25, 28, 32, np.nan]
})

# 删除包含任何缺失值的行
df_drop_any = df.dropna()
print("删除任何缺失行后:")
print(df_drop_any)

# 删除全部缺失的行
df_drop_all = df.dropna(how='all')

# 删除缺失超过2个的行
df_drop_thresh = df.dropna(thresh=2)

嗯,这里要注意:dropna()默认是删除包含任何缺失值的行。如果你只想删除全部缺失的行,记得加how='all'参数。

4.2 均值/中位数/众数填充:最常用的方法

填充法比删除法温和多了。它不丢数据,而是用某个值把空缺补上。最常见的三种:均值、中位数、众数。

填充方法 适用场景 优点 缺点
均值填充 数据近似正态分布,无异常值 计算简单,保持均值不变 受异常值影响大,会降低方差
中位数填充 数据有偏态或存在异常值 鲁棒性强,不受极端值影响 可能改变数据分布形态
众数填充 分类变量或离散数据 保持类别分布 可能引入偏差

我曾经在一个供应链项目中,用均值填充了库存天数。结果发现填充后的数据方差变小了,模型对库存波动的预测能力直接下降。后来换成中位数填充,效果好了不少。你想想看,库存天数这种数据,经常会有极端值(比如某次大促),均值很容易被带偏。

# 均值填充
df['销售额_fill_mean'] = df['销售额'].fillna(df['销售额'].mean())

# 中位数填充
df['库存天数_fill_median'] = df['库存天数'].fillna(df['库存天数'].median())

# 众数填充(适用于分类变量)
df['品类_fill_mode'] = df['品类'].fillna(df['品类'].mode()[0])
💡 小技巧: 对于数值型数据,我建议先画个分布图看看。如果数据是正态分布,用均值;如果是偏态分布,用中位数更稳妥。

4.3 前向/后向填充:时间序列的专属武器

如果你处理的是时间序列数据(比如每天的库存量、每周的销售额),前向填充和后向填充就派上用场了。

前向填充:用上一个非缺失值填充当前缺失值。说白了就是「沿用上一个值」。

后向填充:用下一个非缺失值填充当前缺失值。就是「用后面的值补前面的坑」。

我举个例子你就明白了:

# 时间序列数据
df_time = pd.DataFrame({
    '日期': pd.date_range('2024-01-01', periods=7, freq='D'),
    '库存量': [100, np.nan, np.nan, 80, np.nan, 120, 110]
})

# 前向填充
df_time['库存_ffill'] = df_time['库存量'].fillna(method='ffill')

# 后向填充
df_time['库存_bfill'] = df_time['库存量'].fillna(method='bfill')

print(df_time)

输出结果:

        日期  库存量  库存_ffill  库存_bfill
0 2024-01-01  100.0     100.0     100.0
1 2024-01-02    NaN     100.0      80.0
2 2024-01-03    NaN     100.0      80.0
3 2024-01-04   80.0      80.0      80.0
4 2024-01-05    NaN      80.0     120.0
5 2024-01-06  120.0     120.0     120.0
6 2024-01-07  110.0     110.0     110.0

你看,前向填充把1月2日和3日的库存都填成了100(沿用1月1日的值)。后向填充则把1月2日和3日填成了80(用1月4日的值补前面的)。

🔑 关键点: 前向填充适合「数据变化缓慢」的场景,比如库存水平在短期内不会有剧烈波动。后向填充适合「数据有滞后性」的场景,比如某些报表数据是延迟上报的。

4.4 插值法填充:更智能的填充方式

插值法比前两种方法更「聪明」。它不是简单地复制粘贴,而是根据已有数据的趋势来估算缺失值。

常见的插值方法有:

  • 线性插值:假设缺失值前后是线性关系,用直线连接两个已知点来估算
  • 多项式插值:用多项式曲线拟合数据点,适合非线性趋势
  • 样条插值:分段拟合,平滑度更好
# 线性插值
df_time['库存_linear'] = df_time['库存量'].interpolate(method='linear')

# 多项式插值(2阶)
df_time['库存_poly'] = df_time['库存量'].interpolate(method='polynomial', order=2)

# 时间插值(考虑时间间隔)
df_time['库存_time'] = df_time['库存量'].interpolate(method='time')

我记得有一次处理供应链的日销量数据,连续3天缺失。用前向填充的话,这3天销量都一样,明显不合理。后来用了线性插值,根据前后几天的趋势估算,效果自然多了。

⚠️ 注意: 插值法虽然智能,但也不是万能的。如果缺失值连续出现太多(比如连续缺失超过5个),插值的误差会越来越大。这时候我建议先看看数据为什么缺失,是不是采集系统出了问题。

4.5 如何选择?我的实战经验

说了这么多方法,到底该用哪个?我给大家一个决策思路:

  1. 先看缺失比例:<5%可以考虑删除,>30%必须用填充或插值
  2. 再看数据类型:时间序列优先考虑前向/后向填充或插值;非时间序列用均值/中位数/众数
  3. 最后看业务含义:比如库存数据,如果缺失是因为断货,用前向填充就不合理(断货期间库存应该是0)

我曾经犯过一个错:处理供应商交货周期数据时,直接用均值填充了所有缺失值。结果模型训练出来,预测的交货周期总是偏短。后来一查,原来缺失的数据大多是交货延迟的记录(供应商没及时上报),均值填充把延迟信息全抹掉了。从那以后,我处理缺失值前一定会先跟业务方聊一聊,搞清楚「为什么缺失」。

💡 终极建议: 没有一种方法适合所有场景。我通常的做法是:先尝试2-3种方法,分别训练模型,看哪个效果最好。数据清洗不是一次性的,需要反复迭代。

4.6 本章知识体系

下面这张图帮你梳理了缺失值处理的完整思路:

缺失值处理知识体系 缺失值处理 删除法 删除行 / 删除列 适用:缺失<5%,样本充足 填充法 均值 / 中位数 / 众数填充 前向填充 / 后向填充 插值法 线性插值 / 多项式插值 样条插值 / 时间插值 决策流程:缺失比例 → 数据类型 → 业务含义 最终目标:保留数据完整性,减少偏差 提升模型预测准确性

好了,缺失值处理的内容就到这里。记住,没有银弹,多试几种方法,选最适合你数据的那个。


专注资料整理