【pandas】groupby.mean の TypeError は numeric_only が原因だった話

エラー発生の経緯

KaggleのHomeCreditRiskのEDA(https://www.kaggle.com/code/osciiart/homecreditrisk-extensive-eda-baseline-model-jp/notebook)をKaggle Notebookと手元環境の両方で実行しました。

すると、手元環境でのみ以下のコードがTypeErrorを出しました。

prev_apps_avg = previous_application.groupby('SK_ID_CURR').mean()
TypeError: dtype 'str' does not support operation 'mean'

以下では調査の過程をまとめています。急ぎの方は「解決方法」へどうぞ。

原因の候補

エラーメッセージから、文字列カラムに対してmeanが実行されていることが原因だと推測できます。

まず、Kaggleと手元環境のバージョンを比較しました。ライブラリのメジャーバージョン差による挙動の違いが疑われます。

項目Kaggle手元の環境
Python3.6.53.13.0
pandas0.23.03.0.1

試したこと

pandas はデータ分析で広く使われているため、今回の調査は今後のトラブルシューティングにも役立つと考えています。

まずは、meanの挙動を確認するためにシンプルなコードを実行しました。

import sys
import pandas as pd
print(sys.version)
print(pd.__version__)
df = pd.DataFrame({
    'A': [1, 2, 3, 1, 2, 3],
    'B': ['a', 'b', 'c', 'd', 'e', 'f'],
    'C': [10, 20, 30, 40, 50, 60]
})
print(df.groupby('A').mean())

    C
A    
1  25
2  35
3  45

出力を見ると、文字列カラムBが自動的に除外されて平均が計算されています。

そのため、バージョン差を吸収する暫定対策としては、事前に文字列カラムを削除する方法が考えられます。

df = df.drop(['B'], axis=1)

原因をより正確に把握するため、pandasのバージョンごとに挙動を比較しました。

pip index versions pandas

メジャーバージョン0~3が存在することを確認し、仮想環境でv1.5.3とv2.0.0を用意して検証しました。

python -m venv v_pd_v2
pip install pandas==2.0.0

結果は以下の通りです。

  • v1.5.3→正常終了
  • v2.0.0→TypeError発生

両バージョンのAPIリファレンスを確認すると、numeric_onlyのデフォルト値が変更されていました。

pandas.core.groupby.GroupBy.mean — pandas 1.5.2 documentation
引数v1.5.3(v1.5.2)のデフォルト引数v2.0.0(v2.0.3)のデフォルト引数
numeric_onlyNone(挙動は実質 True)False
engine“cython”“cython”
engine_kwargsNoneNone

v2.0.0のwhat’s newにも以下の記述があります。

What’s new in 2.0.0 (April 3, 2023) — pandas documentation
Changed default of numeric_only in various DataFrameGroupBy methods; all methods now default to numeric_only=False (GH 46072)

GitHubのissueをざっくりまとめると、

  • v1までの非数値カラムの暗黙的なドロップについて問題視された
  • v2からはnumeric_only=Trueを明示した場合のみ非数値カラムを除外する

という方針に変更されています。


v0系・v1系で書かれたコードをv2以降で動かす場合、以下のようにnumeric_only=Trueを明示する必要があります。

import sys
import pandas as pd
print(sys.version)
print(pd.__version__)
df = pd.DataFrame({
    'A': [1, 2, 3, 1, 2, 3],
    'B': ['a', 'b', 'c', 'd', 'e', 'f'],
    'C': [10, 20, 30, 40, 50, 60]
})
# v1まで
# print(df.groupby('A').mean())
# v2以降
print(df.groupby('A').mean(numeric_only=True))

    C
A    
1  25
2  35
3  45

解決策

文字列を含むDataFrameの平均処理はpandasのバージョンで挙動が異なるため、以下のように書き分ける必要があります。

# v2.0.0より前(numeric_only=None だが非数値カラムは自動除外されるため実質 True)
df.groupby('A').mean()

# v2.0.0以降(numeric_only=Falseがデフォルト)
df.groupby('A').mean(numeric_only=True)

おわりに

最近は Copilot などの LLM に質問すれば多くの問題はすぐ解決できますが、今回は回答が得られなかったため、自分でリリースノートや Issue を読みながら調査しました。

結果として、ライブラリの仕様変更を追いかける良い練習になりました。

コメント

タイトルとURLをコピーしました