pandas groupby.applyにおけるDeprecationWarningの解決法
問題の背景
pandasを使用してデータフレームを操作中、groupby.apply()
を実行すると以下の警告が発生します:
python
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.
この警告はgroupby.apply()
の挙動変更に起因します。具体的には、グループ化に使用した列が計算対象に含まれていた従来の動作が非推奨となりました。pandas 3.0以降ではグループ化列は自動的に除外されるため、現在の警告は移行期間中の通知です。
例となるコード:
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')
警告が発生する原因
- グループ化列(
StartDate
,Commodity
,DealType
)がapply()
内の操作対象に含まれていない - グループ化列はグループ定義にのみ使用され、計算では不要にも関わらず自動的に渡されている
- この動作は将来的に変更されるため警告が表示される
解決手法
すべての解決策は同じ計算結果を得られます。環境やコードスタイルに応じて選択してください。
方法1: include_groups=False
を使用
pandas 2.2.0以降で利用可能なオプションを明示的に指定します。
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')
方法2: 必要な列を明示的に選択
警告メッセージにある代替案として、操作対象の列を直接指定します。
python
columns_used = ['MTMValue', 'FixedPriceStrike', 'Quantity']
fprice = df.groupby(['StartDate', 'Commodity', 'DealType'])[columns_used].apply(
lambda group: -(group.MTMValue.sum() - (group.FixedPriceStrike * group.Quantity).sum()
) / group.Quantity.sum()
).reset_index(name='FloatPrice')
方法3: インデックスを使用したグループ化
グループ化列を事前にインデックスに設定します。
python
group_columns = ['StartDate', 'Commodity', 'DealType']
df_indexed = df.set_index(group_columns)
fprice = df_indexed.groupby(group_columns).apply(
lambda group: -(group.MTMValue.sum() - (group.FixedPriceStrike * group.Quantity).sum()
) / group.Quantity.sum()
).reset_index(name='FloatPrice')
(応用) グループ化列を保持する必要がある場合
グループ名(x.name
)を使用して列を再付与します。
高度なケース
グループ化列を計算内で使用する必要がある場合(例:グループ名を参照)は、assign()
で明示的に追加します。
python
fprice = df.groupby(['DealType']).apply(
lambda x: (
x[x['MTMValue'] == x['MTMValue'].max()]
.assign(DealType=x.name) # グループ名を付加
),
include_groups=False
).reset_index(drop=True)
動作変更の詳細解説
実際の挙動の違いを理解するためのサンプルデータ:
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]
})
現行動作(include_groups=True
)
python
df.groupby('a').apply(np.mean)
# 出力:
# a
# 1 1.5 # (1+1+1 + 1+2+3)/6
# 2 3.5 # (2+2+2 + 4+5+6)/6
新しい動作(include_groups=False
)
python
df.groupby('a').apply(np.mean, include_groups=False)
# 出力:
# a
# 1 2.0 # (1+2+3)/3
# 2 5.0 # (4+5+6)/3
重要ポイント
- pandas <2.2.0: デフォルトで全列が渡される
- pandas 2.2.0~3.0: 警告表示。
include_groups=False
で抑制可能 - pandas 3.0~: グループ化列は自動除外(デフォルト動作)
推奨アプローチ
- バージョン確認:
pd.__version__
でpandas 2.2.0以上か確認pythonimport pandas as pd print(pd.__version__) # >=2.2.0 推奨
- 即時対応:
include_groups=False
を追加するのが最速 - 将来的対応: メソッドチェーンを多用する場合は列の明示的選択(方法2)が安定
- 注意: 旧バージョン用のコードには
if
分岐を追加するpythonkwargs = {'include_groups': False} if pd.__version__ >= '2.2.0' else {} df.groupby(...).apply(func, **kwargs)
ベストプラクティス
python
# 必要な列のみ指定 + 最新オプション適用
calc_columns = ["MTMValue", "FixedPriceStrike", "Quantity"]
kwargs = {"include_groups": False} if pd.__version__ >= "2.2.0" else {}
fprice = (
df.groupby(["StartDate", "Commodity", "DealType"])[calc_columns]
.apply(
lambda g: -(g["MTMValue"].sum() - (g["FixedPriceStrike"] * g["Quantity"]).sum())
/ g["Quantity"].sum(),
**kwargs
)
.reset_index(name="FloatPrice")
)