3. 缺失值处理:识别、可视化、删除与填充

数据清洗里,缺失值处理是最绕不开的一步。我做了这么多年数据项目,几乎没见过哪份原始数据是干干净净的。缺失值处理不好,后面建模全是白搭。说白了,这一步就是给你的数据“查漏补缺”。

3.1 缺失值识别:先找到“窟窿”在哪

处理缺失值的第一步,当然是找到它们。别指望肉眼去翻几万行数据,那太傻了。我一般用几个快速方法。

3.1.1 基础统计法

用 isnull() 配合 sum(),一眼就能看到每列缺了多少。

import pandas as pd

df = pd.read_csv('sales_data.csv')
# 统计每列缺失值数量
missing_counts = df.isnull().sum()
print(missing_counts[missing_counts > 0])

输出结果类似这样:

列名 缺失数量 缺失比例
age 45 9.0%
income 12 2.4%
city 3 0.6%
我的习惯:我会先看缺失比例。超过 50% 的列,我基本直接考虑删除。低于 5% 的,填充一下就行。中间那档,得具体分析。

3.1.2 可视化辅助识别

数字有时候不够直观。我特别喜欢用热力图来看缺失模式。为什么?因为能看出缺失是不是有规律。

import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
sns.heatmap(df.isnull(), cbar=False, cmap='viridis')
plt.title('缺失值分布热力图')
plt.show()

如果热力图上出现明显的横条或竖条,那说明缺失不是随机的。比如某台机器在某个时间段坏了,导致那一批数据全缺了。这种信息对后续处理很有用。

3.2 缺失值可视化:让数据“说话”

除了热力图,我还会用条形图来展示缺失比例。这样给业务方看的时候,他们一眼就能明白问题的严重性。

missing_ratio = df.isnull().mean().sort_values(ascending=False)
missing_ratio = missing_ratio[missing_ratio > 0]

plt.figure(figsize=(12, 6))
missing_ratio.plot(kind='bar')
plt.title('各列缺失比例')
plt.ylabel('缺失比例')
plt.xticks(rotation=45)
plt.show()

嗯,这里要注意:可视化不是为了好看,是为了帮你做决策。看到某个特征缺失 80%,你还会花时间去填充它吗?我肯定不会。

缺失值处理全流程 缺失值处理 识别缺失值 可视化分析 处理策略 统计法 热力图 条形图 删除缺失 填充缺失 均值/中位数/众数 插值法 核心原则: 缺失比例 < 5% → 直接填充;5%-50% → 分析后填充;> 50% → 考虑删除该列。

3.3 删除缺失值:简单粗暴,但有代价

删除是最直接的方法。但代价是什么?信息丢失。我一般只在两种情况下用删除:

  • 缺失比例极低(比如 < 1%),删掉几行不影响大局。
  • 缺失比例极高(比如 > 70%),这列基本废了,留着也没用。
# 删除包含任何缺失值的行
df_drop_rows = df.dropna()

# 删除缺失比例超过 70% 的列
threshold = 0.7
df_drop_cols = df.dropna(thresh=len(df) * (1 - threshold), axis=1)
我曾经踩过的坑:有一次我直接 dropna() 删了 30% 的数据,结果模型训练出来偏差特别大。后来一查,删除的数据集中在某个用户群体上,导致样本严重不均衡。所以删除前,一定先看看缺失数据有没有规律。

3.4 填充缺失值:更精细的做法

大多数时候,我们选择填充而不是删除。填充的方法有好几种,选哪个取决于数据特点。

3.4.1 均值填充

适用于数值型数据,且数据分布比较对称(接近正态分布)。

df['age'].fillna(df['age'].mean(), inplace=True)

3.4.2 中位数填充

如果数据有异常值,均值会被拉偏。这时候中位数更稳健。我处理收入数据时,几乎都用中位数。

df['income'].fillna(df['income'].median(), inplace=True)

3.4.3 众数填充

适用于分类数据。比如“城市”这一列,缺失了就填出现次数最多的那个城市。

df['city'].fillna(df['city'].mode()[0], inplace=True)

3.4.4 插值法

对于时间序列数据,插值法特别好用。它会根据前后数据点来推算缺失值。

# 线性插值
df['temperature'].interpolate(method='linear', inplace=True)

# 时间序列插值(适用于时间索引)
df['sales'].interpolate(method='time', inplace=True)

你想想看,如果某天的温度数据缺失了,用前后两天的平均值来补,是不是比用全年均值更合理?插值法的优势就在这里。

3.5 不同填充方法的对比

方法 适用场景 优点 缺点
均值填充 数值型,数据对称分布 计算简单,保持均值不变 受异常值影响大,降低方差
中位数填充 数值型,有异常值 稳健,不受极端值影响 忽略数据分布形态
众数填充 分类数据 符合多数情况 可能引入偏差
插值法 时间序列,有序数据 利用前后信息,更合理 不适用于无序数据

我的建议:没有万能方法。我每次都会先做缺失值可视化,理解缺失模式,再选填充策略。如果条件允许,可以试试多种方法,对比一下对模型的影响。数据清洗没有标准答案,只有最适合当前场景的方案。

好了,缺失值处理这块就聊到这。记住一个核心:别盲目删除,也别无脑填充。理解你的数据,再做决定。