pandasでExcelの申請一覧を絞り込もうとしたとき、急に `ValueError: The truth value of a Series is ambiguous` が出ました。最初は、英語のエラー文を見ただけで手が止まったのを覚えています。Seriesがあいまいと言われても、何があいまいなのか分かりませんでした。そのとき作っていたのは、Excelから読み込んだ一覧を条件で分けるだけの小さな処理です。未処理の申請だけを取り出します。金額が0より大きいものだけを残します。条件に合う行があれば、別のExcelに書き出すつもりでした。
ところが、if文や複数条件を書いたところで処理が止まりました。あとから見ると、原因はpandasのSeriesを1つのTrueかFalseのように扱っていたことでした。この記事では、自分が実際に確認した順番で、同じエラーを直した流れをまとめます。
最初に止まったのは、…
最初に止まったのは、Excelから読み込んだ申請一覧を判定するところでした。DataFrameには、部署、ステータス、金額、申請日の列がありました。自分としては、ステータスが未処理で、金額が0より大きい行だけを取り出したかったです。そこで、いつものif文の感覚で条件を書きました。
import pandas as pd
df = p…
import pandas as pd
df = pd.read_excel("申請一覧.xlsx")
if df["ステータス"] == "未処理":
print("未処理があります")
見た目だけなら、そんなに変なコードには見えませんでした。Excelのセルを1つ見ているつもりだったからです。でも、pandasでは `df[“ステータス”] == “未処理”` の結果が1つの答えになりません。行ごとにTrueやFalseが並んだSeriesになります。
つまり、pandasから見ると「この列の中にTrueとFalseがたくさんありますが、全体としてTrueですか」と聞かれている状態でした。それは確かにあいまいです。1件でも該当すればTrueなのか、全部該当したらTrueなのか、pandasには判断できません。そこで、このエラーが出ていました。

エラー文の中には、`a.empty`、…
エラー文の中には、`a.empty`、`a.bool()`、`a.item()`、`a.any()`、`a.all()` のような候補も出ます。最初は全部使えばよいのかと思いました。でも、ここで大事だったのは、まず自分が何を判定したいのかを分けることでした。行を絞り込みたいのか、全体として該当があるかを知りたいのかで、書き方が変わりました。
最初に見直したのは、if文の中でした。pandasで列を指定すると、1つの値ではなくSeriesになることがあります。Seriesは、複数行ぶんの値をまとめたものです。そのため、普通のif文にそのまま入れると、今回のエラーにつながりました。自分がやっていた間違いは、次のような形です。
if df[“金額”] > 0:
print(…
if df["金額"] > 0:
print("金額あり")
この書き方だと、`df[“金額”] > 0` の結果は行ごとの判定になります。たとえば、1行目はTrue、2行目はFalse、3行目はTrueのように並びます。それをif文に入れると、pandasは1つの答えに決められません。ここで、truth value is ambiguousと止まります。
自分はまず、条件式だけを別に出しました。そうすると、何が起きているか見えやすくなります。
mask = df["金額"] > 0
print(mask)
画面には、TrueとFalseが縦に並びます。これを見て、ようやく「if文に入れていたのは1つの答えではなかった」と分かりました。変数名を `mask` にすると、条件で行を選ぶための印だと考えやすくなります。自分の中では、ここでかなり整理できた感覚がありました。

行を取り出したい場合は、…
行を取り出したい場合は、if文ではなくDataFrameの中に条件を入れました。自分の場合は、次のように書き直しました。
amount_mask = df["金額"] > 0
filtered = df[amount_mask]
この形なら、Trueになった行だけが残ります。全体を1つのTrueかFalseにしようとしていません。行ごとの条件として使っているだけです。まずここを切り分けると、エラーの意味が少し分かりやすくなりました。
次に引っかかったのは、複数条件でした。Pythonのif文では、条件をつなぐときに `and` や `or` をよく使います。その感覚でpandasの条件にも同じように書きました。これも、自分には自然に見えていました。
filtered = df[(df[“ステータス”] …
filtered = df[(df["ステータス"] == "未処理") and (df["金額"] > 0)]
でも、pandasの行ごとの条件では、この書き方が合いませんでした。`and` は、左側と右側をそれぞれ1つのTrueかFalseとして見ようとします。ところが、左側も右側もSeriesです。そのため、ここでもあいまいな真偽値として止まりました。
pandasで行ごとに条件をつなぐときは、`&` や `|` を使う形に変えました。`&` は両方に当てはまる行を選ぶときに使います。`|` はどちらかに当てはまる行を選ぶときに使います。自分の申請一覧では、未処理かつ金額ありなので `&` を使いました。
filtered = df[(df["ステータス"] == "未処理") & (df["金額"] > 0)]
これで、ようやく期待した行だけが残ります
これで、ようやく期待した行だけが残ります。最初は、なぜ記号を使うのか少し気持ち悪く感じたのを覚えています。ただ、pandasでは行ごとにTrueとFalseを並べて、その組み合わせで行を残すと考えると納得しやすかったです。普通のif文とは別物として見た方が楽でした。

`or` の代わりは `|` です。たとえば、ステータスが未処理または差戻しの行を取りたい場合は、次のようにしました。
filtered = df[
(df["ステータス"] == "未処理") |
(df["ステータス"] == "差戻し")
]
このあたりは、Excelのフィルターに近い感覚で考えました。複数の条件を作り、行ごとに当てはまるかどうかを見ます。その結果を使って、DataFrameから必要な行を取り出します。そう考えると、andやorではなく、pandas用のつなぎ方が必要だと分かりました。
`&` や `|` に直したあとも、まだエラーが出ることがありました。そこで見直したのが、括弧です。pandasの複数条件では、条件式ごとに括弧を付ける必要があります。ここを忘れると、見た目は似ていても違う意味に読まれます。
たとえば、次のような書き方は危ないです
たとえば、次のような書き方は危ないです。
filtered = df[df["ステータス"] == "未処理" & df["金額"] > 0]
自分の目には、ステータスが未処理で金額が0より大きい、という意味に見えました。でも、Python側では演算子の優先順位が絡みます。人間が思ったまとまりと、Pythonが読むまとまりがずれます。その結果、別のエラーになったり、truth value is ambiguousに戻ったりしました。
直した形は、条件ごとに括弧で囲む書き方です。
filtered = df[
(df["ステータス"] == "未処理") &
(df["金額"] > 0)
]
この形にすると、1つ目の条件と2つ目の条件がはっきり分かれます。自分は、pandasの複数条件では「条件1つにつき括弧1つ」と覚えることにしました。コードが少し長くなっても、あとから見たときに分かりやすいです。エラーも減りました。
括弧を付けるときは、改行も一緒に入れるようにしました
括弧を付けるときは、改行も一緒に入れるようにしました。1行に詰め込むと、自分の場合は見落とします。条件が増えたときほど、縦に並べた方が確認しやすかったです。
combined_mask = (
(df["ステータス"] == "未処理") &
(df["金額"] > 0) &
(df["部署"] == "経理")
)
filtered = df[combined_mask]
このように、先に `mask` を作る形も助かりました。条件だけを一度表示できます。Trueになっている件数も数えられます。本番の処理へ渡す前に、条件の結果を確認できました。
行を絞り込むだけなら、…
ここで一度立ち止まって考えてみてください
Pythonや自動化スキルを体系的に習得して、ITエンジニアとしてのキャリアを切り開きたい方には「Enjoy Tech!(エンジョイテック)」が選択肢のひとつです。現役エンジニアのサポートで、未経験から実践的なスキルを身につけられます。
行を絞り込むだけなら、DataFrameに条件を入れれば済みました。でも、別の場面ではif文が必要でした。たとえば、未処理の申請が1件でもあればメッセージを出したい場面です。この場合は、Seriesをそのままif文に入れず、1つの答えに変える必要がありました。
自分が最初に書いたのは、また同じような形でした。
if df["ステータス"] == "未処理":
print("未処理があります")
これは、やはり止まります。未処理の行があるかを知りたいなら、`any()` を使いました。`any()` は、1件でもTrueがあればTrueにしてくれます。申請一覧のチェックには、この考え方が合っていました。
has_pending = (df[“ステータス”] …
has_pending = (df["ステータス"] == "未処理").any()
if has_pending:
print("未処理があります")
一方で、全件が条件を満たしているかを見たい場合は `all()` を使いました。たとえば、全行に申請日が入っているかを確認する場面です。1件でも空欄があれば困るので、全体確認になります。
all_has_date = df["申請日"].notna().all()
if all_has_date:
print("申請日はすべて入っています")

ここで、自分の中の整理はかなり進みました。行を取り出したいなら、SeriesのままDataFrameに渡します。全体として判定したいなら、`any()` や `all()` で1つのTrueかFalseにします。この違いを混ぜると、同じエラーに戻りました。
実務では、先に変数名で目的を分けると間違いにくかったです
実務では、先に変数名で目的を分けると間違いにくかったです。`mask_pending` なら行ごとの条件として読めます。`has_pending` なら全体の判定だと分かります。名前を変えるだけでも、自分にはかなり効果がありました。
同じエラーは、`isin()` や否定条件でも出ました。たとえば、ステータスが複数の候補に入っているかを見たい場合です。最初はPythonの `in` を使えばよいと思っていました。でも、pandasの列に対しては `isin()` を使う方が分かりやすかったです。たとえば、未処理と差戻しをまとめて取り出す場合は、次のようにしました。
target_status = ["未処理", "差戻し"]
filtered = df[df["ステータス"].isin(target_status)]
これなら、行ごとに対象ステータスかどうかを判定できます。結果はTrueとFalseのSeriesです。そのSeriesをDataFrameに渡すので、行の抽出として自然に動きました。自分のように複数条件で混乱しやすい場合は、`isin()` の方が読みやすかったです。
条件を反転するときも、…
条件を反転するときも、普通の `not` を使うと混乱しました。pandasの行ごとの条件を反転したい場合は、`~` を使いました。たとえば、完了以外を取り出すなら、次のような形です。
filtered = df[~(df["ステータス"] == "完了")]
`isin()` と組み合わせる場合もあります。
exclude_status = ["完了", "取消"]
filtered = df[~df["ステータス"].isin(exclude_status)]
ここでも、括弧やまとまりを見やすくすることが大事でした。`~` は見落としやすいです。条件が長くなると、自分で書いたコードなのに分からなくなります。そのため、除外条件は一度変数に分けるようにしました。
exclude_status = ["完了", "取消"]
mask_exclude = df["ステータス"].isin(exclude_status)
filtered = df[~mask_exclude]
この書き方なら、まず除外したい行を確認できます
この書き方なら、まず除外したい行を確認できます。そのあとで、反転して残す行を選べます。少し遠回りに見えますが、あとから見たときの安心感が違いました。エラーが出たときも、どの条件が原因か探しやすかったです。
最後に、自分が見る順番を決めました。まず、if文の中にSeriesやDataFrameをそのまま入れていないかを見ます。次は、複数条件で `and` や `or` や `not` を使っていないかを見る流れです。そのあと、`&` や `|` を使っている条件に括弧が付いているかを確認します。
自分用には、次のような順番でメモしました
自分用には、次のような順番でメモしました。
行を取り出したい場合
row_mask = (df["ステータス"] == "未処理") & (df["金額"] > 0)
filtered = df[row_mask]
1件でもあるか知りたい場合
has_pending = (df["ステータス"] == "未処理").any()
全部そろっているか知りたい場合
all_has_date = df["申請日"].notna().all()
この3つを分けるだけで、かなり迷いが減ります。以前は、全部をif文で何とかしようとしていました。でも、pandasでは行ごとの判定と全体の判定を分けた方が楽でした。Seriesはそのまま悪いものではなく、使う場所を間違えると止まるものだと分かりました。
今回のエラーは、英語の文だけ見ると難しく感じます
今回のエラーは、英語の文だけ見ると難しく感じます。ただ、自分の場合は「複数のTrue/Falseを1つに決めようとしていないか」と見ると理解しやすかったです。行を絞り込むなら `df[mask]` と考えます。全体を判定するなら `any()` や `all()` を使います。複数条件では `&` と `|` を使い、条件ごとに括弧を付ける形にしました。
実務の申請一覧では、この順番で直してから処理が安定しました。もちろん、pandasに詳しい人ならもっと短く書けると思います。でも、自分のように手探りで直している人には、まず分けて書く方が安心でした。同じエラーが出たら、コード全体を疑う前に、条件式の目的を1つずつ見直すのが一番マシだと思っています。
無料プレゼント
Excel業務を自動化する前に確認するチェックリスト(PDF)
自動化していい作業かどうか、VBAかPythonか、最初に避けるべき落とし穴。実務でよく迷うポイントを1枚にまとめました。メールアドレスだけで受け取れます。
¥980 ミニキット
コピペで動かせる3スクリプト+自動化チェックリスト
最新ファイルの自動選択・部署名ゆれの正規化・CSV文字コード確認の3本セット。今週の作業を1つだけ楽にするための最小キットです。
関連リンクとチェックリスト
関連記事
pandasの条件式まわりでつまずいたときは、列名の指定ミスや代入忘れも一緒に見ると原因を狭めやすいです。
関連書籍
Python・pandas関連書籍を、手元で見返せる形にしておきたい場合
pandasのエラー対応は、似た症状を何度も見返すことが多いです。データ前処理や型変換を手元で確認できる本を1冊置いておくと、調べ直しの時間を減らしやすいです。
学習サービスとアンケート
このスキルを活かしてさらに前へ進むなら
PythonやExcel自動化スキルを持ったまま、ITエンジニアとして転職したい方には「EBAエデュケーション」が選択肢です。企業が求めるエンジニア像に合わせたカリキュラムで、実務直結のスキルを習得できます。
[アンケート] この記事は役に立ちましたか?

