数据缺失处理
场景描述
用户画像表中 30% 的用户缺少"年龄"字段,用户行为日志偶尔出现 NULL 的 user_id。需要制定缺失数据处理策略。
缺失数据分类
| 类型 | 英文 | 说明 | 示例 |
|---|---|---|---|
| 完全随机缺失 | MCAR | 缺失与任何变量无关 | 问卷随机跳过某题 |
| 随机缺失 | MAR | 缺失与其他已知变量有关 | 年龄缺失与注册渠道相关 |
| 非随机缺失 | MNAR | 缺失与缺失值本身有关 | 高收入用户倾向不填收入 |
不同类型缺失的处理差异
- MCAR:可以直接删除,不会引入偏差
- MAR:可以用其他变量推断填充
- MNAR:任何填充方法都可能引入偏差,需谨慎处理
处理策略
SQL 层处理
-- 1. 过滤缺失(适用于 MCAR,缺失比例 < 5%)
SELECT * FROM user_profile WHERE age IS NOT NULL;
-- 2. 默认值填充
SELECT user_id,
COALESCE(age, -1) AS age, -- 数值型用 -1 标记
COALESCE(city, '未知') AS city -- 字符串用 '未知'
FROM user_profile;
-- 3. 均值/中位数填充
SELECT user_id,
COALESCE(age, (SELECT PERCENTILE_APPROX(age, 0.5) FROM user_profile WHERE age IS NOT NULL)) AS age
FROM user_profile;
-- 4. 按分组填充(MAR 场景)
SELECT a.user_id,
COALESCE(a.age, b.avg_age) AS age
FROM user_profile a
LEFT JOIN (
SELECT gender, AVG(age) AS avg_age
FROM user_profile WHERE age IS NOT NULL
GROUP BY gender
) b ON a.gender = b.gender;
Python 层处理
import pandas as pd
df = pd.read_sql("SELECT * FROM user_profile", conn)
# 缺失率统计
print(df.isnull().sum() / len(df) * 100)
# 填充策略
df['age'].fillna(df['age'].median(), inplace=True) # 中位数
df['city'].fillna(df.groupby('province')['city'].transform('first'), inplace=True) # 分组填充
缺失率与处理决策
| 缺失率 | 建议处理 |
|---|---|
| < 5% | 删除缺失行或简单填充 |
| 5%-30% | 分组均值/中位数填充或模型预测 |
| 30%-50% | 标记为缺失分桶(如"年龄未知"),作为单独特征 |
| > 50% | 考虑弃用该字段,或补充收集 |
常见面试问题
Q1: 缺失值直接删除会有什么问题?
答案:
- 样本偏差:如果缺失不是 MCAR,删除后样本分布会偏移
- 信息损失:删除整行会丢失其他完整字段的信息
- 样本量减少:特别是多个字段都有缺失时,删除后可能数据量骤降
Q2: A/B 测试中遇到缺失数据怎么办?
答案:
- 不能直接删除:缺失可能与分组有关(如某渠道用户缺失率高),删除会引入选择偏差
- ITT 分析:按照初始分组分析,缺失值按最保守估计处理
- 敏感性分析:分别按"缺失都算成功"和"缺失都算失败"两种极端假设分析,看结论是否一致