如何在Python中高效对比大数据集单列的每行与其他行?
高效实现DataFrame中字符串两两对比并生成相似度得分
问题背景
现有包含unique_id和id_string两列的DataFrame:
| unique_id | id_string |
|---|---|
| 123 | abc |
| 456 | pqr |
| 789 | xyz |
| 000 | lmn |
需要将每个unique_id对应的id_string与其他所有id_string对比,生成包含相似度得分的结果,格式如下:
| unique_id | id_string | duplicate_id | duplicate_string | score |
|---|---|---|---|---|
| 123 | abc | 456 | pqr | 91 |
| 123 | abc | 789 | xyz | 92 |
| 123 | abc | 000 | lmn | 93 |
原代码采用双重循环+DataFrame.append的方式,在50万行数据集下运行效率极低,需基于itertools.combinations进行优化。
原低效代码
out_put_df = pd.DataFrame() for i in input_df.index: unique_id = input_df.at[i, 'unique_id'] id_string = input_df.at[i, 'id_string'] j = i+1 for j in range(len(input_df.index)-j): dupicate_id = input_df.at[j, 'unique_id'] duplicate_string = input_df.at[j, 'id_string'] comparition_score = fuzz.token_set_ratio(id_string, duplicate_string) out_put_df = out_put_df.append(pd.DataFrame({'unique_id': unique_id,'id_string': id_string,'dupicate_id': dupicate_id,'duplicate_string': duplicate_string,'comparition_score': comparition_score}, index=[0]), ignore_index=True)
优化方案
1. 基于itertools.combinations的基础优化
itertools.combinations可直接生成所有不重复的两两索引对(避免重复计算A-B和B-A),配合列表存储结果(替代循环中append),大幅提升效率。
import pandas as pd from itertools import combinations from fuzzywuzzy import fuzz # 生成所有i<j的索引对,避免重复对比 index_pairs = combinations(input_df.index, 2) # 用列表存储结果,避免循环中频繁创建DataFrame results = [] for idx1, idx2 in index_pairs: row1 = input_df.loc[idx1] row2 = input_df.loc[idx2] # 计算相似度得分 score = fuzz.token_set_ratio(row1['id_string'], row2['id_string']) # 添加双向记录(匹配输出示例的格式) results.append({ 'unique_id': row1['unique_id'], 'id_string': row1['id_string'], 'duplicate_id': row2['unique_id'], 'duplicate_string': row2['id_string'], 'score': score }) results.append({ 'unique_id': row2['unique_id'], 'id_string': row2['id_string'], 'duplicate_id': row1['unique_id'], 'duplicate_string': row1['id_string'], 'score': score }) # 一次性转换为DataFrame output_df = pd.DataFrame(results)
2. 超大数据集(50万行)的进阶优化
50万行数据的两两组合数约为1.25×10¹¹,直接计算完全不现实,需结合分块处理+高速相似度库调整方案:
- 用
RapidFuzz替代FuzzyWuzzy:前者是后者的C语言重写版本,速度提升数十倍; - 分块处理:将数据集拆分为小块,逐块与其他部分对比,降低内存占用;
- 可选:仅保留相似度高于阈值的结果,减少输出数据量。
import pandas as pd from rapidfuzz import fuzz, process # 设置分块大小,根据内存调整 chunk_size = 1000 results = [] for i in range(0, len(input_df), chunk_size): chunk = input_df.iloc[i:i+chunk_size] for idx1, row1 in chunk.iterrows(): # 排除当前行,获取其他所有行 other_rows = input_df.drop(idx1) # 批量计算当前字符串与其他所有字符串的相似度 scores = process.extract(row1['id_string'], other_rows['id_string'], scorer=fuzz.token_set_ratio, limit=None) # 整理结果 for match_str, score, idx2 in scores: row2 = other_rows.loc[idx2] results.append({ 'unique_id': row1['unique_id'], 'id_string': row1['id_string'], 'duplicate_id': row2['unique_id'], 'duplicate_string': row2['id_string'], 'score': score }) output_df = pd.DataFrame(results)
核心优化点
- 禁止循环中使用
DataFrame.append:每次append都会生成新的DataFrame,时间复杂度极高,改用列表存储字典后一次性转换; - 用
combinations减少循环层级:直接生成所有需要的对比对,代码更简洁,避免嵌套循环的冗余; - 替换相似度计算库:
RapidFuzz在保持API兼容的前提下,性能远超FuzzyWuzzy; - 分块处理:针对超大数据集,分块可有效控制内存使用,避免内存溢出。
内容的提问来源于stack exchange,提问作者anmol khandelwal
相关产品推荐
相关产品推荐

