数据清洗入门:一份来自实践的经验笔记
刚接触数据分析的人,往往把大部分精力放在建模和可视化上,但真正做过几个项目就会发现,数据清洗才是耗时最多的环节。有统计说,数据分析师约60%到80%的时间花在清洗和整理数据上。这个比例听起来夸张,实际做起来只会觉得保守。一份来自业务系统的导出表,列名可能是“用户ID”“用户id”“User_ID”三种写法混在一起,日期字段里既有“2024-01-05”也有“2024/1/5”,还有几行写着“暂无”。不处理这些,后续任何观察和计算都站不住脚。
清洗的第一步不是动手改,而是先看。拿到一份新数据,先做几件事:看行数和列数,看每列的数据类型,看缺失比例,看数值型字段的最小值、最大值和分位数。比如某张订单表有12万行,其中“收货地址”缺失约3%,而“优惠金额”有47%为空。这两个缺失的含义完全不同——地址缺失可能是录入遗漏,优惠金额为空很可能表示“未使用优惠”,应该填0而不是删掉。如果不先看分布就统一删缺失行,会白白丢掉近一半数据,后面的走势分析就会严重失真。
缺失值的处理没有标准答案,常见做法有删除、填充和保留标记三种。删除适合缺失比例很低且随机的情况,比如某列缺失不到1%,删掉几十行不影响整体。填充要看字段含义:数值型可以用中位数,类别型可以用众数,时间序列则可以考虑前向填充。但更稳妥的做法是加一列标记,比如“优惠金额_是否缺失”,把缺失本身当作一个信息保留下来。曾经处理过一份用户活跃表,其中“上次登录时间”缺失的用户,后来发现大多是注册后从未登录的账号,这个缺失模式本身就是有价值的观察。
格式混乱是另一类高频问题。同一列里出现“北京市”“北京”“北京市朝阳区”,需要先统一粒度;手机号有的带86前缀,有的带横线,得先规范成纯数字;日期字段时区不一致,跨天统计就会出错。一个具体的例子:某份日志表的时间戳混用了秒级和毫秒级,10位和13位数字并存。如果不做转换直接排序,13位的时间会被排到很远的位置,导致按小时聚合的走势图出现一个巨大的尖峰。处理办法是先判断位数,再统一乘除到同一单位。这类问题没有捷径,只能逐列检查。
重复记录同样容易被忽略。完全相同的行可以用去重函数直接处理,但更麻烦的是“近似重复”:同一个用户因为手机号和邮箱分别注册了两次,姓名相同、证件号相同,只是ID不同。这种要靠业务主键来判断。实践中可以先用几个关键字段做分组计数,看有没有一组出现两次以上。比如按“姓名+证件号”分组,如果发现某组有两条记录且创建时间相差不到一分钟,基本可以判定为重复提交。去重时保留哪一条,要看业务规则,通常保留信息更完整或时间更晚的那条。
异常值需要单独对待。数值列里出现远超正常范围的值,可能是录入错误,也可能是真实的高价值样本。比如某电商客单价中位数是89元,但有一行显示89999元。直接删除可能丢掉大客户,保留又会影响均值。比较克制的做法是先用箱线图或分位数观察,把超过99.9%分位数的值单独标记出来,再结合业务判断。如果确认是输入错误,比如多打了一个9,就修正;如果无法判断,就保留但在分析时做缩尾处理。预测模型对异常值敏感,这一步不做,结果可能偏差很大。
把这些步骤串起来,一个可复用的流程大概是:先做数据概览,再处理缺失,接着统一格式,然后去重,最后标记异常值。每一步都保留原始列的副本,方便回溯。清洗完的数据最好再跑一遍概览,对比清洗前后的行数、缺失比例和关键字段的分布。如果行数从12万降到11.6万,缺失比例从47%降到0,但订单金额的中位数几乎没变,说明清洗是合理的。反过来,如果中位数大幅移动,就要检查是不是删错了行。数据清洗没有一劳永逸的模板,但有一套稳定的检查顺序,能少走很多弯路。