You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas按多列最相似值匹配合并不同长度DataFrame的方法

不等长Pandas DataFrame 多维度模糊匹配合并方案

核心思路

先通过age、department两个字段做精确分块,把匹配范围缩小到同年龄同部门的记录池内,再做name字段的模糊匹配,从根源避免同名跨部门/跨年龄错配;匹配过程增加空结果判断和相似度阈值,解决直接调用get_close_matches触发的索引报错问题,同时自动过滤无匹配记录。

实现步骤

  • 导入依赖
import pandas as pd
from difflib import get_close_matches
import re
  • 定义name标准化规则
    针对name存在的标点、后缀(如III、Jr.)差异做统一清洗,提升模糊匹配准确率:
def name_standardize(s: str) -> str:
    # 移除标点
    s = re.sub(r'[.,]', '', s)
    # 移除姓名后缀
    s = re.sub(r'\s+(III|Jr|Sr|II)$', '', s, flags=re.IGNORECASE)
    return s.strip().lower()
  • 编写匹配函数
    设置相似度阈值,分块匹配,空结果直接跳过,避免索引报错:
def fuzzy_merge(df_left: pd.DataFrame, df_right: pd.DataFrame, sim_cutoff: float = 0.6) -> pd.DataFrame:
    match_result = []
    # 按精确匹配字段分块,缩小匹配范围
    for (age, dept), left_group in df_left.groupby(["age", "department"]):
        # 筛选右表中同年龄同部门的记录作为当前匹配池
        right_pool = df_right[(df_right["age"] == age) & (df_right["department"] == dept)].copy()
        if right_pool.empty:
            continue
        
        # 预生成匹配池的标准化姓名、原始姓名、其余字段映射
        pool_std_names = right_pool["name"].apply(name_standardize).tolist()
        pool_origin_names = right_pool["name"].tolist()
        pool_other_data = right_pool.drop(columns=["name", "age", "department"]).to_dict("records")

        # 遍历左表当前分块的每条记录做匹配
        for _, left_row in left_group.iterrows():
            left_std_name = name_standardize(left_row["name"])
            # 取相似度最高的1条结果,低于阈值直接返回空
            top_match = get_close_matches(left_std_name, pool_std_names, n=1, cutoff=sim_cutoff)
            if not top_match: # 无匹配直接跳过,解决IndexError问题
                continue
            match_pos = pool_std_names.index(top_match[0])
            # 拼接匹配结果
            row_data = left_row.to_dict()
            row_data["name_y"] = pool_origin_names[match_pos]
            row_data.update(pool_other_data[match_pos])
            match_result.append(row_data)
    
    return pd.DataFrame(match_result)

使用示例

构造测试数据验证效果:

# 测试用df1
df1 = pd.DataFrame({
    "name": ["DJ Griffin", "Harris Smith", "Li Hua", "Wang Ming"],
    "age": [28, 33, 25, 40],
    "department": ["Marketing", "Tech", "HR", "Finance"]
})

# 测试用df2,包含同名不同年龄记录、无匹配记录
df2 = pd.DataFrame({
    "name": ["D.J. Griffin III", "Harris Smith", "Harris Smith", "Miles Jones", "Li H.", "Wang M."],
    "age": [28, 33, 45, 30, 25, 40],
    "department": ["Marketing", "Tech", "Tech", "Admin", "HR", "Finance"],
    "salary": [18000, 25000, 32000, 12000, 15000, 28000]
})

# 执行合并
final_df = fuzzy_merge(df1, df2, sim_cutoff=0.6)

效果说明

  • 基础匹配场景:DJ Griffin会准确匹配到同年龄同部门的D.J. Griffin III,无匹配的Miles Jones会被自动过滤,结果中仅保留匹配成功的记录
  • 同名重复场景:分块匹配机制下,33岁的Harris Smith只会在同年龄的Tech组匹配池内查找,不会错配到45岁的同名记录
  • 报错解决:通过空结果判断逻辑,不会出现get_close_matches返回空列表时取[0]触发的IndexError: list index out of range问题

参数调整建议

  • sim_cutoff:相似度阈值,取值范围0-1,可根据实际数据的姓名差异程度调整,常规场景0.6-0.8即可,值越高匹配规则越严格
  • name_standardize:可根据实际数据的姓名格式差异新增清洗规则,比如移除中间名缩写、统一拼音拼写规范等,进一步提升匹配准确率

内容的提问来源于stack exchange,提问作者Giskard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 18:27:33