4. 数据清洗之空值处理:空值填充策略

空值处理,说白了就是跟「缺失的数据」打交道。我做了这么多年ETL,最头疼的往往不是数据量大,而是数据里那些莫名其妙的空值。你想想看,一个字段空着,到底是没采集到?还是用户没填?还是系统bug?

嗯,这里我们要先明确一点:空值不等于0,也不等于空字符串。在大多数数据库里,NULL就是NULL,它代表「未知」。我见过太多新手上来就把NULL替换成0,结果统计平均值时直接崩了。

4.1 空值填充策略

填充空值,说白了就是用某个「合理的值」去替代NULL。但选什么值,得看业务场景。我个人习惯先问三个问题:

  • 这个字段是数值型还是类别型?
  • 数据分布是否稳定?有没有极端值?
  • 业务上允许用统计值替代吗?

4.1.1 均值填充

均值填充适用于数值型字段,且数据分布接近正态分布的场景。我在项目中遇到过用户年龄字段有空值,直接用均值填充,结果发现年龄分布被拉偏了——因为原始数据里有很多老年人,均值偏大,导致年轻用户被错误地填成了中年。

-- 均值填充示例(SQL)
UPDATE user_profile
SET age = (SELECT AVG(age) FROM user_profile WHERE age IS NOT NULL)
WHERE age IS NULL;
注意:均值对异常值非常敏感。如果数据里有极端值(比如年龄=200),均值会被严重拉偏。我建议先做一次异常值检测,再决定是否用均值。

4.1.2 中位数填充

中位数比均值更稳健。说白了,它不受极端值影响。比如收入字段,马云一个人就能把平均收入拉高到几千万,但中位数可能只有几千块。这种情况下,用中位数填充空值更合理。

-- 中位数填充示例(Python + Pandas)
import pandas as pd
import numpy as np

df['income'].fillna(df['income'].median(), inplace=True)
我的习惯:只要数据分布偏态严重(比如长尾分布),我优先用中位数而不是均值。你可以用直方图快速判断分布形态。

4.1.3 众数填充

众数适用于类别型字段。比如性别、城市、产品类别这些。为什么?因为均值和中位数对类别数据没意义。你总不能说「性别的平均值是0.5」吧?

-- 众数填充示例(SQL)
-- 先找出出现次数最多的值
SELECT city, COUNT(*) AS cnt
FROM user_profile
WHERE city IS NOT NULL
GROUP BY city
ORDER BY cnt DESC
LIMIT 1;

-- 然后用这个值填充
UPDATE user_profile
SET city = '北京'
WHERE city IS NULL;
核心原则:数值型用均值或中位数,类别型用众数。但别忘了——如果某个类别的缺失率超过50%,用众数填充反而会引入偏差。我曾经踩过这个坑,后来加了缺失率阈值判断。

4.1.4 默认值填充

默认值是最简单粗暴的方式。比如:

  • 年龄空值填0(但0岁不合理,建议填-1或999)
  • 性别空值填'未知'
  • 金额空值填0(但要注意业务含义)
-- 默认值填充示例
UPDATE order_table
SET discount_amount = 0
WHERE discount_amount IS NULL;
避坑指南:我曾经把一个金额字段的空值全填成0,结果下游做聚合统计时,平均折扣率被严重拉低。后来我改成填NULL,让业务方自己决定怎么处理。记住:有时候保留NULL比填一个错误的值更好

4.2 空值删除策略

删除空值,听起来简单,但风险很大。我见过有人直接 DROP NULL ROWS,结果删掉了30%的数据,模型直接废了。

什么时候该删?我总结了几条经验:

  • 缺失率极低(比如<5%):直接删除影响不大
  • 缺失完全随机:删除后不会引入偏差
  • 关键字段缺失:比如主键、业务唯一标识,没法填充,只能删
-- 删除空值示例(SQL)
DELETE FROM user_profile
WHERE user_id IS NULL
   OR email IS NULL;
我的建议:删除前先备份,或者加一个标记字段。我习惯先做一次缺失率统计,再决定是删还是填。

4.3 业务规则填充法

这是最「高级」的空值处理方式。说白了,就是利用业务逻辑来推断空值。比如:

  • 用户最近登录时间空值,可以用注册时间+30天来估算
  • 订单金额空值,可以用商品单价×数量来计算
  • 用户所在城市空值,可以用IP地址反查
-- 业务规则填充示例
-- 如果订单金额为空,用单价×数量填充
UPDATE order_detail
SET total_amount = unit_price * quantity
WHERE total_amount IS NULL
  AND unit_price IS NOT NULL
  AND quantity IS NOT NULL;
实战经验:我在做电商数据清洗时,遇到过「优惠券金额」字段大量空值。后来发现,这些空值其实代表「没有使用优惠券」。所以正确的填充值应该是0,而不是均值或中位数。这就是业务规则的力量。

4.4 总结与选择建议

策略 适用场景 风险点
均值填充 数值型、正态分布 受异常值影响大
中位数填充 数值型、偏态分布 丢失分布信息
众数填充 类别型 缺失率高时偏差大
默认值填充 业务含义明确 可能掩盖真实问题
删除策略 缺失率低、随机缺失 可能丢失重要样本
业务规则填充 有明确业务逻辑 规则维护成本高

嗯,最后说一句:没有万能策略。我每次做空值处理前,都会先跑一遍缺失率统计,再画个分布图,最后跟业务方确认。你想想看,数据清洗的本质不是「把空值填满」,而是「让数据更真实地反映业务」。

核心原则:能保留尽量保留,能推断尽量推断,实在不行再删除。千万别为了「好看」而乱填数据。

公众号:蓝海资料掘金营,微信deep3321