pandas:对多层列数据框进行排序/重新排列列

1 投票
2 回答
43 浏览
提问于 2025-04-12 13:28

我有以下数据:

from pandas import Timestamp

values = [['IDX100', 'field1', Timestamp('1999-02-01 05:00:00'), '101'],
       ['IDX100', 'field1', Timestamp('1999-02-02 05:00:00'), '102'],
       ['IDX100', 'field1', Timestamp('1999-02-03 05:00:00'), '103'],
       ['IDX200', 'field1', Timestamp('1999-02-01 05:00:00'), '601'],
       ['IDX200', 'field1', Timestamp('1999-02-02 05:00:00'), '602'],
       ['IDX200', 'field1', Timestamp('1999-02-03 05:00:00'), '603'],
       ['IDX100', 'field2', Timestamp('1999-02-01 05:00:00'), '201'],
       ['IDX100', 'field2', Timestamp('1999-02-02 05:00:00'), '202'],
       ['IDX100', 'field2', Timestamp('1999-02-03 05:00:00'), '203'],
       ['IDX200', 'field2', Timestamp('1999-02-01 05:00:00'), '701'],
       ['IDX200', 'field2', Timestamp('1999-02-02 05:00:00'), '702'],
       ['IDX200', 'field2', Timestamp('1999-02-03 05:00:00'), '703'],
       ['IDX100', 'field3', Timestamp('1999-02-01 05:00:00'), '301'],
       ['IDX100', 'field3', Timestamp('1999-02-02 05:00:00'), '302'],
       ['IDX100', 'field3', Timestamp('1999-02-03 05:00:00'), '303'],
       ['IDX200', 'field3', Timestamp('1999-02-01 05:00:00'), '801'],
       ['IDX200', 'field3', Timestamp('1999-02-02 05:00:00'), '802'],
       ['IDX200', 'field3', Timestamp('1999-02-03 05:00:00'), '803']]

df = pd.DataFrame(values, columns = ['identifier', 'code', 'date', 'value'])

在对我的数据表进行透视后,我得到了以下结果:

df = df.pivot(index=['date'], columns=['identifier', 'code'], values=['value'])

                     value                                   
identifier          IDX100 IDX200 IDX100 IDX200 IDX100 IDX200
code                field1 field1 field2 field2 field3 field3
date                                                         
1999-02-01 05:00:00    101    601    201    701    301    801
1999-02-02 05:00:00    102    602    202    702    302    802
1999-02-03 05:00:00    103    603    203    703    303    803

不过,我希望输出的结果看起来像这样:

identifier           IDX100                IDX200 
code                 field3 field2 field1  field3 field2 field1
date                                                         
1999-02-01 05:00:00    301    201    101   801    701    601
1999-02-02 05:00:00    302    202    102   802    702    602
1999-02-03 05:00:00    303    203    103   803    703    603

我可以通过做一些类似的操作接近这个结果:

df = df.reindex(sorted(df.columns), axis=1)

但是这样做会保持level2列的顺序为field1、field2、field3。我希望能够以不同的顺序来排列这些字段……最好是根据我提供的一个列表来排序。例如,我可能想要的顺序是field3、field2、field1,或者field2、field1、field3。

有没有人能帮我解决这个问题?

2 个回答

3

一种标准且可靠的方法是使用有序的 CategoricalDtype

codes = pd.CategoricalDtype(['field2', 'field1', 'field3'], ordered=True)

out = (df.astype({'code': codes})
         .pivot(index=['date'], columns=['identifier', 'code'], values='value')
         .sort_index(axis=1)
       )

或者,可以在透视后进行排序:

codes = pd.CategoricalDtype(['field2', 'field1', 'field3'], ordered=True)

out = df.pivot(index=['date'], columns=['identifier', 'code'], values='value')

out.columns = pd.MultiIndex.from_frame(out.columns.to_frame().astype({'code': codes}))

out = out.sort_index(axis=1)

输出结果:

identifier          IDX100               IDX200              
code                field2 field1 field3 field2 field1 field3
date                                                         
1999-02-01 05:00:00    201    101    301    701    601    801
1999-02-02 05:00:00    202    102    302    702    602    802
1999-02-03 05:00:00    203    103    303    703    603    803
2

示例代码

特别是当数据框(dataframe)包含多重索引时,需要提供创建数据框的代码,以便你能够复现你的数据框。

这次,我为你提供了代码。

import pandas as pd

data = [[101, 601, 201, 701, 301, 801], [102, 602, 202, 702, 302, 802], [103, 603, 203, 703, 303, 803]]

idx = pd.Index(['1999-02-01 05:00:00', '1999-02-02 05:00:00', '1999-02-03 05:00:00'], name='date')
cols = pd.MultiIndex.from_product([['field1', 'field2', 'field3'], ['IDX100', 'IDX200']], names=['code', 'identifier']).swaplevel(0, 1)
df = pd.DataFrame(data, index=idx, columns=cols)

df

这里输入图片描述


代码

让我们按照 field2 -> field1 -> field3 的顺序进行排序

使用 sort_index 方法

order = ['field2', 'field1', 'field3']
m = {key: num for num, key in enumerate(order)}
out = df.sort_index(axis=1, key=lambda x: x.map(m) if x.name == 'code' else x)

输出:

这里输入图片描述

撰写回答