跳到主要内容

DataFrame 查询

在 Pandas 中,筛选条件本质上是一个与索引对齐的布尔 Series

import pandas as pd

orders = pd.DataFrame({
"customer_id": ["C1", "C2", None],
"amount": [120, 80, 200],
"status": ["paid", "shipped", "pending"],
"created_at": pd.to_datetime(["2026-01-02", "2026-03-31 12:00", "2026-04-01"], format="mixed"),
})
mask = orders["amount"].ge(100) & orders["status"].isin(["paid", "shipped"])
large_orders = orders.loc[mask, ["customer_id", "amount", "status"]]

谓词编写规范

  • 括号是必须的:使用 &|~ 组合多个条件时,务必给每个比较表达式加上括号,因为运算符优先级容易出错。
  • 专用方法:成员判断用 isin,闭区间范围用 between,缺失值检测用 isna/notna
  • 类型访问器:针对字符串、日期时间和分类数据,分别使用 .str.dt.cat 访问器进行类型化操作。
  • 索引对齐陷阱:如果掩码(mask)是从其他对象构建的,务必检查其索引。索引对齐机制可能会意外改变最终选中的行。
recent = orders.loc[
orders["created_at"].ge("2026-01-01") & orders["created_at"].lt("2026-04-01")
]
missing_customer = orders.loc[orders["customer_id"].isna()]

query 方法

DataFrame.query 能让复杂的分析表达式更易读:

threshold = 100
result = orders.query("amount >= @threshold and status == 'paid'")

何时不用 query

  • 列名包含特殊字符或难以阅读时。
  • 谓词需要动态构建时。
  • 你更倾向于使用标准 Python 调试工具时。

安全警告:切勿将来自不可信输入的字符串直接拼接进 query 中,它不是安全边界,存在注入风险。

筛选(Filter)与选择(Select)

  • 筛选:通过谓词选择行。
  • .loc:可以同时指定行和列(基于标签)。
  • .iloc:基于位置索引选择行和列。

保持“筛选”和“选择”这两个概念在逻辑上的分离,能有效避免许多因形状(shape)或标签(label)不匹配导致的错误。

核对选中的行

这里 large_orders 的形状为 (1, 3),保留第 0 行;result 也保留第 0 行,但保留全部四列。recent 保留第 0、1 行。日期采用左闭右开区间,因此包含 3 月 31 日全天;上界若写成 "2026-03-31",实际上是当天零点,会漏掉下午的记录。

布尔索引把可空布尔掩码中的缺失项视为假。只有需要保留判断结果未知的行时,才使用 mask.fillna(True)。Python 的 and/or 不能逐元素组合 Series,应使用 &/|DataFrame.filter 虽然叫 filter,却按轴标签筛选,不按单元格值筛选行。query 能执行任意代码,不能接收不可信表达式。

参考资料

探索关联打开关联网络