apply返回值类型决定结果形状:标量→series,pd.series/dict→dataframe,dataframe→multiindex;需显式命名、避免describe;整组计算用groupby().apply();性能优先agg;调试先测单组。

用 apply 传入自定义函数,但必须返回明确结构
直接对 groupby 对象调用 apply 是最常用方式,但关键不是“能传函数”,而是函数的返回值类型会决定最终结果形状。如果函数返回标量(如 float、int),结果是 Series;如果返回 pd.Series 或 dict,结果是 DataFrame;如果返回一个完整 DataFrame,结果会自动堆叠成多层索引结构——这点极易踩坑。
常见错误现象:apply 后得到意外的 MultiIndex 或列名混乱,本质是函数返回了未命名的 Series 或未指定 name 的字典。
- 函数内若需返回多个统计量,推荐用
pd.Series({'mean': x.mean(), 'std': x.std()}),显式命名 - 避免在函数里直接写
return x.describe(),它返回带层级索引的Series,容易导致后续操作失败 - 如果函数逻辑依赖组内其他列(不止分组键),确保传入的是整个子
DataFrame,而不是默认的Series(即别用df.groupby('A')['B'].apply(...),改用df.groupby('A').apply(...))
处理返回非标量结果时,用 result_type='expand' 控制展开行为
当自定义函数返回 list、tuple 或 np.ndarray 时,apply 默认把整个对象当做一个单元塞进结果,不会自动拆成列。这时候要主动加参数控制。
使用场景:比如每组计算前 3 个最大值,想展开为三列 top1、top2、top3。
- 函数应返回
pd.Series([v1, v2, v3], index=['top1', 'top2', 'top3']),而非[v1, v2, v3] - 或保持返回
list,但调用时加result_type='expand',此时必须确保所有组返回等长列表,否则报错ValueError: all objects must have the same length -
result_type='reduce'强制降维(适合返回单值但误被当对象的情况),result_type='broadcast'会把结果广播回原行数——这两个极少用,别乱试
性能敏感时,优先用 agg + 命名元组,而非 apply
如果自定义函数本质是聚合(输入一组值,输出一个或几个汇总值),agg 比 apply 快得多,且天然支持多函数、多列组合。它不执行 Python 循环,而是委托给底层优化路径。
性能影响:对百万级数据,df.groupby('A').apply(lambda x: (x['B'].mean(), x['C'].sum())) 可能比等价的 df.groupby('A').agg({'B': 'mean', 'C': 'sum'}) 慢 5–10 倍。
- 复杂逻辑可封装为普通函数,然后传给
agg:df.groupby('A').agg({'B': my_custom_agg_func}) - 若需同时返回多个指标,函数应返回
pd.Series,并配合列名映射:df.groupby('A').agg({'B': lambda x: pd.Series({'avg': x.mean(), 'cnt': x.count()})}) -
apply在需要访问整组DataFrame行顺序、做跨行计算(如累计差分、窗口拟合)时不可替代;但只要能拆解为列独立聚合,就别用apply
调试自定义函数时,先用 .head(1) 组测试再全量跑
分组后调用自定义函数出错,往往不是语法问题,而是某组数据为空、含 NaN、类型不一致,或函数内部用了全局变量/副作用(如修改外部 list)。直接全量运行会掩盖具体哪组崩了。
容易被忽略的地方:空组(groupby 后某组无数据)会导致传入空 DataFrame,若函数里写了 x.iloc[0] 就立刻报 IndexError;又或者用了 scipy.stats.linregress(x['A'], x['B']),但某组只有 1 行,直接抛异常。
- 先取一个典型组测试:
grp = list(df.groupby('A'))[0][1]; my_func(grp) - 在函数开头加防御判断:
if len(x) == 0: return np.nan或if x['B'].nunique() - 别依赖
print()调试——apply可能并行或重排执行顺序,输出错乱;改用logging或写临时文件
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











