如何在多级索引数据框中的特定级别重新排序列

40
我有一个带有列名的多级索引DataFrame。我想要轻松地调整列的顺序,以使其与用户指定的顺序匹配。由于这是在管道中进行的,我无法在创建时正确排序,也不能使用推荐的解决方案
我有一个看起来像数据表的东西。
Experiment           BASE           IWWGCW         IWWGDW
Lead Time                24     48      24     48      24     48
2010-11-27 12:00:00   0.997  0.991   0.998  0.990   0.998  0.990
2010-11-28 12:00:00   0.998  0.987   0.997  0.990   0.997  0.990
2010-11-29 12:00:00   0.997  0.992   0.997  0.992   0.997  0.992
2010-11-30 12:00:00   0.997  0.987   0.997  0.987   0.997  0.987
2010-12-01 12:00:00   0.996  0.986   0.996  0.986   0.996  0.986

我希望能够输入一个列表,如['IWWGCW', 'IWWGDW', 'BASE'],并重新排序为:

Experiment           IWWGCW         IWWGDW         BASE           
Lead Time                24     48      24     48      24     48  
2010-11-27 12:00:00   0.998  0.990   0.998  0.990   0.997  0.991  
2010-11-28 12:00:00   0.997  0.990   0.997  0.990   0.998  0.987  
2010-11-29 12:00:00   0.997  0.992   0.997  0.992   0.997  0.992  
2010-11-30 12:00:00   0.997  0.987   0.997  0.987   0.997  0.987  
2010-12-01 12:00:00   0.996  0.986   0.996  0.986   0.996  0.986  

需要注意的是,我并不总是知道“实验”会处于哪个级别。我尝试过(其中df是上面显示的多级索引框架)。

df2 = df.reindex_axis(['IWWGCW', 'IWWGDW', 'BASE'], axis=1, level='Experiment')

但似乎并没有起作用 - 虽然成功完成,但返回的DataFrame未改变其列顺序。

我的解决方法是创建一个函数:

def reorder_columns(frame, column_name, new_order):
    """Shuffle the specified columns of the frame to match new_order."""

    index_level  = frame.columns.names.index(column_name)
    new_position = lambda t: new_order.index(t[index_level])
    new_index    = sorted(frame.columns, key=new_position)
    new_frame    = frame.reindex_axis(new_index, axis=1)
    return new_frame

我希望能够更简单地实现这个功能:reorder_columns(df, 'Experiment', ['IWWGCW', 'IWWGDW', 'BASE'])。虽然目前这种方式可以达到预期效果,但感觉多做了一些工作。是否有更简便的方法呢?


1
以下是最佳答案:https://dev59.com/5pzha4cB1Zd3GeqPFHHP?rq=1 - bonney
6个回答

32

有一种非常简单的方法:只需根据原始数据框创建一个新的数据框,并按正确的多重索引列顺序排列:

multi_tuples = [('IWWGCW',24), ('IWWGCW',48), ('IWWGDW',24), ('IWWGDW',48)
    , ('BASE',24), ('BASE',48)]

multi_cols = pd.MultiIndex.from_tuples(multi_tuples, names=['Experiment', 'Lead Time'])

df_ordered_multi_cols = pd.DataFrame(df_ori, columns=multi_cols)

14

这是对我而言最简单有效的方法:

  1. 为你选择的级别创建一个按所需顺序排列的列列表;

  2. 重新索引你的列并从该列表创建一个MultiIndex对象,记住它会返回一个元组;

  3. 使用MultiIndex对象来重新排序你的DataFrame。

cols = ['IWWGCW', 'IWWGDW', 'BASE']
new_cols = df.columns.reindex(cols, level=0)
df.reindex(columns=new_cols[0]) #new_cols is a single item tuple
在一行中:
df.reindex(columns=df.columns.reindex(['IWWGCW', 'IWWGDW', 'BASE'], level=0)[0])

瞧!


13

根据我上面的评论,使用pandas 1.3.2的解决方案:

df.reindex(columns=['IWWGCW', 'IWWGDW', 'BASE'], level='Experiment')

2
我认为这应该成为新的被接受的答案,因为原先被接受的答案已经过时了。 - Tommy

9

5
这是语法:df.reindex(['top', 'mid', 'btm'], level='first')。详见:https://github.com/pandas-dev/pandas/pull/9019。 - andrew_reece
2
df.reindex(['top', 'mid', 'btm'], level='first') 在多级列上不起作用。 - Tomasz
1
一个(次优的)解决方法适用于我:df.T.reindex(['top', 'mid', 'btm'], level='first').T - Nico
2
@Tomasz 为了响应原帖,df.reindex_axis(columns=['IWWGCW', 'IWWGDW', 'BASE'], level='Experiment') 可以用于多级列。 - Irv
3
reindex_axis已经被弃用,但是df.reindex(columns=['IWWGCW', 'IWWGDW', 'BASE'], level='Experiment')应该可以正常工作(注意:我在pandas 1.2.0中尝试过)。 注:建议使用新的df.reindex方法代替已经弃用的reindex_axis方法,使用指定列和索引级别来重新索引数据框。 - Irv
显示剩余3条评论

2

andrew_reece的评论应该被接受为答案。只需使用reindex()即可。

Github问题复制并粘贴:

>>> df
                     vals
first second third       
mid   3rd    992     1.96
             562    12.06
      1st    73     -6.46
             818   -15.75
             658     5.90
btm   2nd    915     9.75
             474    -1.47
             905    -6.03
      1st    717     8.01
             909   -21.12
      3rd    616    11.91
             675     1.06
             579    -4.01
top   1st    241     1.79
             363     1.71
      3rd    677    13.38
             238   -16.77
             407    17.19
      2nd    728   -21.55
             36      8.09
>>> df.reindex(['top', 'mid', 'btm'], level='first')
                     vals
first second third       
top   1st    241     1.79
             363     1.71
      3rd    677    13.38
             238   -16.77
             407    17.19
      2nd    728   -21.55
             36      8.09
mid   3rd    992     1.96
             562    12.06
      1st    73     -6.46
             818   -15.75
             658     5.90
btm   2nd    915     9.75
             474    -1.47
             905    -6.03
      1st    717     8.01
             909   -21.12
      3rd    616    11.91
             675     1.06
             579    -4.01
>>> df.reindex(['1st', '2nd', '3rd'], level='second')
                     vals
first second third       
mid   1st    73     -6.46
             818   -15.75
             658     5.90
      3rd    992     1.96
             562    12.06
btm   1st    717     8.01
             909   -21.12
      2nd    915     9.75
             474    -1.47
             905    -6.03
      3rd    616    11.91
             675     1.06
             579    -4.01
top   1st    241     1.79
             363     1.71
      2nd    728   -21.55
             36      8.09
      3rd    677    13.38
             238   -16.77
             407    17.19
>>> df.reindex(['top', 'btm'], level='first').reindex(['1st', '2nd'], level='second')
                     vals
first second third       
top   1st    241     1.79
             363     1.71
      2nd    728   -21.55
             36      8.09
btm   1st    717     8.01
             909   -21.12
      2nd    915     9.75
             474    -1.47
             905    -6.03

这并没有回答问题,因为它不能处理已经被展开成列的索引级别。 - ZaxR

1
我已经改进了这里的答案,并编写了一个函数,可以直接在带有两层多重索引的pandas数据框上使用。通过更改函数第3行中的“by”参数,可以将其扩展到高阶列多重索引。
def reorder_multindex_columns(df):
    level_names = list(df.columns.names)
    multi_tuples_df = pd.DataFrame.from_records(df.columns.values)
    multi_tuples_df = multi_tuples_df.sort_values(by = [0,1])
    multi_tuples = list(multi_tuples_df.to_records(index=False))
    multi_cols = pd.MultiIndex.from_tuples(multi_tuples, names = level_names)

    return pd.DataFrame(df, columns=multi_cols)

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