filter()方法用于groupby对象的分组级条件筛选,接收作用于每组dataframe的函数,返回true保留整组;agg()后布尔索引适用于聚合值筛选;transform()则用于广播聚合结果以筛选原始行。

用 filter() 方法对 GroupBy 对象做条件筛选
直接在 groupby() 后调用 filter(),是最贴近“按条件过滤分组”的做法。它接收一个函数,该函数作用于每个分组(即每个 pd.DataFrame 子集),返回 True 表示保留该组,False 表示丢弃。
常见错误是误以为 filter() 接收的是单个值或 Series —— 它实际接收的是整个分组 DataFrame,所以逻辑要围绕子集整体写,比如「该组人数 ≥ 3」或「该组最大销售额 > 10000」。
- 若想保留每组平均值大于 50 的分组:
df.groupby('category').filter(lambda g: g['score'].mean() > 50) - 若依赖多个列判断:用
g['a'].sum() / g['b'].count() > 10这类表达式,注意避免除零(g['b'].count()为 0 时会出错) -
filter()不支持字符串谓词(如"score > 50"),必须传函数;也不能链式调用.agg()后再 filter —— 因为 agg 后已不是 GroupBy 对象
先 agg() 再用布尔索引筛选分组统计结果
当你的条件基于聚合后的标量值(比如“各城市平均薪资 > 15000”),更适合先用 agg() 得到一个带索引的 Series 或 DataFrame,再用布尔索引过滤。这比 filter() 更直观、更易调试。
注意:此时操作对象已不再是 GroupBy,而是聚合结果,所以不能再调用 filter() 或 apply() 等 GroupBy 方法。
- 获取每组均值后筛选:
means = df.groupby('city')['salary'].mean(); means[means > 15000] - 多指标聚合后筛选:用
df.groupby('dept').agg({'salary': 'mean', 'age': 'max'}),然后result[result['salary'] > 8000] - 如果后续还需原始数据,得用
transform()或map()把聚合结果广播回原表,再配合loc筛选行
用 transform() 实现“组内广播 + 全局布尔索引”
当你需要保留满足组级条件的**原始行**(而非整个组或聚合值),transform() 是关键。它把聚合结果按原索引广播回去,生成与原 DataFrame 等长的 Series,可直接用于 loc 索引。
典型场景:「只保留所在组中销售额高于该组均值的订单」—— 这种不能用 filter()(它要么留整组,要么删整组),也不能只靠 agg()(它不返回行级信息)。
- 正确写法:
df.loc[df.groupby('region')['revenue'].transform('mean') -
transform()支持字符串方法('mean','size')和自定义函数,但函数必须返回标量或与输入等长的序列 - 性能上,
transform()比apply()快得多;但如果函数内部做了复杂计算,仍建议先agg()缓存再map(),避免重复执行
容易忽略的边界情况和兼容性问题
GroupBy 过滤看似简单,但几个细节常导致结果意外:
- 空组:如果分组键含
NaN,默认会被丢弃(dropna=True)。若需保留,加参数df.groupby('col', dropna=False) - 多级索引分组后,
filter()函数里的g仍是 DataFrame,但索引是 MultiIndex,取值时注意用g.index.get_level_values(0)而非g.index[0] - Pandas 2.0+ 中
filter()对返回值类型更严格:必须明确返回 Python 布尔(True/False),不能返回 numpy.bool_ 或 pd.BooleanDtype 的 NA —— 否则报ValueError: The truth value of a Series is ambiguous - 用
transform()时若某组全为NaN,结果对应位置也是NaN,参与比较(如> 100)会得False,可能漏掉本应保留的行;必要时先fillna()或改用where()
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











