如何基于Key匹配且Value在列表中实现Pandas左连接?
Pandas左连接满足双条件的实现方法
问题描述
现有两个DataFrame:
import pandas as pd df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': [1, 2, 3, 4]}) df2 = pd.DataFrame({'key': ['B', 'D', 'D', 'F'], 'list_values': [[2, 4, 6], [4, 8], [1, 3, 5], [7, 9]]})
需要实现左连接,同时满足两个条件:
df1['key']与df2['key']相等df1['value']存在于df2['list_values']的列表中
期望输出结果:
key value list_values 0 A 1 NaN 1 B 2 [2, 4, 6] 2 C 3 NaN 3 D 4 [4, 8]
目前已经通过key完成基础左合并:
merged_df = df1.merge(df2, left_on='key', right_on='key', how='left')
需要补充第二个条件以得到目标结果。
解决方案
有几种实用的方法可以实现这个需求,下面给你详细说明:
方法一:合并后过滤+分组处理
先执行基础左合并,再过滤符合条件的行,最后处理缺失情况:
- 执行基础合并:
merged_df = df1.merge(df2, on='key', how='left')
- 添加标记列,判断当前
value是否在对应的list_values中:
merged_df['is_match'] = merged_df.apply( lambda row: row['value'] in row['list_values'] if pd.notna(row['list_values']) else False, axis=1 )
- 按
key和value分组,每组保留匹配成功的行;无匹配则将list_values设为NaN:
result = merged_df.groupby(['key', 'value']).apply( lambda group: group[group['is_match']].head(1) if group['is_match'].any() else group.assign(list_values=pd.NA).head(1) ).reset_index(drop=True)
- 删除多余的标记列:
result = result.drop('is_match', axis=1)
方法二:先预处理df2再合并(效率更高)
先将df2的列表拆分为单行元素,实现精准匹配后再关联回原列表:
- 拆分df2的
list_values,同时保留原列表数据:
df2_expanded = df2.explode('list_values').rename(columns={'list_values': 'value'}) df2_expanded['value'] = df2_expanded['value'].astype(int)
- 通过
key和value合并df1与展开后的df2,再关联回原列表:
temp = df1.merge(df2_expanded, on=['key', 'value'], how='left') temp = temp.merge(df2, on='key', suffixes=('', '_original'))
- 筛选匹配的原列表,其余设为
NaN后去重:
temp['list_values'] = temp.apply( lambda row: row['list_values_original'] if row['value'] in row['list_values_original'] else pd.NA, axis=1 ) result = temp[['key', 'value', 'list_values']].drop_duplicates(subset=['key', 'value'])
方法三:简洁版——过滤+补全缺失行
先完成合并,过滤出符合条件的行,再补全df1中未匹配的行:
merged_df = df1.merge(df2, on='key', how='left') # 筛选满足条件的行 matched_rows = merged_df[merged_df.apply(lambda x: x['value'] in x['list_values'], axis=1)] # 补全未匹配的行并设置list_values为NaN unmatched_rows = df1[~df1.index.isin(matched_rows.index)].assign(list_values=pd.NA) # 合并并按原顺序排序 result = pd.concat([matched_rows, unmatched_rows]).sort_index().reset_index(drop=True)
内容的提问来源于stack exchange,提问作者Galat
相关产品推荐
相关产品推荐

