query() 仅在大数据量(如百万行以上)且多动态条件时性能更优,小数据或简单条件反不如布尔索引;需注意空格列名用反引号、变量注入用@符号、and/or优先级高于&/|、in操作有长度限制及none处理缺陷。

query() 在复杂查询时确实更快、更清晰,但前提是数据量够大、条件够多、变量够动态——小数据或单条件反而可能拖慢速度。
query() 的性能优势只在特定规模才生效
底层用 numexpr 加速,对百万级行以上的 DataFrame 才明显快于布尔索引;10 万行以内,传统 df[mask1 & mask2] 往往更快。这不是 bug,是设计使然:numexpr 启动解析开销固定,小数据摊不上。
实际判断建议:
- 行数 query()
- 行数 > 20 万 且条件 ≥ 3 个 →
query()启动收益 - 含字符串比较(如
name.str.contains())或正则 →query()无法直接调用这些方法,得回退到布尔索引
列名含空格、特殊字符或变量注入必须加反引号或 @ 符号
写 df.query('user name == "Alice"') 会报 KeyError: 'user' —— 因为空格被当成分隔符。正确写法是 df.query('`user name` == "Alice"')。
同理,动态传参不能拼字符串,否则有注入风险:
- ❌ 错误:
df.query(f'age > {min_age}') - ✅ 正确:
df.query('age > @min_age')(@min_age自动解析作用域内变量) - ⚠️ 注意:
@只认局部/全局变量,不认函数返回值或表达式,比如@max(val_list)会失败
and/or/&/| 的优先级和可读性差异很大
query() 支持英文关键字 and/or,也支持符号 &/|,但行为不同:
-
age > 18 and city == "北京"→ 可读性强,推荐用于人工维护场景 -
age > 18 & city == "北京"→ 实际执行等价于age > (18 & city) == "北京",因&优先级高于>,必须加括号:(age > 18) & (city == "北京") - 混用(如
age > 18 and city == "北京" | vip == True)极易出错,逻辑歧义难调试
in / not in 和列表变量的边界情况
query() 支持 col in ["a", "b"],但注意:
- 列表长度超过约 1000 项时,
query()解析变慢,不如先转成pd.Categorical或用isin() -
not in不支持None值判断,col not in [1, 2, None]会静默跳过None行,实际应拆成col not in [1, 2] and col.notna() - 想查“字段是否为空字符串”,不能写
text == "",得用text.str.len() == 0→ 这种就得切回布尔索引
真正卡住人的从来不是语法,而是当你要把一个带函数调用、嵌套条件、动态参数、空值混合的筛选逻辑硬塞进字符串表达式时——query() 的边界就露出来了。这时候别硬扛,该换 loc 就换,该拆链式就拆链式。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











