pandas query 用日期字符串筛选,结果意外为空
日期列直接比较字符串能匹配,query 却返回空表时,检查字符串比较被改写为成员测试的路径。先构造类型明确的 Timestamp,通过 @ 变量传入并保持时区一致。
df[df["d"] == "2026-06-16"] 能找到记录,df.query("d == '2026-06-16'") 却返回空表。若 d 确实是 pandas 日期时间列,应检查 query 对字符串比较的改写,不要先把所有日期列转回文本。
pandas 3.0.6 有这组明确差异。受影响路径会把与字符串的等值比较改成类似 isin 的成员测试,日期列在这条路径中不会按普通标量比较的同样方式解析日期字符串。错误没有抛出异常,空结果也就容易被误认成“当天没有数据”。
让右侧先成为明确的日期时间标量
对无时区的日期时间列,可以先构造 Timestamp,再通过 query 的局部变量机制传入:
import pandas as pd
cutoff = pd.Timestamp("2026-06-16")
matched = df.query("d == @cutoff")
df 和列名使用实际对象。这里比较的是恰好等于当天零点,不是“这一天的所有时间”。列中有时分秒时,应按业务定义构造开始与结束边界,并用半开区间筛选;不要因为空结果就把等值条件偷换成整天范围。
有时区的列应使用匹配的时区语义。先确定输入代表 UTC、当地墙上时间还是已有带偏移时间,再生成对应标量;给无时区字符串随意贴上时区,可能改变真实瞬间。
对于固定的简单比较,直接使用布尔掩码也能明确执行日期标量比较。无论选哪种写法,都保留原索引与 dtype,不必为了这条故障把列转换成字符串,再承担排序与缺失语义的变化。
切换执行引擎未必改变解析
engine="python" 不会跳过 query 前面的表达式解析与改写,所以它不是本题的可靠规避。将微秒时间单位改成纳秒也没有处理字符串被改写的原因;不应靠反复更换 dtype 碰到一个看似正常的结果。
pandas 3.1.0 的字符串比较也仍有这项改写路径。即使换了版本,仍应将 query 与直接的日期标量比较作对照,不要仅凭空表没有抛错就接受结果。
query 只适合受信任的表达式。不要把用户输入用 f-string 拼进 query 字符串;把值作为局部变量传递,也不意味着可以接收任意外部表达式执行。
验收将 query 结果与同一 Timestamp 的布尔掩码逐行对照,覆盖匹配、不匹配、NaT 与实际时区。历史空结果若已经驱动了“无需处理”或增量水位线更新,应重新核对相应区间,修正表达式不会自动补跑此前跳过的任务。


