在pandas中按照多级索引层排序列

30

我有一个如下所示的排序请求。

我需要reset_index(),然后sort(),再set_index()吗?还是有更好的方法来做到这一点呢?

l = [[1,'A',99],[1,'B',102],[1,'C',105],[1,'D',97],[2,'A',19],[2,'B',14],[2,'C',10],[2,'D',17]]
df = pd.DataFrame(l,columns = ['idx1','idx2','col1'])
df.set_index(['idx1','idx2'],inplace=True)

# assume data has been received like this...
print df

           col1
idx1 idx2      
1    A       99
     B      102
     C      105
     D       97
2    A       19
     B       14
     C       10
     D       17

# I'd like to sort descending on col1, partitioning within index level = 'idx2'

           col1
idx1 idx2      
1    C      105
     B      102
     A       99
     D       97

2    A       19
     D       17
     B       14
     C       10
谢谢您的回答。 请注意,我稍微更改了数据:
l = [[1,'A',99],[1,'B',11],[1,'C',105],[1,'D',97],[2,'A',19],[2,'B',14],[2,'C',10],[2,'D',17]]
df = pd.DataFrame(l,columns = ['idx1','idx2','col1'])
df.set_index(['idx1','idx2'],inplace=True)
df = df.sort_index(by='col1', ascending=False)

然而输出结果为

idx1 idx2      
1    C      105
     A       99
     D       97
2    A       19
     D       17
     B       14
1    B       11
2    C       10

我本来希望它是这样的。

idx1 idx2      
1    C      105
     A       99
     D       97
     B       11

2    A       19
     D       17
     B       14
     C       10

这个问题的编辑实际上非常令人困惑,部分原因是答案使用了顶部数据框而不是底部数据框。现在可能为时已晚,但应该删除顶部数据框,以便每个人都知道要使用底部数据框。 - JohnE
4个回答

22
你需要使用 DataFrame.reset_indexDataFrame.sort_valuesDataFrame.set_index 来操作数据框:
l = [[1,'A',99],[1,'B',11],[1,'C',105],[1,'D',97],
     [2,'A',19],[2,'B',14],[2,'C',10],[2,'D',17]]
df = pd.DataFrame(l,columns = ['idx1','idx2','col1'])
df.set_index(['idx1','idx2'],inplace=True)
print (df)
           col1
idx1 idx2      
1    A       99
     B       11
     C      105
     D       97
2    A       19
     B       14
     C       10
     D       17

df = df.reset_index() \
       .sort_values(['idx1','col1'], ascending=[True,False]) \
       .set_index(['idx1','idx2'])
print (df)
           col1
idx1 idx2      
1    C      105
     A       99
     D       97
     B       11
2    A       19
     D       17
     B       14
     C       10

编辑:

对于版本0.23.0,可以同时使用列和索引级别(但如果使用ascending=[True,False]会有bug,所以在更新的版本中可能得到修复):

df = df.sort_values(['idx1','col1'], ascending=[True,False])
print (df)

           col1
idx1 idx2      
1    C      105
     A       99
     D       97
     B       11
2    A       19
     D       17
     B       14
     C       10

1
@JohnE - 你是对的,所以我删除了第一个解决方案。 - jezrael

15
你可以使用 sort_index:
 df.sort_index(level='col1', ascending=False)

在0.17版本之前,level是通过by实现的。
df.sort_index(by='col1', ascending=False)

这将输出:

             col1
idx1    idx2    
1       C    105
        B    102
        A    99
        D    97
2       A    19
        D    17
        B    14
        C    10

1
我不确定是否可能同时按索引级别和列进行排序。 - JAB
2
没问题,我可以使用 .reset_index(),然后在列 {idx1 升序,col1 降序} 上进行常规排序,最后再使用 .set_index(['idx1','idx2'])。 - Dickster
5
sort_index()使用'by'参数的方法已经被弃用。这对此答案有何影响? - zthomas.nc
3
@zthomas.nc 我认为这个答案在编辑后的数据上不起作用。它只适用于原始问题,但仅因为idx=1的所有col1值都大于idx=2的所有col1值。 - JohnE

9

首先按所需列排序,仅在idx1 MultiIndex级别上进行排序,并适用于最新的pandas版本,该版本弃用by关键字。

df.sort_values('col1', ascending=False).sort_index(level='idx1', sort_remaining=False)

输出:

             col1
idx1    idx2    
1       C    105
        B    102
        A    99
        D    97
2       A    19
        D    17
        B    14
        C    10

1

通过使用groupby(已经存在的索引)和apply的另一种方法:

df.groupby(level=[0]).apply(lambda x:x.groupby(level=[1]).sum().sort_values('col1',ascending=False))

输出:

           col1
idx1 idx2      
1    C      105
     B      102
     A       99
     D       97
2    A       19
     D       17
     B       14
     C       10

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