DataFrame.query(expr, inplace=False, **kwargs)
其中的参数:
expr:str,要计算的查询字符串
inplace,bool,查询结果是就地修改数据还是返回修改后的副本
**kwargs
:pandas.eval() 支持的关键字参数,可用于查询,完整详细信息,请参阅 pandas.eval()。
DataFrame:由提供的查询表达式生成的 DataFrame
None:如果 inplace=True,但原数据被就地修改
表达式字符串
在 query 方法中最重要的参数就是 expr,它是一个字符串,这个字符串是合法的 Python 逻辑表达式语句,变量会认为是 DataFrame 的列名。支持的功能点有:
可引用变量,方法是在变量前面加上“@”字符,如 @a+b,a 中字符串外定义的变量
可以用反引号( backticks,一般在键盘左上侧) 来使用不是 Python 有效的变量名,如数字开头、Python 关键字、有空格、包含特殊字符 Area (cm^2)
此方法使用顶级 pd.eval()
函数计算传递的查询,表达式的计算结果首先传递给 DataFrame.loc
,如果由于多维键(例如 DataFrame)而失败,则结果将传递给DataFrame.__getitem__()
。
默认情况下,query() 方法使用略微修改的 Python语 法。例如,& 和 |(按位)运算符近似表达 and 和 or,这在语法上是有效的 Python,但是语义不同。可以通过传递关键字参数 parser='python' (这是 pd.eval 的参数,默认是 parser='pandas' )来更改表达式的语义。这将强制执行与 Python 中相同的语义。同样,您可以传递 engine='python' 以使用 python 本身作为后端计算表达式。不建议这样做,因为与使用 numexpr 作为引擎相比,这样做效率低下。
默认情况下,DataFrame.index 和 DataFrame.columns 属性放置在查询命名空间中,这允许您将框架的索引和列视为框架中的一列。标识符索引(index)用于框架索引,您还可以使用索引的名称在查询中标识它。请注意,Python 关键字不能用作标识符。
支持以下算术运算:+
, -
, *
,/
, **
, %
, //
(仅限 python 引擎)以及以下布尔运算: |
(or), &
(and), 和 ~
(not)。此外,“pandas”解析器允许使用 and、or 和 not,其语义与相应的位运算符不同。详情 pandas.eval()。
更多细节可以参考 Python 的词法分析 (https://docs.python.org/3/reference/lexical_analysis.html) 同时结合 pandas.core.computation.parsing
的源码。
根据以上的功能描述,以下是一些用法的示例:
# pure python
df[(df['a'] < df['b']) & (df['b'] < df['c'])]
# query
df.query('(a < b) & (b < c)')
# 索引取名后可直接用于表达式
df.index.name = 'x'
df.query('x < b and b < c')
# 不命名,可用 index
df.query('index < b < c')
# 列名的命名空间下的方法(需返回布尔序列)可以使用
df.query('col.str.contains("am")') # 包含 am 字符
df.query('col.str.len() > 2') # 长度大于 2
注:如果索引的名称与列名重叠,则列名优先,这时可以使用 index 代表索引,如果不幸列名有个也叫 index,这时可以用 ilevel_0
:
df.query('ilevel_0 == "2022-01-01"')
对于多层索引:
# 索引名的命名空间,color 是其中一层索引的名称
df.query('color == "red"')
# 如果索引没有名字,可用 ilevel_<n> 指示索级别
df.query('ilevel_0 == "red"')
约定为 ilevel_0,表示索引第 0 级的 “索引级别0”。
当您拥有一个 DataFrame 对象集合时,这些对象有一个共同的列名子集(或索引级别/名称)。您可以将相同的查询传递给两个 DataFrame:
expr = '0.0 <= a <= c <= 0.5'
map(lambda frame: frame.query(expr), [df, df2])
pandas 和 Python 表达式的对比:
df.query('(a < b) & (b < c)')
df[(df['a'] < df['b']) & (df['b'] < df['c'])]
# 通过删除括号(比较运算符绑定得比&和|)稍微好一些:
df.query('a < b & b < c')
# 用英语单词代替
df.query('a < b and b < c')
# 继续简化,更加自然
df.query('a < b < c')
in 和not in 运算:
# a 列中有 b 列内容的
df.query('a in b')
# Python 方法,同上
df[df['a'].isin(df['b'])]
df.query('a not in b')
# Python 方法
df[~df['a'].isin(df['b'])]
您可以将其与其他表达式结合使用,以实现非常简洁的查询:
# 列 a 和列 b 具有重叠值的行并且c列的值小于d列的值
df.query('a in b and c < d')
# 纯Python
df[df['b'].isin(df['a']) & (df['c'] < df['d'])]
请注意,in 和 not in 是在 Python 中计算的,因为 numexpr 没有此操作的等效项。但是,只有 in/not-in 表达式本身在普通 Python 中进行计算。例如,在表达式中 df.query('a in b + c + d')
(b+c+d)由 numexpr 计算,然后在普通 Python 中计算 in 操作。通常,任何可以使用 numexpr 进行评估的操作都将被删除。
==
运算符在列表对象中的特殊用法(使用 ==/!= 将值列表与列进行比较工作原理与 in/not in 类似):
df.query('b == ["a", "b", "c"]')
# pure Python
df[df['b'].isin(["a", "b", "c"])]
df.query('c == [1, 2]')
df.query('c != [1, 2]')
# using in/not in
df.query('[1, 2] in c')
df.query('[1, 2] not in c')
# pure Python
df[df['c'].isin([1, 2])]
布尔运算,同时可以使用 not 或~运算符对布尔表达式求反:
df['bools'] = ser > 0.5
df.query('~bools')
df.query('not bools')
df.query('not bools') == df[~df['bools']]
当然,表达式也可以任意复杂:
# 短查询语法
shorter = df.query('a < b < c and (not bools) or bools > 2')
# 纯 Python 中的相同的操作
longer = df[(df['a'] < df['b'])
& (df['b'] < df['c'])
& (~df['bools'])
| (df['bools'] > 2)]
总之,字符串表达式最终的计算结果需要是一个原同长度数据索引的布尔序列,如果是一个聚合值则会报错,如:
# 正确,查询 b 列为偶数的行
df.query('b % 2 == 0')
# 筛选出 b 列大于该列平均值的行
df.query('b > b.mean()')
# 公式计算结果为标量 True,报错:KeyError: True
df.query('b.any()')
关于 query() 的性能。
DataFrame.query() 对于大的 DataFrame,使用 numexpr 比 Python 稍微快一点。如果帧的行数超过大约200000行,则使用 query() 我们可以看到将 numexpr 引擎与 DataFrame 一起使用的性能优势。
此绘图是使用 DataFrame 创建的,DataFrame 有3列,每列包含使用numpy生成的浮点值,使用 numpy.random.randn() 生成。
df.query 与 df.eval 区别
df.query() 与 df.eval() 都可以用字符串表达式来对原数据做计算,区别主要有:
df.query() 是数据查询,返回的符合条件的整行
df.eval() 是计算,根据表达式计算出任意结果
表达式返回值要求不同