Pandas で and が使えず & が要る理由

Pandas で複数条件の絞り込みに and を使うと「The truth value of a Series is ambiguous」で落ちます。and が全体で 1 つの真偽値を求めるのに対し、& は行ごとに判定する別の演算で、各条件は丸カッコで囲む必要があります。

· 644 字 · 3 分
目次 3 sections
  1. 01 and は「全体で 1 つ」を求める
  2. 02 & は「行ごとに 1 つ」
  3. 03 まとめ

Understat から取ってきたセリエAの選手データで「ミランの選手」かつ「5 得点以上」を絞り込もうとして、素直にこう書きました。

df[df["team_title"] == "AC Milan" and df["goals"] >= 5]
ValueError: The truth value of a Series is ambiguous.
Use a.empty, a.bool(), a.item(), a.any() or a.all().

and は「全体で 1 つ」を求める#

Python の and は、左右のオブジェクトを真偽値 1 つに変換してから判定します。ところが df["team_title"] == "AC Milan" が返すのは 行数と同じ長さの bool の Series で、真偽値 1 つではありません。

print(df["team_title"] == "AC Milan")
0 False
1 False
2 False
...
585 False
Name: team_title, Length: 586, dtype: bool

586 行ぶんの True/False が並んだこれを、and は「Yes か No のどちらか 1 つ」に決めようとします。1 つでも True があれば True なのか、全部 True でなければ False なのか。決めようがないので Pandas は判断を拒否し、ambiguous(あいまい)だと言ってきます。エラーメッセージが a.any() や a.all() を勧めてくるのは、「全体で 1 つにまとめるならどちらの意味か明示しろ」ということです。

& は「行ごとに 1 つ」#

一方、こちらが本当にやりたいのは 1 行ずつ「両方の条件を満たすか」を判定する ことで、全体を 1 つにまとめることではありません。それをやるのが & です。

df[(df["team_title"] == "AC Milan") & (df["goals"] >= 5)]
player_name team_title position goals
Rafael Leão AC Milan F S 9
Christian Pulisic AC Milan F S 8
Christopher Nkunku AC Milan F S 7
Adrien Rabiot AC Milan M S 6
Strahinja Pavlovic AC Milan D S 5

& は 2 つの bool Series を要素ごとに比較し、結果もまた bool の Series で返します。df[...] はそれを行の選択に使います。or に対応するのが |、not に対応するのが ~ で、対応関係は同じです。

なお各条件を丸カッコで囲んでいるのは飾りではありません。& は == や >= より結合が強いため、囲まないと意図と違う順序で評価されます。

まとめ#

& は and とはそもそも別の演算で、代替品ではありません。and が「まとめて 1 つの答え」を出すのに対し、& は「行ごとに答え」を出します。Pandas の絞り込みで欲しいのは常に後者です。

データの取得元については Pythonでサッカー分析を学ぼうとしたら、無料のxGが消えていた に書きました。

記事一覧