3、缺失值处理:检测、删除与填充

缺失值,说白了就是数据里的「窟窿」。做向量数据清洗时,这玩意儿几乎躲不掉。我个人习惯是,拿到数据第一件事——先看看有没有空值。别急着建模,先把这个坑填平再说。

核心原则:缺失值处理没有银弹。删除还是填充,取决于缺失比例、数据重要性、以及下游模型对缺失的容忍度。

3.1 检测缺失值:先找到「窟窿」在哪

检测缺失值,方法很简单。但要注意,不同库对「缺失」的定义不一样。

  • NumPy 数组:用 np.isnan() 或 np.isinf()。NaN 和 Inf 都是缺失。
  • Pandas DataFrame:用 df.isnull() 或 df.isna()。返回布尔矩阵。
  • 向量场景:如果向量里混了 None 或字符串 "null",记得先统一转换。
import numpy as np
import pandas as pd

# 示例:检测缺失
data = np.array([[1.0, 2.0, np.nan],
                 [4.0, np.nan, 6.0],
                 [7.0, 8.0, 9.0]])

mask = np.isnan(data)
print("缺失位置:\n", mask)

# 向量场景:检查是否有任何缺失
has_missing = np.any(mask)
print("是否存在缺失:", has_missing)

我的习惯:检测完缺失后,我会顺手算一下每列的缺失比例。如果某列缺失超过 70%,我基本直接删掉——补了也没意义。

3.2 删除策略:什么时候该「一刀切」?

删除缺失值,最粗暴,但也最有效。不过,别乱删。

场景 推荐操作 原因
缺失比例 < 5% 直接删除该行 对整体数据影响极小
缺失比例 5%~20% 视情况删除或填充 需结合业务判断
缺失比例 > 20% 优先考虑填充 删除会丢失太多信息
向量场景:整行缺失 直接删除 空向量没有意义
# 删除包含缺失的行
clean_data = data[~np.any(mask, axis=1)]
print("删除后数据:\n", clean_data)

# 向量场景:删除空向量
vectors = np.array([[1, 2, 3],
                    [0, 0, 0],   # 空向量
                    [4, 5, 6]])
# 删除所有元素为0的向量
valid_vectors = vectors[~np.all(vectors == 0, axis=1)]
print("有效向量:\n", valid_vectors)

我曾经踩过的坑:有一次做推荐系统,直接删了缺失行,结果发现删掉的都是冷启动用户的数据。模型训练完,新用户来了直接崩。所以,删除前一定要看看缺失数据有没有业务特殊性。

3.3 填充策略:把「窟窿」补上

填充比删除更精细。选什么值来补,直接影响模型效果。

3.3.1 均值/中位数/众数填充

  • 均值填充:适用于数值型、数据分布对称。但容易受异常值影响。
  • 中位数填充:适用于数值型、数据有偏或含异常值。我个人更常用这个。
  • 众数填充:适用于类别型。比如用户性别缺失,用出现最多的性别补。
# 均值填充
mean_val = np.nanmean(data[:, 0])
data[:, 0][np.isnan(data[:, 0])] = mean_val

# 中位数填充
median_val = np.nanmedian(data[:, 1])
data[:, 1][np.isnan(data[:, 1])] = median_val

# 众数填充(类别型示例)
from scipy import stats
category_data = np.array(['A', 'B', 'A', None, 'B', 'A'])
mode_val = stats.mode(category_data[category_data != None])[0][0]
category_data[pd.isnull(category_data)] = mode_val

3.3.2 前向填充(Forward Fill)

前向填充,就是用上一个有效值来补当前缺失。特别适合时间序列数据。

# 前向填充示例
time_series = np.array([1.0, np.nan, np.nan, 4.0, np.nan, 6.0])
for i in range(1, len(time_series)):
    if np.isnan(time_series[i]):
        time_series[i] = time_series[i-1]
print("前向填充结果:", time_series)

我建议:前向填充在传感器数据里特别好用。比如温度传感器每10秒采集一次,中间丢了几秒数据,用前一秒的值补上,比用全局均值合理得多。

3.4 向量场景下的特殊处理

向量数据跟普通表格数据不一样。每个向量是一个整体,缺失处理要考虑「向量完整性」。

向量场景 特殊处理方式 说明
向量中部分维度缺失 维度均值填充 用该维度在所有向量中的均值补
整个向量缺失 删除该向量 空向量无意义
向量长度不一致 截断或补零 统一到固定长度
稀疏向量缺失 保持稀疏结构 不要填充为稠密向量
# 向量场景:维度均值填充
vectors = np.array([[1.0, 2.0, np.nan],
                    [4.0, np.nan, 6.0],
                    [7.0, 8.0, 9.0]])

# 按列计算均值,忽略NaN
col_means = np.nanmean(vectors, axis=0)
print("各维度均值:", col_means)

# 填充缺失
for i in range(vectors.shape[1]):
    mask_col = np.isnan(vectors[:, i])
    vectors[mask_col, i] = col_means[i]
print("填充后向量:\n", vectors)

注意:向量场景下,千万别用全局均值填充所有缺失维度。每个维度有自己的分布,混在一起补,向量空间结构就乱了。我见过有人这么干,结果聚类效果直接崩了。

3.5 缺失值处理流程总览

下面这张图,概括了缺失值处理的完整决策路径。你可以把它贴在工位上。

缺失值处理决策流程 缺失值处理决策流程 检测缺失值 缺失比例 < 5% ? 是 删除缺失行 否 数值型还是类别型? 数值型 均值/中位数填充 类别型 众数填充 向量场景特殊处理 完成缺失值处理 避坑提示 • 删除前检查业务特殊性 • 向量场景保持维度一致性 • 稀疏向量不要填充为稠密

3.6 实战建议:我的「三板斧」

做多了缺失值处理,我总结了一套自己的套路,分享给你:

  1. 先看比例:缺失比例 < 5%,直接删。别纠结。
  2. 再看分布:数据对称用均值,有偏用中位数。类别型用众数。
  3. 最后看场景:时间序列用前向填充。向量数据保持维度一致性。

我的经验:如果你不确定用什么策略,先做一次均值填充,再做一个中位数填充,对比两个结果对模型的影响。哪个效果好,就用哪个。别在缺失值处理上花太多时间——它只是预处理的一步,不是终点。