HOWTO · Pandas
依欄值篩選 pandas DataFrame 資料列
使用布林遮罩、loc、isin、between、文字與遺漏值檢查或 query,依欄值篩選 pandas DataFrame 資料列。
本頁內容
篩選資料列就是為每列產生一個布林值,並保留值為 True 的資料列。通用寫法是 df.loc[條件];第二個參數也能同時指定輸出欄。
建立範例 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"]),
}
)
依單一欄值篩選資料列
將 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() 篩選軸標籤,而非依儲存格值篩選資料列。
篩選時不變更來源資料
若要修改原表,應透過原 DataFrame 的 .loc 指派;若要建立獨立工作集,則使用複本。如此不會依賴鏈式索引或不確定的記憶體共享。
DataFrame.loc, Series.isin, Series.between, Series.str.contains, DataFrame.query