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 数据清洗流程框架

说了这么多技巧,最后我画了一张流程图,把整个数据清洗的流程串起来。这也是我每次做报告前必走的流程。

数据清洗流程框架 1. 数据源识别 2. 数据质量评估 3. 清洗策略选择 4. 执行清洗 5. 清洗后验证 通过 6. 数据入库 不通过,回退到步骤3 💡 说明: 清洗后验证是关键环节。我会随机抽取 5% 的数据做人工复核,确保清洗逻辑没有引入新的错误。

警告:清洗后的数据一定要做版本管理。我曾经因为清洗脚本的一个 bug,把整张表洗坏了,还好有备份,不然三天的工作全白费。建议每次清洗都生成一个新表或新文件,保留原始数据不动。

好了,这一章的内容就这些。数据清洗没有银弹,核心就是「先诊断、再开药、留后路」。你想想看,如果连数据都不可信,那报告写得再漂亮又有什么用呢?