Skip to content

解决 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=11.52.0旧:(1+1+1+1+2+3)/6=1.5; 新:(1+2+3)/3=2.0
a=23.55.0旧:(2+2+2+4+5+6)/6=3.5; 新:(4+5+6)/3=5.0

最佳实践建议

  1. 判断是否需使用分组列

    • 若 Lambda 函数中未使用分组列 → 采用 include_groups=False
    • 若 Lambda 函数中使用了分组列 → 采用显式列选择方法
  2. 升级兼容性处理

    python
    # 通用兼容写法
    kwargs = {'include_groups': False} if pd.__version__ >= '2.2.0' else {}
    df.groupby('group_col').apply(func, **kwargs)
  3. 明确列选择原则

    • apply() 过程中,仅传入计算必需的列
    • 避免依赖分组列参与数值计算(除非明确需要)

长远解决方案

未来升级到 pandas 3.0+ 时,include_groups=False 将成为默认行为。立即修改代码可确保平稳过渡。

总结

当遇到 groupby.apply() 的弃用警告时,核心解决方案是明确隔离分组列与计算列

  1. 最优方案是添加 include_groups=False 参数
  2. 也可以通过显式选定处理列或将分组列设为索引
  3. 理解 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')
)