
本文介绍如何在 Pandas 中对两组含数字集合(如 {1, 2, 3})的姓名字段(name_b 和 name_s)进行精确关联匹配,通过 explode() 展开集合并 merge() 对齐公共数字,生成完整的候选匹配结果表。
本文介绍如何在 pandas 中对两组含数字集合(如 `{1, 2, 3}`)的姓名字段(`name_b` 和 `name_s`)进行精确关联匹配,通过 `explode()` 展开集合并 `merge()` 对齐公共数字,生成完整的候选匹配结果表。
在实际数据清洗与实体对齐任务中,常遇到一类结构化但非标准的匹配需求:两个字段(如 name_b 和 name_s)各自关联一组编号(以 Python set、字符串或列表形式存储),需找出所有满足“编号交集非空”的 (name_b, name_s) 组合,并按每个共同编号展开为独立匹配行。这种场景常见于药品别名映射、设备型号-序列号关联、或跨系统人员标识对齐等任务。
Pandas 提供了高效且简洁的解决方案——核心在于 集合展开 + 基于数字键的外连接。下面以典型输入为例,逐步说明实现逻辑:
✅ 数据准备与前提假设
确保 number_b 和 number_s 列为真正的 Python set 对象(而非字符串 " {1, 2, 3}")。若为字符串,需先转换:
import ast
# 若 number_b 是字符串(如 "{1, 2, 3}"),先安全解析为 set
df["number_b"] = df["number_b"].apply(lambda x: ast.literal_eval(x) if isinstance(x, str) and x.strip().startswith("{") else x)
df["number_s"] = df["number_s"].apply(lambda x: ast.literal_eval(x) if isinstance(x, str) and x.strip().startswith("{") else x)✅ 步骤一:分别展开两组数字集合
使用 .explode() 将每个 set 拆分为多行,每行对应一个数字元素:
df1 = (df[["Entity", "name_b", "number_b"]]
.explode("number_b")
.dropna(subset=["number_b"]) # 过滤 number_b 为空或 NaN 的行
.rename(columns={"number_b": "number"}))
df2 = (df[["name_s", "number_s"]]
.explode("number_s")
.dropna(subset=["number_s"])
.rename(columns={"number_s": "number"}))⚠️ 注意:.explode() 要求列中元素为可迭代对象(如 set, list, tuple)。若含 NaN 或标量值,需提前清理,否则会报错。
✅ 步骤二:基于数字键执行外连接(outer merge)
通过 number 列合并,保留所有数字组合(包括仅存在于 name_b 或仅存在于 name_s 的编号),从而完整覆盖匹配与未匹配情形:
result = pd.merge(df1, df2, on="number", how="outer", sort=False)
该操作天然支持“一对多”和“多对多”匹配:例如 Zyla 关联 {1,2,3},Zeela 关联 {1,2,3},则生成 9 行(3×3);而 GCP Zyla 关联 {4,7} 但 name_s 中无对应编号,故 name_s 列填充为 NaN(显示为 - 可后续用 fillna("-") 处理)。
✅ 完整可运行示例
import pandas as pd
# 构造原始数据(注意:number_b/number_s 为 set)
df = pd.DataFrame({
"Entity": [1, 1, 1, 1, 1],
"name_b": ["Zyla", "Zyla 620", "Xyla 620", "GCP Zyla", "Zy"],
"number_b": [{1, 2, 3}, {1}, {2}, {4, 7}, {103}],
"name_s": ["Zeela", "Zylo", "Xylaa", "Ann Zyl", None],
"number_s": [{1, 2, 3}, {1}, {2}, {103}, None]
})
# 展开 + 合并
df1 = df[["Entity", "name_b", "number_b"]].explode("number_b").dropna(subset=["number_b"]).rename(columns={"number_b": "number"})
df2 = df[["name_s", "number_s"]].explode("number_s").dropna(subset=["number_s"]).rename(columns={"number_s": "number"})
out = pd.merge(df1, df2, on="number", how="outer").sort_values(["Entity", "number"]).reset_index(drop=True)
# 可选:将 NaN 替换为 '-' 以匹配预期输出格式
out["name_s"] = out["name_s"].fillna("-")
print(out[["Entity", "number", "name_b", "name_s"]])? 输出解读与注意事项
- 结果中每行代表一个“数字级匹配单元”,即 name_b 与 name_s 共享该 number;
- how="outer" 确保不丢失任何编号(如 4 和 7 仍保留在结果中,name_s 为 -);
- 若需进一步筛选高置信匹配(如仅保留 name_b 与 name_s 均非空的行),可追加 .dropna(subset=["name_s"]);
- 性能提示:对超大数据集,建议先对 number 列做 astype("category") 或预过滤高频数字,避免笛卡尔爆炸。
掌握这一模式,即可灵活应对各类“标签-编号”双维度对齐问题,无需循环或复杂正则,真正实现向量化、可扩展的实体匹配流水线。










