如何基于首两位标识在DataFrame间匹配含最多共同token的行?
高效匹配同组DataFrame中共同Token最多的行
问题场景
给定两个大型DataFrame(df1、df2),要求对df2的每一行,只在df1中first_two(首两位标识)相同的行里,选出和该行拥有最多共同token的行作为匹配结果;如果对应first_two组在df1中没有行,或者没有共同token,匹配结果设为None。
示例数据
df1
| name | first_two |
|---|---|
| common work | co |
| summer hot | su |
| apple | ap |
| colorful fall | co |
| support it | su |
| could comp | co |
df2
| name | first_two |
|---|---|
| condition work it | co |
| common mistakes | co |
| could comp work | co |
| summer | su |
| appears | ap |
预期输出
| name | first_two | match |
|---|---|---|
| condition work it | co | common work |
| common mistakes | co | common work |
| could comp work | co | could comp |
| summer | su | summer hot |
| appears | ap | None |
你已经完成的代码:
df3=(df1.groupby(['first_two']) .agg({'name': lambda x: ",".join(x)}) .reset_index()) merge_=df3.merge(df2, on='first_two',how='inner')
核心待解决问题:如何在merge_的name_x列中,为每个name_y找到含最多共同token的元素?
解决方案
思路
无需把同组name合并成逗号分隔的字符串,先将文本转换为token集合,再按first_two分组计算交集数量,最终选出对应最大值的行即可。
代码实现
1. 预处理:将文本转为Token集合
为两个DataFrame的name列生成对应的token集合,方便快速计算交集:
import pandas as pd # 给df1添加token集合列 df1['tokens'] = df1['name'].str.split().apply(set) # 给df2添加token集合列 df2['tokens'] = df2['name'].str.split().apply(set)
2. 基础版匹配逻辑
直接遍历df2每一行,在同组df1中筛选共同token最多的行:
def find_best_match(df2_row, df1_group): # 计算当前df2行与同组每个df1行的共同token数量 common_counts = df1_group['tokens'].apply(lambda x: len(x & df2_row['tokens'])) max_count = common_counts.max() if max_count == 0: return None # 取第一个达到最大共同token数的df1行name(存在并列时取第一个,可按需调整) return df1_group.loc[common_counts.idxmax(), 'name'] # 为df2添加匹配结果列 df2['match'] = df2.apply( lambda row: find_best_match(row, df1[df1['first_two'] == row['first_two']]), axis=1 )
3. 大型数据集优化版
如果数据量极大,上述apply效率不足,可将df1按first_two分组存入字典,减少重复查询开销:
# 将df1按first_two分组,转换为字典:key为first_two,value为[(tokens集合, name), ...] grouped_df1 = df1.groupby('first_two')[['tokens', 'name']].apply(list).to_dict() def fast_match(row): group_data = grouped_df1.get(row['first_two'], []) if not group_data: return None max_common = -1 best_name = None target_tokens = row['tokens'] # 遍历同组所有df1行,筛选共同token最多的条目 for tokens, name in group_data: current_common = len(tokens & target_tokens) if current_common > max_common: max_common = current_common best_name = name return best_name if max_common > 0 else None df2['match'] = df2.apply(fast_match, axis=1)
4. 格式调整(匹配预期输出)
将匹配结果转为代码格式展示:
df2['match'] = df2['match'].apply(lambda x: f"`{x}`" if x is not None else x)
补充说明
- 若存在多个df1行与当前df2行的共同token数并列最多,上述代码会返回第一个出现的行;如需返回所有匹配行,可修改为收集所有满足
common_counts == max_count的name。 - 针对超大型数据集,建议采用分块处理或Dask框架,避免内存溢出。
内容的提问来源于stack exchange,提问作者user15649753
相关产品推荐
相关产品推荐

