pandasで複数条件を指定する方法|AND(&)・OR(|)・NOT(~)の書き方とandでエラーになる理由

pandasで複数条件を組み合わせて行を抽出するときは、ANDに &、ORに |、NOTに ~ を使い、条件はそれぞれ丸括弧で囲みます。Pythonの and・or・not をそのまま書くとエラーになりますが、df.query() の文字列の中なら and・or・not が使えます。

この記事では、3つの演算子の基本、括弧を忘れたときに起きること、欠損値(NaN)がある列でNOTを使うときの注意、isin()・between()・query()での書き換えまでを、実行結果つきで説明します。コードは pandas 3.0.6(PyPIで2026年9月17日公開、2026年10月14日確認)と Python 3.12.15 で実行しました。演算子の仕様はpandas公式ドキュメントのBoolean indexingによります。

AND・OR・NOTの基本:&・|・~

import pandas as pd

df = pd.DataFrame({
    "商品": ["りんご", "バナナ", "みかん", "牛肉", "鶏肉", "キャベツ"],
    "分類": ["果物", "果物", "果物", "肉", "肉", None],
    "価格": [150, 100, 80, 900, 300, 200],
    "在庫": [30, 0, 50, 5, 12, 8],
})

print("AND: 価格が100以上 かつ 在庫が10以上")
print(df[(df["価格"] >= 100) & (df["在庫"] >= 10)])
print("\nOR: 分類が肉 または 在庫が0")
print(df[(df["分類"] == "肉") | (df["在庫"] == 0)])
print("\nNOT: 分類が果物ではない")
print(df[~(df["分類"] == "果物")])
AND: 価格が100以上 かつ 在庫が10以上
    商品  分類   価格  在庫
0  りんご  果物  150  30
4   鶏肉   肉  300  12

OR: 分類が肉 または 在庫が0
    商品  分類   価格  在庫
1  バナナ  果物  100   0
3   牛肉   肉  900   5
4   鶏肉   肉  300  12

NOT: 分類が果物ではない
     商品   分類   価格  在庫
3    牛肉    肉  900   5
4    鶏肉    肉  300  12
5  キャベツ  NaN  200   8
やりたいこと pandasの書き方 意味
AND(かつ) (条件A) & (条件B) 両方Trueの行だけ
OR(または) (条件A) | (条件B) どちらかがTrueの行
NOT(ではない) ~(条件A) Falseの行(条件を反転)
XOR(どちらか一方だけ) (条件A) ^ (条件B) 片方だけTrueの行

条件式はそれぞれ、行ごとにTrue/Falseが並んだSeries(ブールのSeries)になります。& や | はこのSeriesどうしを行ごとに組み合わせ、df[...] はTrueの行だけを返します。

cond = (df["価格"] >= 100) & (df["在庫"] >= 10)
print(cond)
0     True
1    False
2    False
3    False
4     True
5    False
dtype: bool

and・orを使うとエラーになる理由

try:
    df[(df["価格"] >= 100) and (df["在庫"] >= 10)]
except ValueError as e:
    print("ValueError:", e)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Pythonの and・or・not は、値全体を1つのTrue/Falseに変換しようとします。Seriesには「全体としてTrueかどうか」が決められないため、pandasはValueErrorを出します(公式ドキュメントのFAQ)。行ごとに組み合わせたいときは &・|・~ を使ってください。

括弧を忘れると同じエラーになる

try:
    df[df["価格"] >= 100 & df["在庫"] >= 10]
except Exception as e:
    print(type(e).__name__ + ":", e)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

& は >= や == より先に計算されます。そのため括弧がないと、上の式は df["価格"] >= (100 & df["在庫"]) >= 10 と解釈され、比較の連鎖の中で暗黙の and が使われてエラーになります。条件は1つずつ必ず丸括弧で囲む、と覚えておけば迷いません。公式ドキュメントでも、括弧でまとめる必要があると説明されています。

NOT(~)と欠損値(NaN)の注意点

「分類が果物ではない行」を取り出すと、分類が空(NaN)のキャベツも結果に入ります。

print(df[df["分類"] != "果物"])          # != は欠損値の行も残す
print()
print(df[~(df["分類"] == "果物")])       # ~(==) も同じ
print()
print(df[(df["分類"] != "果物") & df["分類"].notna()])  # 欠損値を除く
     商品   分類   価格  在庫
3    牛肉    肉  900   5
4    鶏肉    肉  300  12
5  キャベツ  NaN  200   8

     商品   分類   価格  在庫
3    牛肉    肉  900   5
4    鶏肉    肉  300  12
5  キャベツ  NaN  200   8

   商品 分類   価格  在庫
3  牛肉  肉  900   5
4  鶏肉  肉  300  12

欠損値との == は常にFalseになるので、~ で反転するとTrueになり、NaNの行が残ります。!= も同じ結果です。「果物以外の分類が入っている行」だけがほしいときは、notna() をANDで加えます。

OR条件が多いときは isin()、範囲なら between()

print(df[df["商品"].isin(["りんご", "牛肉", "キャベツ"])])
print()
print(df[~df["商品"].isin(["りんご", "牛肉", "キャベツ"])])   # NOT IN
print()
print(df[df["価格"].between(100, 300)])   # 100以上300以下(両端を含む)
     商品   分類   価格  在庫
0   りんご   果物  150  30
3    牛肉    肉  900   5
5  キャベツ  NaN  200   8

    商品  分類   価格  在庫
1  バナナ  果物  100   0
2  みかん  果物   80  50
4   鶏肉   肉  300  12

     商品   分類   価格  在庫
0   りんご   果物  150  30
1   バナナ   果物  100   0
4    鶏肉    肉  300  12
5  キャベツ  NaN  200   8

同じ列に対する「AまたはBまたはC」は、isin()にリストを渡すと1行で書けます。~ を付ければ「リストに含まれない(NOT IN)」になります。「100以上300以下」のような範囲は between() が便利で、既定では両端を含みます。

query()なら and・or・not がそのまま書ける

print(df.query("価格 >= 100 and 在庫 >= 10"))
print()
print(df.query("分類 == '肉' or 在庫 == 0"))
print()
print(df.query("not 商品 in ['りんご', '牛肉']"))
print()
limit = 200
print(df.query("価格 <= @limit and 分類 == '果物'"))
    商品  分類   価格  在庫
0  りんご  果物  150  30
4   鶏肉   肉  300  12

    商品  分類   価格  在庫
1  バナナ  果物  100   0
3   牛肉   肉  900   5
4   鶏肉   肉  300  12

     商品   分類   価格  在庫
1   バナナ   果物  100   0
2   みかん   果物   80  50
4    鶏肉    肉  300  12
5  キャベツ  NaN  200   8

    商品  分類   価格  在庫
0  りんご  果物  150  30
1  バナナ  果物  100   0
2  みかん  果物   80  50

DataFrame.query() の文字列の中では、and・or・not や in を使えます(&・| も可)。Pythonの変数は @limit のように @ を付けて参照します。列名を何度も書かなくてよいので、条件が長くなるときに読みやすくなります。詳しくはPandas DataFrame query()メソッドで条件抽出を簡単にで解説しています。

文字列を含む・含まないで絞る

print(df[df["商品"].str.contains("肉")])
print()
print(df[~df["分類"].str.contains("果", na=False)])   # 欠損値をFalse扱いにしてから反転
   商品 分類   価格  在庫
3  牛肉  肉  900   5
4  鶏肉  肉  300  12

     商品   分類   価格  在庫
3    牛肉    肉  900   5
4    鶏肉    肉  300  12
5  キャベツ  NaN  200   8

部分一致は str.contains() を使います。欠損値がある列で ~ を付けるときは、na=False を指定して欠損値をFalseにしてから反転すると、エラーや意図しない結果を避けられます。上の例では、分類に「果」を含まない行として、肉の2行と分類が空のキャベツが残りました。

条件が多いとき・抽出した行に値を入れるとき

import numpy as np
conds = [df["価格"] >= 100, df["在庫"] > 0, df["分類"].notna()]
print(df[np.logical_and.reduce(conds)])
print()
print(df[(df["価格"] >= 200) ^ (df["在庫"] >= 10)])   # どちらか一方だけ(XOR)
print()
df.loc[(df["在庫"] < 10) & (df["分類"] == "肉"), "発注"] = "要"
print(df)
    商品  分類   価格  在庫
0  りんご  果物  150  30
3   牛肉   肉  900   5
4   鶏肉   肉  300  12

     商品   分類   価格  在庫
0   りんご   果物  150  30
2   みかん   果物   80  50
3    牛肉    肉  900   5
5  キャベツ  NaN  200   8

     商品   分類   価格  在庫   発注
0   りんご   果物  150  30  NaN
1   バナナ   果物  100   0  NaN
2   みかん   果物   80  50  NaN
3    牛肉    肉  900   5    要
4    鶏肉    肉  300  12  NaN
5  キャベツ  NaN  200   8  NaN
  • 条件が3つ以上あるなら、リストにまとめて np.logical_and.reduce()(ORなら np.logical_or.reduce())で一度に結合できます。
  • ^ は「どちらか一方だけ」を表すXORです。
  • 条件に合う行の列に値を代入するときは、df.loc[条件, "列名"] = 値 と書きます。df[条件]["列名"] = 値 のような連鎖した代入は、pandas 3.0ではもとのDataFrameに反映されず、ChainedAssignmentError という警告が出ます(公式ドキュメントのCopy-on-Write)。

書き方の早見表

やりたいこと ブールインデックス query()
AかつB df[(A) & (B)] df.query("A and B")
AまたはB df[(A) | (B)] df.query("A or B")
Aではない df[~(A)] df.query("not A")
リストのどれか df[df["列"].isin(リスト)] df.query("列 in @リスト")
リストのどれでもない df[~df["列"].isin(リスト)] df.query("列 not in @リスト")

行の取り出し方全般はpandasの使い方を初心者向けに解説、列名で列を選ぶ方法はPandas filter()の記事も参考にしてください。

よくある質問

Q. pandasでOR条件はどう書きますか?

縦棒 | を使い、条件をそれぞれ丸括弧で囲んで df[(df[“A”] == 1) | (df[“B”] > 10)] のように書きます。同じ列で値の候補が複数あるなら df[df[“A”].isin([1, 2, 3])] のほうが簡潔です。

Q. pandasでNOT条件(〜ではない)はどう書きますか?

チルダ ~ を条件の前に付けて df[~(df[“A”] == 1)] と書きます。リストに含まれない行は df[~df[“A”].isin(リスト)] です。欠損値の行も残る点に注意してください。

Q. andやorを使うと「The truth value of a Series is ambiguous」と出るのはなぜですか?

Pythonの and・or・not はSeries全体を1つのTrue/Falseに変換しようとし、pandasはそれを決められないためValueErrorを出します。行ごとに組み合わせるには &・|・~ を使います。

Q. & を使ったのにエラーになります。

条件を丸括弧で囲んでいない可能性があります。& は比較演算子より先に計算されるため、(df[“A”] > 1) & (df[“B”] < 5) のように条件ごとに括弧が必要です。

Q. query()で and・or は使えますか?

使えます。df.query(“価格 >= 100 and 在庫 >= 10”) のように文字列の中に書きます。not や in も使え、Pythonの変数は @変数名 で参照します。

まとめ

pandasの複数条件は、AND=&、OR=|、NOT=~ で組み合わせ、条件ごとに丸括弧で囲むのが基本です。and・orを使いたいときは query() に切り替え、同じ列の候補が多いときは isin()、範囲なら between() を使うと短く書けます。NOTで反転すると欠損値の行も残ることだけは、結果を見て確かめる習慣をつけておきましょう。

pandas|抽出・選択の関連記事

loc・ilocや条件指定で、必要な行・列だけを取り出す操作の記事です。

pandas全体の使い方は「pandasの使い方を初心者向けに解説」でまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら