如何用Fuzzywuzzy高效对比不等长字符串列表并生成多相似度指标
高效字符串相似度匹配优化方案
核心问题分析
原方案的效率瓶颈主要有两点:一是双重循环遍历所有组合(1.5亿次计算)带来的时间开销;二是循环内反复拼接DataFrame(pd.concat每次都会创建新对象,内存和时间成本极高);另外Fuzzywuzzy本身是纯Python实现,性能存在上限。
以下是针对性的优化方案:
1. 替换工具库:用RapidFuzz替代Fuzzywuzzy
RapidFuzz是Fuzzywuzzy的高性能替代,底层基于C++实现,API与Fuzzywuzzy完全兼容,但计算速度能提升几十到上百倍,是处理大规模字符串匹配的首选。
2. 避免逐行循环与低效拼接
先生成两个数据集的笛卡尔积,再批量计算相似度指标,最后过滤符合阈值的结果——彻底摆脱双重循环和逐次拼接的低效操作。
3. 可选预过滤:减少无效计算量
对字符串做预处理(转小写、去除特殊字符),或通过n-gram/Jaccard相似度快速过滤明显不相似的组合,进一步减少后续需要计算的样本量。
具体实现代码
步骤1:安装依赖
pip install rapidfuzz pandas
步骤2:基础高效实现
import pandas as pd from rapidfuzz import fuzz # 示例数据 forbes = pd.DataFrame( { "company_name": [ "Deloitte", "PriceWaterhouseCoopers", "KPMG", "Ernst & Young", "intentionall typo company XYZ", ], "revenue": [100, 200, 300, 250, 400], } ) sf = pd.DataFrame( {"salesforce_name": ["Deloite", "PriceWaterhouseCooper"], "CEO": ["John", "Jane"]} ) # 生成笛卡尔积:一次性合并所有可能的组合(替代双重循环) cross_df = pd.merge(forbes.assign(key=1), sf.assign(key=1), on="key").drop("key", axis=1) # 批量计算相似度指标 cross_df["r"] = cross_df.apply(lambda x: fuzz.ratio(x["company_name"], x["salesforce_name"]), axis=1) cross_df["pr"] = cross_df.apply(lambda x: fuzz.partial_ratio(x["company_name"], x["salesforce_name"]), axis=1) cross_df["tsr"] = cross_df.apply(lambda x: fuzz.token_sort_ratio(x["company_name"], x["salesforce_name"]), axis=1) cross_df["tser"] = cross_df.apply(lambda x: fuzz.token_set_ratio(x["company_name"], x["salesforce_name"]), axis=1) # 过滤符合阈值的结果 filtered_scores = cross_df[(cross_df["r"] > 80) | (cross_df["pr"] > 80) | (cross_df["tsr"] > 80) | (cross_df["tser"] > 80)] # 调整列名匹配需求 final_scores = filtered_scores.rename(columns={ "company_name": "forbes_company", "salesforce_name": "salesforce_company" })[["forbes_company", "salesforce_company", "r", "pr", "tsr", "tser"]] print(final_scores)
进阶优化:使用矢量化API(更快)
RapidFuzz提供了矢量化计算接口,比apply效率更高,适合处理5万+3万的大规模数据:
from rapidfuzz import vectorized # 提取字符串数组 forbes_names = cross_df["company_name"].values sf_names = cross_df["salesforce_name"].values # 矢量化批量计算所有指标 cross_df["r"] = vectorized.ratio(forbes_names, sf_names) cross_df["pr"] = vectorized.partial_ratio(forbes_names, sf_names) cross_df["tsr"] = vectorized.token_sort_ratio(forbes_names, sf_names) cross_df["tser"] = vectorized.token_set_ratio(forbes_names, sf_names) # 后续过滤步骤同上
预过滤示例(可选)
通过2-gram的Jaccard相似度先过滤掉明显不相似的组合,减少后续模糊计算的量:
from sklearn.feature_extraction.text import CountVectorizer from sklearn.metrics.pairwise import jaccard_score def char_ngram_jaccard(s1, s2, n=2): vec = CountVectorizer(ngram_range=(n,n), analyzer='char').fit([s1, s2]) vec1 = vec.transform([s1]).toarray()[0] vec2 = vec.transform([s2]).toarray()[0] return jaccard_score(vec1, vec2) # 先过滤Jaccard相似度>0.5的组合 cross_df = cross_df[cross_df.apply(lambda x: char_ngram_jaccard(x["company_name"], x["salesforce_name"]) > 0.5, axis=1)] # 再执行后续模糊相似度计算
性能对比
- 原方案:双重循环+Fuzzywuzzy+逐次concat,1.5亿次计算预计耗时数小时
- 优化方案:RapidFuzz矢量化+笛卡尔积批量计算,预计耗时几分钟(硬件不同略有差异,速度提升可达50-100倍)
内容的提问来源于stack exchange,提问作者user2205916
相关产品推荐
相关产品推荐

