HOWTO · Pandas
pandasで列の値に基づいてDataFrameの行を絞り込む
pandasのDataFrameをブールマスク、loc、isin、between、文字列・欠損値の判定、queryで絞り込む方法を説明します。
このページの内容
行の絞り込みでは、各行に1つのブール値を作り、Trueの行だけを残します。基本形はdf.loc[条件]です。第2引数を使えば出力列も同時に指定できます。
サンプルDataFrameを作成する
例の表には文字列、数値、日付、欠損した商品名があります。以降はordersを再利用します。元のインデックスは保持され、必要な場合だけ.reset_index(drop=True)で振り直します。
import pandas as pd
orders = pd.DataFrame(
{
"order_id": [101, 102, 103, 104, 105, 106],
"region": ["East", "West", "East", "North", "West", "East"],
"product": ["Desk", "Chair", "Desk lamp", "Desk", None, "Chair"],
"sales": [450, 275, 125, 700, 320, 410],
"status": ["paid", "pending", "paid", "paid", "cancelled", "pending"],
"ordered_at": pd.to_datetime(["2026-08-01", "2026-08-03", "2026-08-08", "2026-08-11", "2026-08-14", "2026-08-18"]),
}
)
1つの列値で行を絞り込む
Seriesとの比較がマスクを作ります。単純な角括弧でも同じ行を選べますが、.locなら列も明示できます。除外には!=、欠損値にはisna()とnotna()を使います。
east_orders = orders.loc[orders["region"] == "East"]
print(east_orders[["order_id", "region", "sales"]].to_string(index=False))
出力:
order_id region sales
101 East 450
103 East 125
106 East 410
large_orders = orders.loc[orders["sales"] >= 400, ["order_id", "sales"]]
print(large_orders.to_string(index=False))
出力:
order_id sales
101 450
104 700
106 410
複数の条件を組み合わせる
要素単位の条件は&、|、~で結び、各比較を括弧で囲みます。Pythonのandとorは単一の真偽値を要求するため、Seriesでは真偽値が曖昧というエラーになります。
mask = (orders["region"] == "East") & (orders["sales"] >= 400)
result = orders.loc[mask, ["order_id", "region", "sales"]]
print(result.to_string(index=False))
出力:
order_id region sales
101 East 450
106 East 410
mask = (orders["status"] == "paid") | (orders["sales"] > 600)
result = orders.loc[mask, ["order_id", "status", "sales"]]
# Raises ValueError: The truth value of a Series is ambiguous.
orders.loc[(orders["region"] == "East") and (orders["sales"] >= 400)]
値のリストで絞り込む
Series.isin()は多数の等価比較を置き換えます。単一の文字列ではなく、リストなどのリスト状コレクションを渡します。除外する場合はisin()の結果全体に~を付けます。
allowed = ["East", "West"]
result = orders.loc[orders["region"].isin(allowed), ["order_id", "region"]]
print(result.to_string(index=False))
出力:
order_id region
101 East
102 West
103 East
105 West
106 East
excluded = ["cancelled", "pending"]
paid_only = orders.loc[~orders["status"].isin(excluded)]
範囲と日付で絞り込む
between()は既定で両端を含みます。pd.to_datetime()で変換した日付にも使えます。タイムゾーン付きデータでは境界も互換性のあるタイムゾーンにします。
mid_value = orders.loc[orders["sales"].between(300, 500), ["order_id", "sales"]]
print(mid_value.to_string(index=False))
出力:
order_id sales
101 450
105 320
106 410
date_mask = orders["ordered_at"].between("2026-08-03", "2026-08-14")
result = orders.loc[date_mask, ["order_id", "ordered_at"]]
欠損値または非欠損値で絞り込む
isna()はNone、NaN、NaTを選び、notna()は値がある行を選びます。空文字列は自動的には欠損値にならないため、別に正規化または判定します。
missing_product = orders.loc[orders["product"].isna(), ["order_id", "product"]]
present_product = orders.loc[orders["product"].notna()]
print(missing_product.to_string(index=False))
出力:
order_id product
105 None
文字列の値で絞り込む
str.contains()はベクトル化された文字列マスクを作ります。regex=Falseはリテラル検索、case=Falseは大文字小文字を無視し、na=Falseは欠損文字列を非一致にします。
has_desk = orders["product"].str.contains("desk", case=False, regex=False, na=False)
result = orders.loc[has_desk, ["order_id", "product"]]
print(result.to_string(index=False))
出力:
order_id product
101 Desk
103 Desk lamp
104 Desk
読みやすい式にDataFrame.query()を使う
query()では@でローカル変数を参照し、空白を含む列名はバッククォートで囲みます。信頼できない入力を式へ連結しないでください。評価時にコードを実行できるためです。
minimum_sales = 400
result = orders.query("region == 'East' and sales >= @minimum_sales")
print(result[["order_id", "region", "sales"]].to_string(index=False))
出力:
order_id region sales
101 East 450
106 East 410
.loc、角括弧、query()を使い分ける
一般には.loc、行だけの短い選択には角括弧、管理された式が読みやすい場合にquery()を使います。独立した結果を変更する前には.copy()を呼びます。
east_orders = orders.loc[orders["region"] == "East"].copy()
east_orders["sales_with_tax"] = east_orders["sales"] * 1.2
インデックスの整合とデータ型の問題を避ける
マスクはインデックスラベルで整合されるため、同じDataFrameから作ります。数値や日付の文字列は比較前に変換し、業務規則が必要とするときだけ表記を正規化します。
ブールマスクを再利用して検証する
名前付きマスクは件数確認やテストができます。一致がなくても有効な空のDataFrameが返ります。DataFrame.filter()はセル値ではなく軸ラベルを選ぶ別のAPIです。
元データを変更せずに絞り込む
元の表を更新するなら元DataFrameへ.locで代入し、別の作業表が必要ならコピーします。これにより連鎖インデックスや不明確なメモリ共有に依存しません。
DataFrame.loc, Series.isin, Series.between, Series.str.contains, DataFrame.query