如何对两个pandas DataFrame应用fuzzy ratio实现模糊字符串匹配
原代码问题说明
你现有代码存在两个明显问题:
- 自定义函数
get_ratio里两次取值都用了row['address'],且变量名和外部的sample1、sample2重名,无法正确取到两个DataFrame的地址字段 - 没有将两个sample的待比对字段合并成同一行可访问的结构,apply无法同时遍历两个独立的DataFrame
场景1:按索引位置一一对应比对两个样本的同序行
如果你的两个sample行数相同,要对比第1行和第1行、第2行和第2行的地址相似度,用这个方案:
import pandas as pd from fuzzywuzzy import fuzz # 你的采样逻辑保留 sample1 = pd.DataFrame(data1.sample(n=200, random_state=42)) sample2 = pd.DataFrame(data2.sample(n=200, random_state=13)) # 构造比对用的合并表,保留两个样本的地址字段 compare_df = pd.DataFrame({ 'addr_sample1': sample1['address'], 'addr_sample2': sample2['address'] }) # 重写相似度计算函数 def get_ratio(row): return fuzz.token_set_ratio(row['addr_sample1'], row['addr_sample2']) # 计算相似度并筛选结果 compare_df['match_score'] = compare_df.apply(get_ratio, axis=1) match = compare_df[compare_df['match_score'] >= 78] no_matched = compare_df[compare_df['match_score'] <= 77]
场景2:两个样本所有行两两交叉比对(找所有符合阈值的地址组合)
如果要遍历sample1的每一行,和sample2的所有行逐一比对相似度,用笛卡尔积合并的方案:
import pandas as pd from fuzzywuzzy import fuzz sample1 = pd.DataFrame(data1.sample(n=200, random_state=42)) sample2 = pd.DataFrame(data2.sample(n=200, random_state=13)) # 加临时键构造笛卡尔积,得到所有行的两两组合 sample1['tmp_join_key'] = 0 sample2['tmp_join_key'] = 0 cross_compare_df = sample1.merge( sample2, on='tmp_join_key', suffixes=('_s1', '_s2') # 重名字段自动加后缀区分 ).drop('tmp_join_key', axis=1) # 相似度计算函数 def get_ratio(row): return fuzz.token_set_ratio(row['address_s1'], row['address_s2']) # 计算得分并筛选 cross_compare_df['match_score'] = cross_compare_df.apply(get_ratio, axis=1) match = cross_compare_df[cross_compare_df['match_score'] >= 78] no_matched = cross_compare_df[cross_compare_df['match_score'] <= 77]
性能优化建议
如果后续数据量变大,两两比对性能较差,可以用fuzzywuzzy的批量匹配接口优化,避免全量笛卡尔积计算:
from fuzzywuzzy import process sample2_addr_list = sample2['address'].tolist() # 对sample1的每个地址,直接找sample2中相似度最高的匹配 def match_top1(addr): top_match = process.extractOne(addr, sample2_addr_list, scorer=fuzz.token_set_ratio) return pd.Series([top_match[0], top_match[1]]) if top_match else pd.Series([None, 0]) sample1[['matched_addr_sample2', 'match_score']] = sample1['address'].apply(match_top1) match = sample1[sample1['match_score'] >=78] no_matched = sample1[sample1['match_score'] <=77]
内容的提问来源于stack exchange,提问作者a1234
相关产品推荐
相关产品推荐

