Pandas自定义排序多级索引

5
我有以下示例数据集,我想按照自定义顺序对索引列进行排序,该顺序不包含在数据帧中。到目前为止,在SO上查找,我还没有解决这个问题。示例:
import pandas as pd

data = {'s':[1,1,1,1], 
        'am':['cap', 'cap', 'sea', 'sea'], 
        'cat':['i', 'o', 'i', 'o'],
        'col1':[.55, .44, .33, .22],
        'col2':[.77, .66, .55, .44]}

df = pd.DataFrame(data=data)
df.set_index(['s', 'am', 'cat'], inplace=True)

Out[1]: 
           col1  col2
s am  cat            
1 cap i    0.55  0.77
      o    0.44  0.66
  sea i    0.33  0.55
      o    0.22  0.44

What I would like is the following:

Out[2]: 
           col1  col2
s am  cat            
1 sea i    0.33  0.55
      o    0.22  0.44
  cap i    0.55  0.77
      o    0.44  0.66

我可能还想按 'cat' 排序,顺序为 ['o', 'i']。

2个回答

5

使用 sort_valuessort_index

df.sort_values(df.columns.tolist()).sort_index(level=1, ascending=False, 
                                                        sort_remaining=False)

              col1  col2
s   am   cat        
1   sea  i    0.33  0.55
         o    0.22  0.44
    cap  i    0.55  0.77
         o    0.44  0.66

将索引转换为 categorical 以获取自定义顺序。
data = {'s':[1,1,1,1], 
            'am':['cap', 'cap', 'sea', 'sea'], 
            'cat':['i', 'j', 'k', 'l'],
            'col1':[.55, .44, .33, .22],
            'col2':[.77, .66, .55, .44]}

df = pd.DataFrame(data=data)
df.set_index(['s', 'am', 'cat'], inplace=True)

idx = pd.Categorical(df.index.get_level_values(2).values,
          categories=['j','i','k','l'],
          ordered=True)

df.index.set_levels(idx, level='cat', inplace=True)

df.reset_index().sort_values('cat').set_index(['s','am','cat'])

             col1   col2
s   am  cat     
1   cap  j   0.44   0.66
         i   0.55   0.77
    sea  k   0.33   0.55
         l   0.22   0.44

1
谢谢,这对于特定情况确实有效,但是否有一种方法可以实际指定要按索引的哪些列进行排序,还可以输入列表以指定排序顺序? - fffrost
@fffrost 您可以更改级别以按特定索引级别排序。如果您想按“cat”排序,则使用sort_index(level=2) - Abhi
如果我有一个'cat'中包含4个级别,例如['i', 'j', 'k', 'l'],并且想要将它们按照['j', 'l', 'k', 'i']的顺序进行自定义排序,应该如何操作? - fffrost
@fffrost 您可以将猫索引转换为分类,并指定您想要排序的顺序。我已经更新了答案。 - Abhi

1

从Pandas 1.1开始,sort_valueskey参数提供了另一种选择。

SORT_VALS = {"am": ["sea", "cap"]}

def sorter(column):
    if column.name not in SORT_VALS:
        return column
    mapper = {val: order for order, val in enumerate(SORT_VALS[column.name])}
    return column.map(mapper)

new_df = df.sort_values(by=["s", "am", "cat"], key=sorter)

#            col1  col2
# s am  cat            
# 1 sea i    0.33  0.55
#       o    0.22  0.44
#   cap i    0.55  0.77
#       o    0.44  0.66

你也可以在排序器中使用pd.Categorical,并返回一个分类的Series以用于自定义排序列。这可能会因场景不同而有不同的性能影响,但请注意,在pandas中存在一个即将修复的错误, 这可能会阻止使用Categorical进行多列排序。

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