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