Python数据分析实战:Pandas处理缺失值的5个高级技巧

真实业务数据从来不会干净。今天把我在项目中踩过的坑,一次性整理给你。

做数据分析的都知道,数据清洗占整个分析工作量的60-80%。而缺失值处理,又是数据清洗中最常见的问题。

很多人遇到缺失值第一反应就是 df.dropna(),一行代码删完。然后呢?数据从10万行变成3万行,分析结论偏差巨大。

今天分享5个在真实项目中验证过的高级技巧,每个都有代码示例。

技巧1:按业务逻辑分组填充

场景:用户收入数据有缺失,但你不能简单地用全局均值填充。一线城市和三线城市的收入差距很大,全局均值会把三线城市的数据拉高。

方案:按城市分组,用组内中位数填充。

import pandas as pd
import numpy as np

# 模拟数据
df = pd.DataFrame({
    'city': ['北京', '上海', '北京', '成都', '上海', '成都', '北京', '成都'],
    'income': [25000, 22000, None, 8000, None, 7500, 28000, None]
})

# 按城市分组,用组内中位数填充
df['income'] = df.groupby('city')['income'].transform(
    lambda x: x.fillna(x.median())
)

print(df)
#    city  income
# 0   北京  25000.0
# 1   上海  22000.0
# 2   北京  26500.0  <- 用北京的中位数填充
# 3   成都   8000.0
# 4   上海  22000.0  <- 用上海的中位数填充
# 5   成都   7500.0
# 6   北京  28000.0
# 7   成都   7750.0  <- 用成都的中位数填充

为什么用中位数不用均值?
收入数据通常右偏分布(少数高收入拉高均值),中位数更稳健。这是数据分析的基本功,但很多人会忽略。

技巧2:时间序列插值法

场景:每日活跃用户数据有几天缺失,不能直接删除(会影响趋势分析),也不能用均值填充(会抹平波动)。

方案:用时间插值法,基于前后数据点推算缺失值。

# 模拟日活数据(4月10日和12日缺失)
dates = pd.date_range('2026-04-08', periods=7)
dau = pd.Series([12000, 13500, None, 14200, None, 15100, 14800], index=dates)

# 线性插值
dau_filled = dau.interpolate(method='time')

print(dau_filled)
# 2026-04-08    12000.0
# 2026-04-09    13500.0
# 2026-04-10    13850.0  <- 基于前后值线性推算
# 2026-04-11    14200.0
# 2026-04-12    14650.0  <- 基于前后值线性推算
# 2026-04-13    15100.0
# 2026-04-14    14800.0

Pandas的 interpolate() 方法支持多种插值策略:linear(线性)、time(考虑时间间隔)、quadratic(二次曲线)等。

技巧3:多重插值法处理高缺失率

场景:某个字段的缺失率超过30%,简单填充会引入大量偏差。

方案:用其他特征做回归预测来填充缺失值。

from sklearn.linear_model import LinearRegression
import numpy as np

# 模拟数据:age和income,income有缺失
df = pd.DataFrame({
    'age': [25, 30, 35, 28, 40, 32, 45, 27, 38, 33],
    'income': [8000, 12000, None, 9500, 22000, None, 25000, 8500, None, 15000]
})

# 分离有值和缺失的数据
known = df[df['income'].notna()]
unknown = df[df['income'].isna()]

# 用已知数据训练回归模型
model = LinearRegression()
model.fit(known[['age']], known['income'])

# 预测缺失值
df.loc[df['income'].isna(), 'income'] = model.predict(unknown[['age']])

print(df.round(0))
#    age    income
# 0   25   8000.0
# 1   30  12000.0
# 2   35  16500.0  <- 模型预测值
# 3   28   9500.0
# 4   40  22000.0
# 5   32  13200.0  <- 模型预测值
# 6   45  25000.0
# 7   27   8500.0
# 8   38  19500.0  <- 模型预测值
# 9   33  15000.0

实际项目中建议用 IterativeImputer(sklearn提供),它会对每个缺失特征迭代使用其他特征做预测,精度更高。

技巧4:标记缺失值本身也是一种信息

场景:用户注册时"年收入"字段为空,这个"空"本身可能意味着"不愿意填"或"收入较低"。

方案:新增一列标记是否缺失,再填充原始列。

# 新增缺失标记列
df['income_missing'] = df['income'].isna().astype(int)

# 再用中位数填充原始列
df['income'] = df['income'].fillna(df['income'].median())

print(df)
#    age    income  income_missing
# 0   25   8000.0                0
# 1   30  12000.0                0
# 2   35  15000.0                1  <- 标记为缺失
# 3   28   9500.0                0
# 4   40  22000.0                0
# 5   32  15000.0                1  <- 标记为缺失

# 后续建模时,income_missing可以作为特征使用
# 模型能学到"缺失"这个模式

船长经验:在金融风控和用户分析项目中,缺失标记列往往比填充值本身更有预测力。一个不愿意填收入的用户,违约概率可能更高。

技巧5:缺失值可视化诊断

场景:数据集有50多个字段,你需要快速了解缺失值的分布情况。

方案:用缺失值热力图和统计表做快速诊断。

import pandas as pd

# 模拟多字段数据集
np.random.seed(42)
df = pd.DataFrame({
    'user_id': range(1000),
    'age': np.random.choice([np.nan, *range(18, 65)], 1000),
    'income': np.random.choice([np.nan, *range(3000, 50000, 1000)], 1000),
    'city': np.random.choice([np.nan, '北京', '上海', '广州', '深圳'], 1000),
    'login_days': np.random.choice([np.nan, *range(1, 365)], 1000),
    'order_count': np.random.choice([np.nan, *range(0, 50)], 1000),
})

# 1. 缺失值统计表
missing_stats = pd.DataFrame({
    '缺失数量': df.isnull().sum(),
    '缺失比例': (df.isnull().sum() / len(df) * 100).round(1),
    '数据类型': df.dtypes
})
missing_stats = missing_stats[missing_stats['缺失数量'] > 0].sort_values(
    '缺失比例', ascending=False
)
print(missing_stats)
#              缺失数量  缺失比例  数据类型
# income         520    52.0  float64
# city           498    49.8   object
# age            503    50.3  float64
# login_days     495    49.5  float64
# order_count    502    50.2  float64

# 2. 快速判断:缺失是否随机
# 按某个字段分组看缺失率差异
print(df.groupby('city')['income'].apply(lambda x: x.isna().mean()))
# city
# 上海    0.50
# 北京    0.51
# 广州    0.50
# 深圳    0.49
# 如果某组缺失率显著偏高,说明缺失不是随机的

总结:缺失值处理决策流程

第一步:先看缺失率——低于5%可以直接删或简单填充,高于30%需要特殊处理

第二步:判断缺失模式——是随机缺失还是系统性缺失(系统性缺失要标记)

第三步:选择填充策略——业务分组 > 时间插值 > 回归预测 > 全局统计量

第四步:始终保留缺失标记列——"缺失"本身就是信息

第五步:验证填充效果——对比填充前后的分布,确保没有引入偏差

船长的话:数据不说谎,但会误导人。缺失值处理不当,你的分析结论就是建立在沙子上的城堡。这5个技巧覆盖了90%的真实业务场景,建议收藏备用。别再用dropna一键删完了。

你平时怎么处理缺失值?有没有遇到过什么奇葩场景?评论区聊聊。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐