如何判断dataframe中含部分重叠信息的两行是否为相同/相似记录
同地址脏姓名数据匹配解决方案
前置预处理步骤
首先对全量数据做统一归一化处理,降低后续匹配误判率:
- 新增
norm_last字段:对Last Name做归一化,统一转小写、去除特殊符号/常用称谓后缀(Jr./Sr./罗马数字后缀等)、去除前后空格 - 拆分
First Name字段,新增两个衍生字段:main_first:First Name按空格拆分后的第一个片段,做与上述相同的归一化处理middle_name_list:First Name拆分后除第一个片段外的剩余片段列表,无中间名则为空列表
两类匹配问题判定规则
第一类:含中间名的行匹配
同一地址分组下的两条记录,同时满足以下条件即可判定为同一人:
- 两条记录的
norm_last完全相等 - 其中一条记录的
First Name长度更短,且其完整main_first与另一条记录的main_first完全相等
匹配示例:
First Name=James Agnew, Last Name=Bond和First Name=James, Last Name=Bond符合该类匹配规则,合并时可保留更长的全量姓名字段作为标准值。
第二类:缩略名匹配
同一地址分组下的两条记录,同时满足以下基础条件后,满足任意一个模糊匹配规则即可判定为同一人:
- 基础条件:两条记录的
norm_last完全相等 - 模糊匹配规则(二选一即可):
- 前缀匹配:较短的
main_first所有字符与较长的main_first的前N位完全一致,N取较短名的长度,且要求短名长度≥3,避免单/双字母的误匹配 - 常用缩略映射匹配:提前预制常用英文名-缩略/昵称映射表,两个
main_first属于同一标准名的不同表达 - (可选补充规则)两个
main_first的莱文斯坦编辑距离≤2,覆盖少量拼写错误场景
- 前缀匹配:较短的
注意:如果业务对匹配准确率要求极高,可关闭编辑距离补充规则,仅保留前缀匹配+预制映射表规则,可大幅降低误判概率
Pandas 场景实现参考
因为已按地址分组且每组仅2-3条记录,直接做组内两两比对即可,计算成本极低:
import pandas as pd from Levenshtein import distance # 姓名归一化函数 def normalize_name(s: str) -> str: s = s.lower().strip() # 清理特殊符号和常见后缀 for remove_str in ['.', ',', 'jr', 'sr', 'iii', 'ii', '-']: s = s.replace(remove_str, '') return s.strip() # 预处理生成衍生字段 df['norm_last'] = df['Last Name'].apply(normalize_name) df['first_split'] = df['First Name'].apply(lambda x: [normalize_name(p) for p in x.split() if p.strip()]) df['main_first'] = df['first_split'].str[0] # 按地址分组后匹配,匹配成功的记录标记相同的person_id df['person_id'] = None current_id = 1 # address为你的地址分组字段名,可根据实际表结构修改 for addr, group in df.groupby('address'): records = group.to_dict('records') matched = set() for i in range(len(records)): if i in matched: continue # 标记当前未匹配记录的id df.loc[records[i]['index'], 'person_id'] = current_id matched.add(i) # 和组内其他记录比对 for j in range(i+1, len(records)): if j in matched: continue r1, r2 = records[i], records[j] # 姓氏不一致直接跳过 if r1['norm_last'] != r2['norm_last']: continue # 第一类匹配判定 min_len = min(len(r1['main_first']), len(r2['main_first'])) if r1['main_first'][:min_len] == r2['main_first'][:min_len] and (len(r1['first_split'])>1 or len(r2['first_split'])>1): df.loc[r2['index'], 'person_id'] = current_id matched.add(j) continue # 第二类匹配判定 if (r1['main_first'] == r2['main_first'][:min_len] and min_len >=3) or distance(r1['main_first'], r2['main_first']) <=2: df.loc[r2['index'], 'person_id'] = current_id matched.add(j) current_id +=1
内容的提问来源于stack exchange,提问作者Isaac A
相关产品推荐
相关产品推荐

