基于分组内子集过滤DataFrame的高效实现方法问询
问题场景与需求
示例DataFrame:
import pandas as pd df_example = pd.DataFrame({'name': ['a', 'a', 'a', 'b', 'b', 'b'], 'class': [1, 2, 2, 3, 2, 2], 'price': [3, 4, 2, 1, 6, 5]})
需求:按name分组,筛选出每个分组中price**大于该分组内class==2子集的最小price**的行。
已尝试的操作及问题
- 先排序生成临时表,再用
groupby.apply尝试过滤,但未得到正确结果:
df_example.sort_values(['name', 'price'], inplace=True) df_tem = df_example[df_example['class'] == 2].groupby('name').first() df_example.groupby('name').apply(lambda key, val: val['price'] > df_tem.loc[key]['price']).reset_index()
- 尝试新增
smallest列再过滤,但生成的列存在大量NaN值,无法正常筛选:
df_example['smallest '] = df_example[df_example['class'] == 2].groupby('name')['price'].transform('first')
预期结果
每个name分组内class=2的最小price为:
class price name a 2 2 b 2 5
最终需筛选出分组a中price>2、分组b中price>5的行,输出结果:
pd.DataFrame({'name': ['a', 'a', 'b'], 'class': [1, 2, 2], 'price': [3, 4, 6]})
解决方案
方法一:用transform直接计算分组内目标最小值(高效推荐)
先通过groupby.transform将每个分组中class=2的最小price广播到全组,再进行筛选:
# 计算每个name分组里class=2的最小price,广播到所有行 df_example['min_class2_price'] = df_example.groupby('name')['price'].transform( lambda x: x[df_example.loc[x.index, 'class'] == 2].min() ) # 筛选符合条件的行,删除临时列 result = df_example[df_example['price'] > df_example['min_class2_price']].drop(columns='min_class2_price')
方法二:提取分组最小值后合并筛选
先单独提取每个分组的目标最小值,再合并到原表进行筛选:
# 提取每个name分组中class=2的最小price min_price_map = df_example[df_example['class'] == 2].groupby('name')['price'].min().rename('min_class2_price') # 合并到原表,筛选后删除临时列 result = df_example.merge(min_price_map, on='name') result = result[result['price'] > result['min_class2_price']].drop(columns='min_class2_price')
方法三:groupby.apply直接处理分组(适合小数据集)
自定义函数处理每个分组,直接返回符合条件的行:
def filter_group(group): # 获取当前分组中class=2的最小price min_p = group[group['class'] == 2]['price'].min() # 返回符合条件的行 return group[group['price'] > min_p] result = df_example.groupby('name').apply(filter_group).reset_index(drop=True)
关于之前NaN问题的说明
之前的代码出现NaN,是因为先通过df_example[df_example['class'] == 2]过滤掉了非class=2的行,后续transform只能给这些被保留的行赋值,原表中被过滤的行位置就会填充NaN。正确的做法是在transform的lambda函数内部,针对整个分组的索引去筛选class=2的行,而不是提前过滤原表。
内容的提问来源于stack exchange,提问作者user6703592
相关产品推荐
相关产品推荐

