比 'query' 到 select MultiIndexed 行更方便的方法,通过索引级别的名称指定部分标签?

A more convenient way, than 'query', to select MultiIndexed rows, designating partial labels by the name of their index levels?

假设您有很多命名的索引级别——我将在此处显示 4 个,但发挥您的想象力:

midx = pd.MultiIndex.from_product([['A0','A1'], ['B0','B1'],['C0','C1'],['D0','D1']],names=['quack','woof','honk','snarf'])
dfmi = pd.DataFrame(np.arange(32).reshape((len(midx), len(columns))),index=midx, columns=columns)
dfmi
                       foo  bar
quack woof honk snarf          
A0    B0   C0   D0       0    1
                D1       2    3
           C1   D0       4    5
                D1       6    7
      B1   C0   D0       8    9
                D1      10   11
           C1   D0      12   13
                D1      14   15
A1    B0   C0   D0      16   17
                D1      18   19
           C1   D0      20   21
                D1      22   23
      B1   C0   D0      24   25
                D1      26   27
           C1   D0      28   29
                D1      30   31

然后,在这条线上的某个地方,您忘记了名称 'snarf' 与哪个级别相关联,甚至忘记了有多少级别,您想要执行以下操作:

dfmi[dfmi.snarf=='D1']

除了 DataFrame 很大所以保留另一个,reset_indexed 副本会花费太多 space 而且它会很慢,而且,因为懒惰,你不想去查找它,并且您不想使用 query:

深入研究另一种语法

dfmi.query('snarf'=='D1')

糟糕!

dfmi.query("'snarf'=='D1'")

再次糟糕!

dfmi.query("snarf=='D1'")
                      foo  bar
quack woof honk snarf          
A0    B0   C0   D1       2    3
           C1   D1       6    7
      B1   C0   D1      10   11
           C1   D1      14   15
A1    B0   C0   D1      18   19
           C1   D1      22   23
      B1   C0   D1      26   27
           C1   D1      30   31

终于!

不确定是否更方便,但是基于字符串的查询的一种替代方法是使用 index.get_level_values:

dfmi[dfmi.index.get_level_values('snarf') == 'D1']

                       foo  bar
quack woof honk snarf          
A0    B0   C0   D1       2    3
           C1   D1       6    7
      B1   C0   D1      10   11
           C1   D1      14   15
A1    B0   C0   D1      18   19
           C1   D1      22   23
      B1   C0   D1      26   27
           C1   D1      30   31

如果我们要按名称在给定级别内查找相应的值(相等比较),那么 xs 可以工作:

dfmi.xs('D1', level='snarf', drop_level=False)

                       foo  bar
quack woof honk snarf          
A0    B0   C0   D1       2    3
           C1   D1       6    7
      B1   C0   D1      10   11
           C1   D1      14   15
A1    B0   C0   D1      18   19
           C1   D1      22   23
      B1   C0   D1      26   27
           C1   D1      30   31

您可以只保留索引的副本

inds = dfmi.index.to_frame()
dfmi[inds.snarf == "D1"]
 
#                        foo  bar
# quack woof honk snarf          
# A0    B0   C0   D1       2    3
#            C1   D1       6    7
#       B1   C0   D1      10   11
#            C1   D1      14   15
# A1    B0   C0   D1      18   19
#            C1   D1      22   23
#       B1   C0   D1      26   27
#            C1   D1      30   31

您还可以使用:

df = df[df.index.isin(['D1'], level='snarf')]

输出:

                       foo  bar
quack woof honk snarf          
A0    B0   C0   D1       2    3
           C1   D1       6    7
      B1   C0   D1      10   11
           C1   D1      14   15
A1    B0   C0   D1      18   19
           C1   D1      22   23
      B1   C0   D1      26   27
           C1   D1      30   31