eval和query比布尔索引快,因其底层调用numexpr库,支持内存对齐、延迟计算与多线程,尤其在千万行以上数据中快2–5倍;但含str.contains等python函数时会退化为解释器执行,丧失优势。

eval 和 query 为什么比普通布尔索引快?
因为它们在底层使用 numexpr 库,能自动做内存对齐、延迟计算和多线程运算,尤其在大 DataFrame(千万行以上)上,query 常比 df[df.a > 1 & df.b == 'x'] 快 2–5 倍。但前提是表达式不含 Python 函数调用——一旦出现 str.contains 或 datetime.year,就会退回到 Python 解释器执行,性能优势消失。
query 方法的正确写法和常见翻车点
query 接收字符串表达式,变量需加 @ 前缀,列名直接写,不支持点号访问嵌套属性(如 df.timestamp.dt.year 不行)。错误示例:df.query('col1 > threshold') 会报 NameError: name 'threshold' is not defined,必须写成 df.query('col1 > @threshold')。
- 支持
in语法:df.query("category in @valid_list"),但valid_list必须是 Python list/tuple,不能是 pd.Series - 字符串匹配要用单引号包裹值:
df.query("name == 'Alice'"),双引号里再套双引号会解析失败 - 空值判断写
col != col(利用 NaN != NaN 为 True),不要用col.isnull() - 不支持
and/or,必须用&/|,且每段条件要加括号:df.query('(a > 1) & (b in @xs)')
eval 能做什么、不能做什么
eval 主要用于生成新列或中间布尔数组,比如 df.eval('flag = (a + b) / c > 0.5');它比 df.assign(flag=...) 内存更省。但它**不能直接筛选行**——没有 inplace 或返回视图的能力,想筛选还得配合布尔索引:df[df.eval('a > b & c 。这时其实不如直接用 <code>query 简洁。
- 支持局部变量插值:
df.eval('a * @multiplier + @offset') - 不支持方法链式调用:
df.col.str.upper()会报错,只能提前算好列再传入 - 数值计算中自动启用浮点向量化,但
astype、fillna等操作仍需走 pandas 原生路径 - 若表达式含未定义列名,错误信息是
UndefinedVariableError,不是 KeyError
什么时候该放弃 eval/query?
当逻辑涉及自定义函数、正则提取、时区转换、分组内计算(如 rank())、或需要短路求值(比如先判空再取属性)时,query 和 eval 都无能为力。硬套会导致 NotImplementedError 或静默结果错误。这时候老实用 loc + apply 或预处理列——宁可慢一点,也不能错。
另外,DataFrame 列名含空格或特殊字符(如 'user id')时,query 必须用反引号:df.query("`user id` > 100"),漏掉就 SyntaxError;这个细节容易被忽略,一跑就崩。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











