5.2 重复数据与实体解析
缺失分析追问为什么预期事实没有出现。重复分析则提出相反问题:重复的行是一件事件被记录两次、多个合法事件恰好共享属性,还是同一实体的冲突版本?答案取决于第 1 章定义的观测单位,以及第 4 章实施的键契约。
删除之前先定义“重复”
整行重复(full-row duplicate)是所选字段全部重复。键重复(key duplicate)则是模式要求唯一的字段重复。两者回答不同问题:
full_row = orders.duplicated(keep=False)
same_order_id = orders.duplicated("order_id", keep=False)两行可能拥有同一个 order_id,却在状态或更新时间上不同。它们不是整行重复,但违反“一笔订单一行”的契约。相反,两笔不同订单完全可以拥有相同区域、分钟数与费用。
诊断阶段应使用 keep=False 标记重复组中的所有成员:
conflicts = (
orders.loc[same_order_id]
.sort_values(["order_id", "updated_at"])
)默认的 keep="first" 会标记后续记录,却不标记第一次出现。应用已经决定的规则时这很方便,但在审计阶段可能隐藏完整冲突。
还要检查重复组大小:
group_sizes = (
orders.groupby("order_id", dropna=False)
.size()
.sort_values(ascending=False)
)缺失键需要单独处理。多行 order_id 都缺失,并不表示它们因为键值都不存在就属于同一订单。
幸存记录需要业务规则
drop_duplicates 应用的是位置保留规则:
first_seen = orders.drop_duplicates("order_id", keep="first")
last_seen = orders.drop_duplicates("order_id", keep="last")“first”和“last”指当前行顺序,而不是时间最早和最晚。源顺序变化后,幸存记录也可能改变。应先按明确优先级排序:
ranked = orders.sort_values(
["order_id", "success", "updated_at"],
ascending=[True, True, True],
)
clean = ranked.drop_duplicates("order_id", keep="last")
assert clean["order_id"].is_unique成功记录排在失败记录之后,因此这条规则保留最新的成功版本。真实系统可能依据来源权威性、完整度、审批状态或事件版本。应先用文字写出规则,再编码,并测试并列情况。
删除前要保存数据血缘:
work = orders.reset_index(names="source_row_id")审计表应把每条被删除的 source_row_id 映射到幸存记录,并保存规则、时间和原因。不要覆盖唯一的原始副本。去重会改变计数、总量,甚至总体定义。
近似重复需要实体解析
跨系统可能没有完全一致的键。同一位客户可能写作:
Chen Wei | chen.wei@example.com | 200120
Wei Chen | chenwei@example.com | 200120
陈伟 | missing | 200120实体解析(entity resolution)用于连接被认为描述同一现实实体的记录。与精确去重不同,它通常是不确定推断。假合并(false merge)会把不同的人合在一起;漏匹配(missed match)会让同一个人分散在多个 ID 下。两类错误都很重要,但成本可能截然不同。
一条可审查流程包含多个阶段。
1. 保存并规范化匹配字段
保留原值,另建匹配键:
customers["name_key"] = (
customers["name"]
.astype("string")
.str.normalize("NFKC")
.str.strip()
.str.casefold()
)规范化让比较更一致,却不能证明身份。两个不同客户可能共享同一个规范姓名。
2. 生成合理候选对
比较 条记录的所有组合,需要
次比较。一百万条记录会产生近五千亿个候选对。阻塞(blocking)只比较共享邮编、邮箱域或电话前缀等粗特征的记录:
for _, block in customers.groupby("postal_code", dropna=False):
compare_pairs_within(block)阻塞节省计算,却可能漏掉落在不同块中的真实匹配。应使用多条有依据的阻塞规则,并在已知匹配样本上测量覆盖率。
3. 为多项证据评分
候选特征可以包括规范姓名相似度、邮箱完全一致、地址相似度、日期接近度或电话尾号一致。证据必须结合背景解释:常见姓氏相同的证据强度,远低于经过验证的邮箱相同。
不要只因为能提高分数,就加入受保护或敏感特征。身份系统会影响真实的人,因此特征选择、访问权限与错误复核都需要治理。
4. 按置信度分开处理
一项实用策略可以设为:
- 高分且有强精确证据:自动匹配。
- 模糊的中间区间:人工复核。
- 低分或存在矛盾证据:不匹配。
必须在带标签的记录对上评估阈值。提高匹配阈值通常减少假合并,却增加漏匹配。真实匹配很少时,只看准确率会产生误导;应分别检查两类错误及其成本。
记录对决策必须形成一致实体
成对匹配可以构成图。如果 A 匹配 B,B 匹配 C,连通分量规则可能把三者放入同一实体,即使 A 与 C 互相矛盾。这就是传递闭包(transitive closure),它会放大一条错误连接。
分配规范 ID 之前,应检查实体级约束,例如不兼容的已验证邮箱、不可能重叠的账户,或过大的聚类规模。还应保存:
- 每条源记录与源系统标识符。
- 候选特征、分数、模型或规则版本,以及决策。
- 适用时的人工复核者与理由。
- 带生效日期的规范 ID 映射。
- 撤销或替代错误合并的方法。
精确去重和实体解析都依赖一致表示。下一节将有意构建这些表示,把机械规范化与业务含义分开,并覆盖文本、类别、日期和单位。