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 缺失值处理流程总览
下面这张图,概括了缺失值处理的完整决策路径。你可以把它贴在工位上。
3.6 实战建议:我的「三板斧」
做多了缺失值处理,我总结了一套自己的套路,分享给你:
- 先看比例:缺失比例 < 5%,直接删。别纠结。
- 再看分布:数据对称用均值,有偏用中位数。类别型用众数。
- 最后看场景:时间序列用前向填充。向量数据保持维度一致性。
我的经验:如果你不确定用什么策略,先做一次均值填充,再做一个中位数填充,对比两个结果对模型的影响。哪个效果好,就用哪个。别在缺失值处理上花太多时间——它只是预处理的一步,不是终点。