如何比较两个Pandas数据框中的列表列,提取共同元素?
问题
需要对比df2中set_1和set_2列的列表,与df1中ins列的列表,找出所有共同元素。目前仅能实现单行单列的比较,不知道如何对两个数据框的所有行进行比较以得到目标结果。
现有单行比较代码:
import pandas as pd d1 = {'chr': [1, 1], 'start': [64, 1000], 'end': [150, 2000], 'family': ['a', 'b'], 'ins': [['P1-12', 'P1-22', 'P1-25', 'P1-28', 'P1-90'], ['P1-6', 'P1-89', 'P1-92', 'P1-93']]} df1 = pd.DataFrame.from_dict(data=d1) d2 = {'set_1': [['P1-12', 'P1-25', 'P1-28'], ['P1-6', 'P1-89', 'P1-93']], 'set_2': [['P1-89', 'P1-92', 'P1-93'], ['P1-25', 'P1-28', 'P1-90']]} df2 = pd.DataFrame.from_dict(data=d2) matches = [x for x in df2.iloc[0, 0] if x in df1.iloc[0, 4]]
输入数据(原始数据df1约13k行,df2约90行):df1:
chr start end family ins 0 1 64 150 a [P1-12, P1-22, P1-25, P1-28, P1-90] 1 1 1000 2000 b [P1-6, P1-89, P1-92, P1-93]
df2:
set_1 set_2 0 [P1-12, P1-25, P1-28] [P1-89, P1-92, P1-93] 1 [P1-6, P1-89, P1-93] [P1-25, P1-28, P1-90]
期望输出:
chr start end family df2_index ins_set1 ins_set2 0 1 64 150 a 0 [P1-12, P1-25, P1-28] [] 1 1 64 150 a 1 [] [P1-25, P1-28, P1-90] 2 1 1000 2000 b 0 [] [P1-89, P1-92, P1-93] 3 1 1000 2000 b 1 [P1-6, P1-89, P1-93] []
解决方案
要实现所有行的交叉比较,核心是先构建df1和df2的笛卡尔积(即每一行df1对应所有df2的行),再逐行计算交集。具体步骤如下:
- 给
df2添加索引列df2_index,方便后续关联; - 用
merge生成两个数据框的笛卡尔积(通过指定一个共同的临时列); - 定义函数计算两个列表的交集;
- 分别计算
ins与set_1、ins与set_2的交集,生成ins_set1和ins_set2列; - 整理输出格式,去掉临时列。
完整代码:
import pandas as pd # 初始化数据框 d1 = {'chr': [1, 1], 'start': [64, 1000], 'end': [150, 2000], 'family': ['a', 'b'], 'ins': [['P1-12', 'P1-22', 'P1-25', 'P1-28', 'P1-90'], ['P1-6', 'P1-89', 'P1-92', 'P1-93']]} df1 = pd.DataFrame.from_dict(data=d1) d2 = {'set_1': [['P1-12', 'P1-25', 'P1-28'], ['P1-6', 'P1-89', 'P1-93']], 'set_2': [['P1-89', 'P1-92', 'P1-93'], ['P1-25', 'P1-28', 'P1-90']]} df2 = pd.DataFrame.from_dict(data=d2).reset_index(names='df2_index') # 定义交集计算函数 def get_intersection(list1, list2): return list(set(list1) & set(list2)) # 生成笛卡尔积 df1['key'] = 1 df2['key'] = 1 merged = pd.merge(df1, df2, on='key').drop('key', axis=1) # 计算交集列 merged['ins_set1'] = merged.apply(lambda row: get_intersection(row['ins'], row['set_1']), axis=1) merged['ins_set2'] = merged.apply(lambda row: get_intersection(row['ins'], row['set_2']), axis=1) # 整理输出格式 result = merged[['chr', 'start', 'end', 'family', 'df2_index', 'ins_set1', 'ins_set2']] print(result)
运行后输出与期望一致。考虑到df1有13k行、df2有90行,笛卡尔积会生成117万行数据,apply逐行计算可能稍慢,若要优化可以把列表转为集合提前存储,减少重复计算:
# 提前把ins列转为集合 df1['ins_set'] = df1['ins'].apply(set) # 修改交集函数 def get_intersection_fast(set1, list2): return list(set1 & set(list2)) # 计算交集时用提前转好的集合 merged['ins_set1'] = merged.apply(lambda row: get_intersection_fast(row['ins_set'], row['set_1']), axis=1) merged['ins_set2'] = merged.apply(lambda row: get_intersection_fast(row['ins_set'], row['set_2']), axis=1)
这样能减少每次计算时重复转集合的开销,提升处理速度。
内容的提问来源于stack exchange,提问作者emor
相关产品推荐
相关产品推荐

