按多重索引级别和列排序数据框

5

#更新:pandas版本0.23.0通过按列和索引级别的组合排序解决了这个问题。


我一直在苦苦挣扎,但我怀疑有更好的方法。如何按索引级别名称“idx_0”,级别= 0和列“value_1”降序排序以下数据框,以使列“MyName”垂直读取“SCOTTBOSTON”。

import pandas as pd
import numpy as np
df = pd.DataFrame({'idx_0':[2]*6+[1]*5,
                   'idx_1':[6,4,2,10,18,5,11,1,7,9,3],
                   'value_1':np.arange(11,0,-1),
                   'MyName':list('BOSTONSCOTT')})

df = df.set_index(['idx_0','idx_1'])
df

输出:

            MyName  value_1
idx_0 idx_1                
2     6          B       11
      4          O       10
      2          S        9
      10         T        8
      18         O        7
      5          N        6
1     11         S        5
      1          C        4
      7          O        3
      9          T        2
      3          T        1

#使用以下预期输出:

df.sort_values(['value_1'], ascending=False)\
  .reindex(sorted(df.index.get_level_values(0).unique()), level=0)

我怀疑有一种更简单的方式,而不需要重置索引。
            MyName  value_1
idx_0 idx_1                
1     11         S        5
      1          C        4
      7          O        3
      9          T        2
      3          T        1
2     6          B       11
      4          O       10
      2          S        9
      10         T        8
      18         O        7
      5          N        6

失败 #1:

df.sort_values('value_1', ascending=False).sort_index(level=0)

首先按照值排序,然后按照索引级别0排序,但是级别1也会被排序。

            MyName  value_1
idx_0 idx_1                
1     1          C        4
      3          T        1
      7          O        3
      9          T        2
      11         S        5
2     2          S        9
      4          O       10
      5          N        6
      6          B       11
      10         T        8
      18         O        7

失败 #2


df.sort_index(level=0).sort_values('value_1', ascending=False)

按照索引级别为0进行排序,然后按值排序,但索引为0的部分会再次混乱。

            MyName  value_1
idx_0 idx_1                
2     6          B       11
      4          O       10
      2          S        9
      10         T        8
      18         O        7
      5          N        6
1     11         S        5
      1          C        4
      7          O        3
      9          T        2
      3          T        1

我找不到任何优雅的解决方案。这里有一个选项:df.reset_index(level=0).sort_values(['idx_0','value_1'], ascending=[1,0]).set_index('idx_0', append=True).swaplevel(),但是你的版本看起来更好... - MaxU - stand with Ukraine
3个回答

5
以下是一些可能满足您需求的解决方案: 方法1:
 (df.sort_values('value_1', ascending=False)
    .sort_index(level=[0], ascending=[True]))

方法二:

 (df.set_index('value_1', append=True)
    .sort_index(level=[0,2], ascending=[True,False])
    .reset_index('value_1'))

本文基于pandas 0.22.0和Python 3.6.4进行测试。


你的方法一之所以有效,是因为在sort_index中,level参数使用了列表而不是标量? - Scott Boston
如果你查看源代码,set_index()有一个参数sort_remaining=True。这就是为什么当你按level-0排序时,其他级别仍然会参与进来的原因。当你提供一个列表时,它会将其他级别屏蔽掉作为剩余级别。要得到确切的答案,你需要更深入地挖掘他们的源代码。 - jxc
啊...排序剩下的可能就是我在寻找的了。 - Scott Boston

2
这是我的不太好看的选项:
In [139]: (df.assign(x=df.index.get_level_values(0) * \
                       10**np.ceil(np.log10(df.value_1.max()))-df.value_1)
             .sort_values('x')
             .drop('x',1))
Out[139]:
            MyName  value_1
idx_0 idx_1
1     11         S        5
      1          C        4
      7          O        3
      9          T        2
      3          T        1
2     6          B       11
      4          O       10
      2          S        9
      10         T        8
      18         O        7
      5          N        6

一些解释:

In [140]: np.ceil(np.log10(df.value_1.max()))
Out[140]: 2.0

In [141]: df.assign(x=df.index.get_level_values(0)*10**np.ceil(np.log10(df.value_1.max()))-df.value_1)
Out[141]:
            MyName  value_1      x
idx_0 idx_1
2     6          B       11  189.0
      4          O       10  190.0
      2          S        9  191.0
      10         T        8  192.0
      18         O        7  193.0
      5          N        6  194.0
1     11         S        5   95.0
      1          C        4   96.0
      7          O        3   97.0
      9          T        2   98.0
      3          T        1   99.0

另一个选项是添加idx_0并按照它和value_1进行排序,然后删除该额外列:
In [142]: (df.assign(x=df.index.get_level_values(0)).sort_values(['x', 'value_1'], ascending=[1,0])
             .drop('x',1))
Out[142]:
            MyName  value_1
idx_0 idx_1
1     11         S        5
      1          C        4
      7          O        3
      9          T        2
      3          T        1
2     6          B       11
      4          O       10
      2          S        9
      10         T        8
      18         O        7
      5          N        6

0

使用 pandas 版本 0.23.0 进行更新

按列和索引级别的组合排序

df.sort_values(by=['idx_0','value_1'], ascending=[True,False])
 

输出:

             value_1 MyName
idx_0 idx_1                
1     11           5      S
      1            4      C
      7            3      O
      9            2      T
      3            1      T
2     6           11      B
      4           10      O
      2            9      S
      10           8      T
      18           7      O
      5            6      N

有趣的是,@jxc 指出了一个解决方案,我认为应该可以工作,而且几乎与我的第一个失败一模一样。
df.sort_values('value_1', ascending=False)\
  .sort_index(level=0, ascending=[True])

ascending作为列表传递是使上述语句按预期工作的关键。我认为在pandas中,传递标量值和一个元素的列表应该起到相同的作用。然而,在这种情况下,它似乎不能起到相同的作用。

我将提交一个错误报告。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接