Pandas内连接后如何保留无公共匹配值的列条目
问题说明
现有3张表:
ch_df:存储国家与酒店的映射关系hm_df:存储酒店与供应菜单的映射关系cm_df:存储国家允许出现的菜单规则
现有逻辑通过双列内连接实现「筛选出每个国家下酒店供应的、符合国家规则的菜单」,但会丢失所有无符合规则菜单的国家-酒店配对(比如意大利的Oberoi酒店),需要保留这类配对,对应菜单项展示为空列表,且不希望使用「加占位值再事后删除」的低效hack方案。
实现方案
核心思路是拆分「过滤合法菜单」和「保留全量配对」两个步骤,全程用pandas原生连接、聚合逻辑实现,无冗余操作:
import pandas as pd # 原始测试数据 ch = {'country':['India','India','India','USA','USA','Italy','Italy'],'hotel':['Taj','Oberoi','Hilton','Taj','Hilton','Oberoi','Marriott']} ch_df = pd.DataFrame.from_dict(ch) hm = {'hotel':['Taj','Taj','Taj','Oberoi','Oberoi','Marriott','Marriott','Marriott','Hilton','Hilton'],'menu':['ildi','dosa','soup','soup','ildi','soup','pasta','pizza','pizza','burger']} hm_df = pd.DataFrame.from_dict(hm) cm = {'country':['India','India','India','USA','USA','USA','Italy','Italy'],'menu':['ildi','dosa','soup','dosa','burger','pizza','pizza','pasta']} cm_df = pd.DataFrame.from_dict(cm) # 1. 关联得到所有国家-酒店对应的自有供应菜单 chm_df = pd.merge(ch_df, hm_df, on='hotel') # 2. 过滤出同时符合国家菜单规则的合法条目,用merge的indicator参数标记匹配状态 valid_menu_entries = pd.merge( chm_df, cm_df, on=['country', 'menu'], how='left', indicator=True ).query('_merge == "both"').drop(columns='_merge') # 3. 以原始全量国家-酒店配对为基准左连接,聚合得到菜单列表,无匹配的返回空列表 result = pd.merge( ch_df, valid_menu_entries, on=['country', 'hotel'], how='left' ).groupby(['country', 'hotel'], as_index=False).agg( **{'menu items': ('menu', lambda x: [] if x.isna().all() else list(x.dropna()))} )
运行后输出结果如下,所有原始国家-酒店配对都会保留:
country hotel menu items 0 India Taj [ildi, dosa, soup] 1 India Oberoi [ildi, soup] 2 India Hilton [] 3 USA Taj [dosa] 4 USA Hilton [burger] 5 Italy Oberoi [] 6 Italy Marriott [pasta, pizza]
逻辑说明
- 第二步用左连接+
indicator参数直接标记匹配状态,不需要额外新增临时列,过滤掉不满足国家菜单规则的菜单条目即可 - 第三步以最原始的
ch_df(全量国家-酒店配对)为左表做连接,从根源保证所有配对不会丢失 - 聚合时判断分组内菜单值是否全为空,空值分组返回空列表,非空分组返回去空后的菜单列表,完全符合需求,不需要额外的占位值增删操作
内容的提问来源于stack exchange,提问作者Quiescent
相关产品推荐
相关产品推荐

