如何用Pandas找出两个DataFrame中属性匹配最多的姓名与用户名?
解决思路:对比两个DataFrame的属性匹配度
问题背景
有两个Pandas DataFrame:
第一个df_names结构如下:
name attributes 0 John [ABC, DEF, GHI, JKL, MNO, PQR, STU] 1 Mike [EUD, DBS, QMD, ABC, GHI] 2 Jane [JKL, EJD, MDE, MNO, DEF, ABC] 3 Kevin [FHE, EUD, GHI, MNO, ABC, AUE, HSG, PEO] 4 Stefanie [STU, EJD, DUE]
第二个df_usernames结构如下:
username attr 0 username_1 [DHD, EOA, AUE, CHE, ABC, PQR, QJF] 1 username_2 [ABC, EKR, ADT, GHI, JKL, EJD, MNO, MDE] 2 username_3 [DSB, AOD, DEF, MNO, DEF, ABC, TAE] 3 username_4 [DJH, EUD, GHI, MNO, ABC, FHE] 4 username_5 [CHQ, ELT, ABC, DEF, GHI]
需要找出name和username中共享属性数量最多的组合,比如示例里username_4和Kevin有5个匹配属性。
可行解决思路
方法1:集合交集+笛卡尔积全组合对比
先把属性列表转成集合(自动去重,重复属性不影响匹配计数),然后生成两个DataFrame的全组合,计算每组的交集长度:
import pandas as pd # 构造示例数据 df_names = pd.DataFrame({ 'name': ['John', 'Mike', 'Jane', 'Kevin', 'Stefanie'], 'attributes': [ ['ABC', 'DEF', 'GHI', 'JKL', 'MNO', 'PQR', 'STU'], ['EUD', 'DBS', 'QMD', 'ABC', 'GHI'], ['JKL', 'EJD', 'MDE', 'MNO', 'DEF', 'ABC'], ['FHE', 'EUD', 'GHI', 'MNO', 'ABC', 'AUE', 'HSG', 'PEO'], ['STU', 'EJD', 'DUE'] ] }) df_usernames = pd.DataFrame({ 'username': ['username_1', 'username_2', 'username_3', 'username_4', 'username_5'], 'attr': [ ['DHD', 'EOA', 'AUE', 'CHE', 'ABC', 'PQR', 'QJF'], ['ABC', 'EKR', 'ADT', 'GHI', 'JKL', 'EJD', 'MNO', 'MDE'], ['DSB', 'AOD', 'DEF', 'MNO', 'DEF', 'ABC', 'TAE'], ['DJH', 'EUD', 'GHI', 'MNO', 'ABC', 'FHE'], ['CHQ', 'ELT', 'ABC', 'DEF', 'GHI'] ] }) # 将属性列转为集合,方便计算交集 df_names['attr_set'] = df_names['attributes'].apply(set) df_usernames['attr_set'] = df_usernames['attr'].apply(set) # 生成全组合(笛卡尔积) cross_df = df_names.assign(key=1).merge(df_usernames.assign(key=1), on='key').drop('key', axis=1) # 计算每组共享属性的数量 cross_df['shared_attr_count'] = cross_df.apply( lambda row: len(row['attr_set_x'] & row['attr_set_y']), axis=1 ) # 筛选出共享数量最多的组合 max_shared = cross_df['shared_attr_count'].max() result = cross_df[cross_df['shared_attr_count'] == max_shared][['name', 'username', 'shared_attr_count']] print(result)
运行后会直接输出所有共享属性数量最多的name和username组合。
方法2:内存优化版(大数据量适用)
如果两个DataFrame行数较多,笛卡尔积会占用大量内存,可以改用循环遍历组合,同时记录最大值:
import itertools max_count = 0 best_pairs = [] # 遍历所有name和username的组合 for name_row, user_row in itertools.product(df_names.itertuples(), df_usernames.itertuples()): name_attr = set(name_row.attributes) user_attr = set(user_row.attr) shared_num = len(name_attr & user_attr) if shared_num > max_count: max_count = shared_num best_pairs = [(name_row.name, user_row.username, shared_num)] elif shared_num == max_count: best_pairs.append((name_row.name, user_row.username, shared_num)) # 输出结果 print(f"最大共享属性数:{max_count}") print("最佳匹配组合:") for pair in best_pairs: print(f"{pair[0]} 和 {pair[1]},共享{pair[2]}个属性")
处理字符串形式的列表
如果你的DataFrame中attributes或attr列是字符串格式的列表(比如"[ABC, DEF, GHI]"而非真正的Python列表),需要先解析成列表:
import ast # 将字符串形式的列表转为Python列表 df_names['attributes'] = df_names['attributes'].apply(ast.literal_eval) df_usernames['attr'] = df_usernames['attr'].apply(ast.literal_eval)
用ast.literal_eval可以安全地将字符串解析为对应的Python对象,之后再按上述方法处理即可。
内容的提问来源于stack exchange,提问作者Jelz
相关产品推荐
相关产品推荐

