2. 缺失值处理:常见方法与面试要点

聊到特征工程,缺失值处理几乎是最先遇到的门槛。说实话,这题在面试里出现的频率极高,但大部分人都答得太表面。我只想说,面试官问「你会怎么处理缺失值」,其实是在考察你对数据分布的理解、对业务场景的判断,以及代码落地的能力。

先别急着背答案,我建议你从这三个层面去思考:方法本身、选型逻辑、以及实战中的坑。下面我们一个个展开。

2.1 三大类处理方法:删除、填充、插值

先看这张图,把整个知识体系串起来,接下来的讨论都围绕它展开。

缺失值处理知识体系 缺失值处理 删除 按行删除 按列删除 填充 统计量填充 模型预测填充 固定值填充 插值 线性插值 多项式/样条插值 面试核心:什么时候用哪种?为什么? 选择策略 缺失比例 < 5% → 删除(简单粗暴) 缺失比例 5%~30% → 填充或插值 缺失比例 > 30% → 优先考虑特征是否保留

图中我用了三种颜色来区分:红色代表删除,绿色代表填充,紫色代表插值。面试官通常先让你复述这三种方法,然后追问「具体怎么做?」。别慌,代码和逻辑都简单,关键是你得讲清楚「为什么在某些场景下,删除比填充更好」。

2.2 方法详解与代码示范

2.2.1 删除(Deletion)

删除分为按行删和按列删。按行删除就是直接丢掉有缺失的样本,按列删除就是丢掉缺失率太高的特征。我个人习惯在缺失比例低于 5% 时,优先考虑按行删除。为什么?因为不影响整体分布,而且简单。

不过,你想想看,如果缺失不是随机的,删除会引入偏差。我曾经在一个风控模型里发现,缺失值集中在高收入人群,一删掉就把这部分样本全丢了,模型直接废了。所以删除前,一定要检查缺失是否随机。

面试常用代码示例:
import pandas as pd
import numpy as np

# 构造含有缺失值的数据
df = pd.DataFrame({
    'A': [1, 2, np.nan, 4, 5],
    'B': [np.nan, 2, 3, np.nan, 5],
    'C': [1, 2, 3, 4, 5]
})

# 按行删除:只要任意列缺失就丢掉
df_drop_row = df.dropna()
print("按行删除后:\n", df_drop_row)

# 按列删除:某列缺失比例超过阈值才删
threshold = 0.3
df_drop_col = df.loc[:, df.isnull().mean() < threshold]
print("按列删除后:\n", df_drop_col)
注意:dropna() 默认是只要有一个缺失就删行。如果只想删全部缺失的行,加 how='all'。小样本数据慎用删除,信息损失可能比你想象的严重。

2.2.2 填充(Imputation)

填充是最常用的方法。常见的有:用均值、中位数、众数填充,或用模型(KNN、回归)预测缺失值,或用指定常数填充。我在项目中碰到过连续变量缺失,一般先用中位数填充——因为中位数对异常值不敏感。而分类变量缺失,我会先用众数填充。

但千万别以为填充就万事大吉。填充的本质是「引入没有依据的信息」,如果填充量太大,会扭曲分布。我曾经参加一个比赛,一个特征缺失 40%,我直接用均值填充,结果模型过拟合严重,后来换成 KNN 填充才好了一些。

常见填充方式代码:
# 1. 统计量填充
df['A'].fillna(df['A'].mean(), inplace=True)   # 均值
df['B'].fillna(df['B'].median(), inplace=True) # 中位数
df['C'].fillna(df['C'].mode()[0], inplace=True) # 众数

# 2. 固定值填充(适用于业务上有特殊含义的场景)
df['A'].fillna(-1, inplace=True)

# 3. 前向填充 / 后向填充(时序数据常用)
df['A'].fillna(method='ffill', inplace=True)    # 用上一个值填充
df['A'].fillna(method='bfill', inplace=True)    # 用下一个值填充
一个小技巧:填充后,一定要对比填充前后的分布(直方图或 Q-Q 图),看是否发生了显著偏移。如果偏移太大,说明你的填充策略可能不合适。

2.2.3 插值(Interpolation)

插值比填充更「智能」一点,它基于已有数据的趋势来推测缺失值。最常用的是线性插值,适合连续变量且缺失不太密集的情况。如果有周期性特征,可以用多项式插值或 spline 插值。

说白了,插值就是「猜」,但它猜得更有逻辑。不过,它也有个软肋:如果缺失值出现在拐点或极值区间,插值结果可能偏差很大。我遇到过时间序列里突然跳变的一段缺失,线性插值直接拉平了趋势,导致后续分析全错。

插值代码示例:
# 构造示例数据,中间有缺失
ts = pd.Series([1, 2, np.nan, np.nan, 5, 6, np.nan, 8])

# 线性插值
ts_linear = ts.interpolate(method='linear')
print("线性插值:\n", ts_linear)

# 多项式插值(需要设置 order)
ts_poly = ts.interpolate(method='polynomial', order=2)
print("多项式插值(order=2):\n", ts_poly)
时间序列注意:如果数据按时间排序,务必加上 interpolate(method='time'),它会以时间索引的间隔来插值,而不是直接按行号。

2.3 面试高频问题与思路

面试官问得最多的不是「怎么做」,而是「为什么」。我整理了几个典型的追问,你提前想好怎么答。

面试问题 核心考察点 推荐回答思路
缺失值很多(比如 80%)怎么办? 特征重要性判断 先分析缺失是否与目标变量相关;若为随机缺失且特征重要,用模型预测填充;否则直接删列
填充均值与填充中位数有何区别? 对异常值的敏感度 均值受异常值影响大,中位数更稳健;数据分布偏斜时优先用中位数
什么时候用删除比填充好? 对数据完整性的理解 样本量充足且缺失比例小( <5% )时;缺失非随机且无法准确填充时
如何判断缺失是否随机(MCAR/MAR/MNAR)? 缺失机制理解 做独立性检验或分群统计;非随机缺失需要用专门方法(如多重插补)
sklearn 的 SimpleImputer 和 KNNImputer 怎么选? 场景选型能力 样本量小用简单填充;特征间关联性强用 KNNImputer;大数据量考虑 IterativeImputer

关于缺失机制,我得再提一句。MCAR(完全随机缺失)最好处理,MAR(条件随机缺失)次之,MNAR(非随机缺失)最难。面试时如果被问到这个,能区分三种机制,就算加分了。

2.4 实战建议:别让缺失值「静默」影响你的模型

我自己带团队时,要求每个项目都必须做缺失值分析报告,至少包含:每列缺失比例、缺失模式热力图、填充前后分布对比。你想想看,如果缺失值处理不当,后面模型调得再好也是白搭。

最后给个总结性的建议:不要一刀切。缺失值处理没有银弹,针对不同特征、不同缺失机制、不同业务场景,方法都不一样。面试官真正想听的,不是你背出几种方法,而是你能根据情况做出合理的判断。