3. 数据收集与清洗:数据源的识别、数据质量评估、常见数据清洗技巧
数据收集与清洗,说白了就是「把饭做熟前的洗菜切菜」。你想想看,再厉害的分析模型,喂进去一堆脏数据,出来的结果你敢信吗?我见过太多团队花 80% 的时间在清洗上,最后 20% 的时间做分析,结果反而最靠谱。这一章,我们就聊聊怎么把「菜」洗干净。
3.1 数据源的识别:你的数据从哪来?
做完整性报告,第一步不是写代码,而是搞清楚数据源在哪。我个人习惯先画一张数据地图,把每个字段的来路标清楚。
常见的数据源类型,我整理了一张表:
| 数据源类型 | 典型格式 | 常见问题 |
|---|---|---|
| 关系型数据库 | MySQL, PostgreSQL, Oracle | 字段缺失、外键断裂 |
| 日志文件 | .log, .txt, JSON Lines | 时间戳格式不统一、乱码 |
| API 接口 | JSON, XML | 字段结构变化、限流导致数据不全 |
| Excel/CSV | .xlsx, .csv | 合并单元格、空行、日期被自动转换 |
| 埋点数据 | Parquet, Avro | 事件丢失、重复上报 |
嗯,这里要注意:不要假设数据源永远不变。我在项目中遇到过,上游数据库字段类型从 INT 改成了 VARCHAR,下游脚本直接崩了。所以每次跑报告前,我都会先跑一遍数据源探针脚本,确认结构没变。
3.2 数据质量评估:先看病,再开药
拿到数据后,别急着清洗。先做质量评估,就像医生看病,得先诊断。我一般从四个维度下手:
- 完整性:有没有空值?空值比例多少?
- 准确性:数值在不在合理范围?比如年龄 200 岁,明显有问题。
- 一致性:同一字段在不同表里格式一样吗?比如日期有的是 "2024-01-01",有的是 "01/01/2024"。
- 时效性:数据是不是最新的?有没有延迟?
我的经验法则:如果某个字段的空值率超过 60%,我会直接考虑剔除它。如果空值率在 20%-60%,我会看能不能用其他字段推算。低于 20%,直接填充或删除行。
为什么会这样?因为空值太多,你强行填充反而会引入噪声,还不如不要。
3.3 常见数据清洗技巧:动手实操
好了,诊断完了,该动手了。下面是我最常用的几个清洗技巧,每个都踩过坑。
3.3.1 处理缺失值
缺失值处理,说白了就三种策略:删、填、猜。
- 删除:直接删掉有缺失的行或列。适合缺失比例极低的情况。
- 填充:用均值、中位数、众数填充。适合数值型字段。
- 预测:用机器学习模型预测缺失值。适合重要字段,但成本高。
我曾经犯过一个错:用全局均值填充用户收入字段,结果把低收入和高收入用户全拉平了,分析结论完全失真。后来我改用分组均值(按城市、职业分组),效果好了很多。
3.3.2 处理重复数据
重复数据是脏数据的重灾区。我见过一个报告,因为没去重,用户数直接翻了一倍。
去重的核心逻辑:
-- SQL 示例:按用户 ID 去重,保留最新一条
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY update_time DESC) AS rn
FROM raw_data
)
SELECT * FROM ranked WHERE rn = 1;
嗯,这里要注意:去重前一定要确认去重键。有时候你以为 user_id 是唯一的,结果发现同一个用户在不同系统里 ID 不一样。这时候就得用更复杂的匹配逻辑了。
3.3.3 处理异常值
异常值,就是那些明显「不对劲」的数据。比如订单金额是负数,或者年龄是 200 岁。
我常用的方法:
- 3σ 原则:超出均值 ±3 个标准差的值,视为异常。
- 箱线图法:超出上下四分位数 1.5 倍 IQR 的值,视为异常。
- 业务规则:比如订单金额不能为负,年龄不能超过 120。
小技巧:不要一棍子打死所有异常值。有时候异常值反而是最有价值的信息,比如突然的流量暴增可能意味着营销活动生效了。先标记,再分析,最后决定是剔除还是保留。
3.3.4 格式统一与标准化
格式不统一,是数据清洗里最烦人的事。日期格式、电话号码、邮政编码,每个系统都有自己的「方言」。
我一般会写一个标准化函数,统一处理:
import re
from datetime import datetime
def standardize_date(date_str):
"""统一日期格式为 YYYY-MM-DD"""
patterns = [
(r'(\d{4})[-/](\d{1,2})[-/](\d{1,2})', r'\1-\2-\3'),
(r'(\d{1,2})[-/](\d{1,2})[-/](\d{4})', r'\3-\1-\2'),
(r'(\d{4})年(\d{1,2})月(\d{1,2})日', r'\1-\2-\3'),
]
for pattern, replacement in patterns:
if re.match(pattern, date_str):
return re.sub(pattern, replacement, date_str)
return None # 无法识别的格式
你看,代码不长,但能省下大量手动改格式的时间。
3.4 数据清洗流程框架
说了这么多技巧,最后我画了一张流程图,把整个数据清洗的流程串起来。这也是我每次做报告前必走的流程。
警告:清洗后的数据一定要做版本管理。我曾经因为清洗脚本的一个 bug,把整张表洗坏了,还好有备份,不然三天的工作全白费。建议每次清洗都生成一个新表或新文件,保留原始数据不动。
好了,这一章的内容就这些。数据清洗没有银弹,核心就是「先诊断、再开药、留后路」。你想想看,如果连数据都不可信,那报告写得再漂亮又有什么用呢?