如何动态查询Pandas DataFrame,筛选同时存在于多指定动物园的动物
问题:找出同时出现在指定多个动物园的动物
我有如下结构的Pandas DataFrame:
| 动物 | 动物园 |
|---|---|
| Lion | Berlin |
| Lion | Munich |
| Lion | Paris |
| Monkey | Berlin |
| Monkey | Munich |
| Monkey | Rotterdam |
| Bat | Berlin |
| Goose | Rotterdam |
| Tiger | Paris |
| Tiger | Munich |
需求是动态构建查询,返回同时出现在指定多个动物园中的唯一动物值,比如找出所有同时在Berlin和Munich动物园的动物,预期结果为:
result = ['Lion', 'Monkey']
我尝试了以下代码:
# 初始化列表 data = [ ['Lion', 'Berlin'], ['Lion', 'Munich'], ['Lion', 'Paris'], ['Monkey', 'Berlin'], ['Monkey', 'Munich'], ['Monkey', 'Rotterdam'], ['Bat', 'Berlin'], ['Goose', 'Rotterdam'], ['Tiger', 'Paris'], ['Tiger', 'Munich'] ] # 创建Pandas DataFrame df = pd.DataFrame(data, columns=['Animal', 'Zoo']) # 筛选DataFrame df_filtered = df.query(" (`Zoo` == 'Berlin' | `Zoo` == 'Munich')") # 将动物转为列表 result = df_filtered['Animal'].unique().tolist() # 打印结果 print(result)
但该代码返回的是出现在Berlin或Munich动物园的动物,结果为:
['Lion', 'Monkey', 'Bat', 'Tiger']
将逻辑或(|)改为逻辑与(&)后,查询返回空DataFrame:
df.query(" (`Zoo` == 'Berlin' & `Zoo` == 'Munich')")
现寻求正确实现该需求的方法。
解决方案
方法1:分组统计法(推荐,灵活支持任意数量目标动物园)
核心思路是按动物分组,检查每组的动物园集合是否包含所有目标动物园:
import pandas as pd data = [ ['Lion', 'Berlin'], ['Lion', 'Munich'], ['Lion', 'Paris'], ['Monkey', 'Berlin'], ['Monkey', 'Munich'], ['Monkey', 'Rotterdam'], ['Bat', 'Berlin'], ['Goose', 'Rotterdam'], ['Tiger', 'Paris'], ['Tiger', 'Munich'] ] df = pd.DataFrame(data, columns=['Animal', 'Zoo']) # 指定目标动物园列表 target_zoos = ['Berlin', 'Munich'] # 分组过滤:保留包含所有目标动物园的动物 result = df.groupby('Animal')['Zoo'].filter( lambda x: set(target_zoos).issubset(set(x)) ).unique().tolist() print(result) # 输出:['Lion', 'Monkey']
方法2:集合交集法
分别获取每个目标动物园的动物集合,再求所有集合的交集:
target_zoos = ['Berlin', 'Munich'] # 生成每个目标动物园对应的动物集合 zoo_animal_sets = [set(df[df['Zoo'] == zoo]['Animal']) for zoo in target_zoos] # 计算所有集合的交集 result = list(set.intersection(*zoo_animal_sets)) print(result) # 输出:['Lion', 'Monkey']
方法3:基于query的改进方案
如果偏好使用query,可以先筛选目标动物园的记录,再通过分组计数过滤:
target_zoos = ['Berlin', 'Munich'] # 先筛选出目标动物园的记录 filtered = df.query("Zoo in @target_zoos") # 保留分组后记录数等于目标动物园数量的动物(即每个目标动物园都有该动物) result = filtered.groupby('Animal').filter( lambda x: len(x) == len(target_zoos) )['Animal'].unique().tolist() print(result) # 输出:['Lion', 'Monkey']
错误原因说明
- 使用
|(逻辑或)时,只要动物出现在任意一个目标动物园就会被选中,因此包含了仅在Berlin的Bat和仅在Munich的Tiger。 - 使用
&(逻辑与)时,要求单条记录同时满足Zoo等于Berlin和Munich,而单条记录只有一个Zoo值,因此不可能成立,返回空DataFrame。
内容的提问来源于stack exchange,提问作者Maxhlnug2021
相关产品推荐
相关产品推荐

