解决 pandas groupby.apply 中的弃用警告
问题描述
在使用 pandas 处理数据时,当使用 groupby().apply()
方法对 DataFrame 进行分组操作时,您可能会遇到以下弃用警告:
DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
此警告通常出现在类似下面的代码中:
python
fprice = df.groupby(['StartDate', 'Commodity', 'DealType']).apply(
lambda group: -(group['MTMValue'].sum() - (group['FixedPriceStrike'] * group['Quantity']).sum()) / group['Quantity'].sum()
).reset_index(name='FloatPrice')
问题根源
问题原因是 pandas 2.2.0 及以上版本改变了 groupby.apply()
的行为:默认会将分组列包含在处理数据中。而在未来的 pandas 版本中(预计 3.0+),分组列将默认被排除在处理范围外。
解决方案
以下是三种解决此警告的方法:
方法 1:使用 include_groups=False(推荐)
原理说明
直接在 apply()
方法中添加 include_groups=False
参数,显式排除分组列:
python
fprice = df.groupby(['StartDate', 'Commodity', 'DealType']).apply(
lambda group: -(group['MTMValue'].sum() - (group['FixedPriceStrike'] * group['Quantity']).sum()) / group['Quantity'].sum(),
include_groups=False # 关键参数
).reset_index(name='FloatPrice')
版本要求
此参数在 pandas 2.2.0 及以上版本可用。后续版本中此参数将是兼容新行为的推荐方式。
方法 2:显式选择处理列
原理说明
在 groupby 后明确指定需要处理的列,使分组列不被自动传入:
python
# 显式选择需要计算的列
fprice = df.groupby(['StartDate', 'Commodity', 'DealType'])[['MTMValue', 'FixedPriceStrike', 'Quantity']].apply(
lambda group: -(group['MTMValue'].sum() - (group['FixedPriceStrike'] * group['Quantity']).sum()) / group['Quantity'].sum()
).reset_index(name='FloatPrice')
方法 3:设置分组列为索引
原理说明
将分组列设置为索引后再进行分组操作:
python
# 先将分组列设为索引
df_indexed = df.set_index(['StartDate', 'Commodity', 'DealType'])
fprice = df_indexed.groupby(level=['StartDate', 'Commodity', 'DealType']).apply(
lambda group: -(group['MTMValue'].sum() - (group['FixedPriceStrike'] * group['Quantity']).sum()) / group['Quantity'].sum()
).reset_index(name='FloatPrice')
新旧行为对比
了解新旧行为差异有助于理解警告根源:
python
import pandas as pd
import numpy as np
df = pd.DataFrame({'a': [1, 1, 1, 2, 2, 2],
'b': [1, 2, 3, 4, 5, 6]})
# 旧行为 (pandas <2.2.0,分组列包含在计算中)
df.groupby('a').apply(np.mean, include_groups=True)
# 新行为 (pandas >=2.2.0 推荐的模式)
df.groupby('a').apply(np.mean, include_groups=False)
结果解释
分组 | 旧行为结果 | 新行为结果 | 计算逻辑 |
---|---|---|---|
a=1 | 1.5 | 2.0 | 旧:(1+1+1+1+2+3)/6=1.5; 新:(1+2+3)/3=2.0 |
a=2 | 3.5 | 5.0 | 旧:(2+2+2+4+5+6)/6=3.5; 新:(4+5+6)/3=5.0 |
最佳实践建议
判断是否需使用分组列:
- 若 Lambda 函数中未使用分组列 → 采用
include_groups=False
- 若 Lambda 函数中使用了分组列 → 采用显式列选择方法
- 若 Lambda 函数中未使用分组列 → 采用
升级兼容性处理:
python# 通用兼容写法 kwargs = {'include_groups': False} if pd.__version__ >= '2.2.0' else {} df.groupby('group_col').apply(func, **kwargs)
明确列选择原则:
- 在
apply()
过程中,仅传入计算必需的列 - 避免依赖分组列参与数值计算(除非明确需要)
- 在
长远解决方案
未来升级到 pandas 3.0+ 时,include_groups=False
将成为默认行为。立即修改代码可确保平稳过渡。
总结
当遇到 groupby.apply()
的弃用警告时,核心解决方案是明确隔离分组列与计算列:
- 最优方案是添加
include_groups=False
参数 - 也可以通过显式选定处理列或将分组列设为索引
- 理解 pandas 的行为变更可避免未来兼容性问题
处理后的代码如下:
python
# 最终优化版
fprice = (
df.groupby(['StartDate', 'Commodity', 'DealType'])
.apply(
lambda g: -(
g['MTMValue'].sum()
- (g['FixedPriceStrike'] * g['Quantity']).sum()
) / g['Quantity'].sum(),
include_groups=False
)
.reset_index(name='FloatPrice')
)