19 query() 方法
DataFrame
对象有一个 query()
方法,它允许使用表达式进行选择。
例如,想要后去 b
列值位于 a
列 和 c
列之间的行
In [215]: n = 10
In [216]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [217]: df
Out[217]:
a b c
0 0.438921 0.118680 0.863670
1 0.138138 0.577363 0.686602
2 0.595307 0.564592 0.520630
3 0.913052 0.926075 0.616184
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
6 0.792342 0.216974 0.564056
7 0.397890 0.454131 0.915716
8 0.074315 0.437913 0.019794
9 0.559209 0.502065 0.026437
# pure python
In [218]: df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Out[218]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
# query
In [219]: df.query('(a < b) & (b < c)')
Out[219]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
如果不存在名为 a
的列,则会使用名为 a
的命名索引
In [220]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [221]: df.index.name = 'a'
In [222]: df
Out[222]:
b c
a
0 0 4
1 0 1
2 3 4
3 4 3
4 1 4
5 0 3
6 0 1
7 3 4
8 2 3
9 1 1
In [223]: df.query('a < b and b < c')
Out[223]:
b c
a
2 3 4
如果你不想或不能命名索引,你可以在查询表达式中使用 index
指代
In [224]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [225]: df
Out[225]:
b c
0 3 1
1 3 0
2 5 6
3 5 2
4 7 4
5 0 1
6 2 5
7 0 1
8 6 0
9 7 9
In [226]: df.query('index < b < c')
Out[226]:
b c
2 5 6
注意:
如果索引的名称与列名重叠,则列名优先。例如
In [227]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [228]: df.index.name = 'a'
In [229]: df.query('a > 2') # 使用列而不是索引
Out[229]:
a
a
1 3
3 3
你仍然可以使用 index
来引用索引
In [230]: df.query('index > 2')
Out[230]:
a
a
3 3
4 2
如果出于某些原因,您有一个名为 index
的列,那么您也可以使用 ilevel_0
来引用索引,但此时您应该考虑将您的列重命名为不那么模糊的名称
19.1 MultiIndex query() 语法
对包含多级索引的 DataFrame
查询,可以直接使用 level
名称,就像使用列名一样方便
In [231]: n = 10
In [232]: colors = np.random.choice(['red', 'green'], size=n)
In [233]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [234]: colors
Out[234]:
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
'green', 'green'], dtype='<U5')
In [235]: foods
Out[235]:
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
'eggs'], dtype='<U4')
In [236]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [237]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [238]: df
Out[238]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [239]: df.query('color == "red"')
Out[239]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
如果 MultiIndex
的 level
没有命名,你可以使用特殊的名称来引用它们
In [240]: df.index.names = [None, None]
In [241]: df
Out[241]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [242]: df.query('ilevel_0 == "red"')
Out[242]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
通常 ilevel_0
的意思是索引级别为 0
,代表 index
的第 1
个 level
19.2 query() 示例
当你有多个 DataFrame
对象,且它们之间存在共同的列(或索引),你可以将相同的查询同时应用于这些对象,而不需要指定一一指定
In [243]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [244]: df
Out[244]:
a b c
0 0.224283 0.736107 0.139168
1 0.302827 0.657803 0.713897
2 0.611185 0.136624 0.984960
3 0.195246 0.123436 0.627712
4 0.618673 0.371660 0.047902
5 0.480088 0.062993 0.185760
6 0.568018 0.483467 0.445289
7 0.309040 0.274580 0.587101
8 0.258993 0.477769 0.370255
9 0.550459 0.840870 0.304611
In [245]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [246]: df2
Out[246]:
a b c
0 0.357579 0.229800 0.596001
1 0.309059 0.957923 0.965663
2 0.123102 0.336914 0.318616
3 0.526506 0.323321 0.860813
4 0.518736 0.486514 0.384724
5 0.190804 0.505723 0.614533
6 0.891939 0.623977 0.676639
7 0.480559 0.378528 0.460858
8 0.420223 0.136404 0.141295
9 0.732206 0.419540 0.604675
10 0.604466 0.848974 0.896165
11 0.589168 0.920046 0.732716
In [247]: expr = '0.0 <= a <= c <= 0.5'
In [248]: map(lambda frame: frame.query(expr), [df, df2])
Out[248]: <map at 0x7f7110fdd910>
19.3 query() 的 Python 和 pandas 语法比较
类似 numpy
的语法
In [249]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [250]: df
Out[250]:
a b c
0 7 8 9
1 1 0 7
2 2 7 2
3 6 2 2
4 2 6 3
5 3 8 2
6 1 7 2
7 5 1 5
8 9 8 0
9 1 5 0
In [251]: df.query('(a < b) & (b < c)')
Out[251]:
a b c
0 7 8 9
In [252]: df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Out[252]:
a b c
0 7 8 9
去掉括号会更简单一些
In [253]: df.query('a < b & b < c')
Out[253]:
a b c
0 7 8 9
使用英语替代符号
In [254]: df.query('a < b and b < c')
Out[254]:
a b c
0 7 8 9
更简洁的方式是
In [255]: df.query('a < b < c')
Out[255]:
a b c
0 7 8 9
19.4 in 和 not in 操作
query()
还支持 Python
的 in
和 not
比较运算符的使用,是 Series
或 DataFrame
的 isin
方法的一个简单替代
In [256]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....: 'c': np.random.randint(5, size=12),
.....: 'd': np.random.randint(9, size=12)})
.....:
In [257]: df
Out[257]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [258]: df.query('a in b')
Out[258]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
# 使用纯 Python 语法
In [259]: df[df['a'].isin(df['b'])]
Out[259]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
In [260]: df.query('a not in b')
Out[260]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# 纯 Python
In [261]: df[~df['a'].isin(df['b'])]
Out[261]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
可以将这一语法与其他表达式结合起来,组合成更复杂的查询
# 提取 a 列的值在 b 列出现,且 c 列的值小于 d 列的值的行
In [262]: df.query('a in b and c < d')
Out[262]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
# 纯 Python
In [263]: df[df['b'].isin(df['a']) & (df['c'] < df['d'])]
Out[263]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
10 f c 0 6
11 f c 1 2
注意:
因为 numexpr
不支持 in
和 not in
操作,所以会被 Python
执行。例如
df.query('a in b + c + d')
(b + c + d)
会被 numexpr
执行,而 in
操作是被 Python
执行。通常其他任何操作都可以使用 numexpr
执行
19.5 == 和 list 的使用
使用 ==
和 !=
对比列和值列表的方式与 in/not in
类似
In [264]: df.query('b == ["a", "b", "c"]')
Out[264]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [265]: df[df['b'].isin(["a", "b", "c"])]
Out[265]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [266]: df.query('c == [1, 2]')
Out[266]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [267]: df.query('c != [1, 2]')
Out[267]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# using in/not in
In [268]: df.query('[1, 2] in c')
Out[268]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [269]: df.query('[1, 2] not in c')
Out[269]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# pure Python
In [270]: df[df['c'].isin([1, 2])]
Out[270]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
19.6 布尔操作
可以用 not
或 ~
运算符对布尔表达式取反
In [271]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [272]: df['bools'] = np.random.rand(len(df)) > 0.5
In [273]: df.query('~bools')
Out[273]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [274]: df.query('not bools')
Out[274]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [275]: df.query('not bools') == df[~df['bools']]
Out[275]:
a b c bools
2 True True True True
7 True True True True
8 True True True True
当然,表达式也可以任意的复杂的组合
# 简洁的 query 语法
In [276]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# 纯 Python 实现
In [277]: longer = df[(df['a'] < df['b'])
.....: & (df['b'] < df['c'])
.....: & (~df['bools'])
.....: | (df['bools'] > 2)]
.....:
In [278]: shorter
Out[278]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [279]: longer
Out[279]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [280]: shorter == longer
Out[280]:
a b c bools
7 True True True True
19.7 query() 的性能
query
使用 numexpr
会比纯 Python
在大数据框的查询上速度更快一些
网友评论