美文网首页Python数据分析
Python 数据处理(二十六)—— 索引和选择之 query

Python 数据处理(二十六)—— 索引和选择之 query

作者: 名本无名 | 来源:发表于2021-03-03 21:01 被阅读0次

    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
    

    如果 MultiIndexlevel 没有命名,你可以使用特殊的名称来引用它们

    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 的第 1level

    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() 还支持 Pythoninnot 比较运算符的使用,是 SeriesDataFrameisin 方法的一个简单替代

    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 不支持 innot 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 在大数据框的查询上速度更快一些

    image.png

    相关文章

      网友评论

        本文标题:Python 数据处理(二十六)—— 索引和选择之 query

        本文链接:https://www.haomeiwen.com/subject/kducqltx.html