如何在连接多重索引数据框时保持列的顺序?

3 投票
2 回答
56 浏览
提问于 2025-04-12 15:28

我有两个多重索引的 pandas 数据框,它们看起来像这样:

>>> df1 = pd.DataFrame({
...     ('y1', '0'): [1, 2, 3],
...     ('y2', '0'): [4, 5, 6],
...     ('y11', '0'): [7, 8, 9],
... })

>>> df2 = pd.DataFrame({
...     ('y1', '1'): [1.5, 2.5, 3.5],
...     ('y2', '1'): [4.5, 5.5, 6.5],
...     ('y11', '1'): [7.5, 8.5, 9.5],
... })

我想把它们合并在一起,结果应该是这样的:

>>> df = pd.DataFrame({
...    ('y1', '0'): [1, 2, 3],
...    ('y1', '1'): [1.5, 2.5, 3.5],
...    ('y2', '0'): [4, 5, 6],
...    ('y2', '1'): [4.5, 5.5, 6.5],
...    ('y11', '0'): [7, 8, 9],
...    ('y11', '1'): [7.5, 8.5, 9.5],
... })

也就是说,第一层的多重索引顺序是:y1;y2;y11,要保持不变,而第二层的索引则要合理地交错排列。

有没有什么方法可以合并这两个多重索引的数据框,同时保持第一层索引的顺序不变呢?

如果我使用:

>>> df = pd.concat((df1, df2), axis="columns").sort_index(axis="columns")

这几乎可以实现,但第一层的顺序变得乱七八糟(按字母顺序排列),变成了 y1y11y2

>>> print(df)
  y1      y11      y2     
   0    1   0    1  0    1
0  1  1.5   7  7.5  4  4.5
1  2  2.5   8  8.5  5  5.5
2  3  3.5   9  9.5  6  6.5

我可以用一个复杂的正则表达式来做到这一点,但我觉得应该有比这更好的解决办法。

2 个回答

1

你也可以使用正则表达式来手动提取数字,并通过把这些数字当作数字来排序。如果你在 DataFrame.sort_index 中指定一个级别,这个方法可以作为一个 key 函数传入。

import pandas as pd
from re import match

df1 = pd.DataFrame({
    ('y1', '0'): [1, 2, 3],
    ('y2', '0'): [4, 5, 6],
    ('y11', '0'): [7, 8, 9],
})

df2 = pd.DataFrame({
    ('y1', '1'): [1.5, 2.5, 3.5],
    ('y2', '1'): [4.5, 5.5, 6.5],
    ('y11', '1'): [7.5, 8.5, 9.5],
})

df = (
    pd.concat((df1, df2), axis="columns")
    .sort_index(
        axis='columns',
        level=0,
        key=lambda idx:
            idx.str.extract(r'(\w)(\d+)').astype({1: int})
    )
)

print(df)
#   y1      y2      y11
#    0    1  0    1   0    1
# 0  1  1.5  4  4.5   7  7.5
# 1  2  2.5  5  5.5   8  8.5
# 2  3  3.5  6  6.5   9  9.5
5

一个简单的办法是使用 concat,然后用 sort_index 来排序,最后用 df1 恢复你想要的顺序:

out = (pd.concat([df1, df2], axis=1)
          .sort_index(axis=1, level=0)
       [df1.columns.get_level_values(0)]
      )

如果你不能依赖原来的顺序,并且想要强制进行自然排序,可以使用 natsort

from natsort import natsorted

out = (pd.concat([df1, df2], axis=1)
          .sort_index(axis=1, level=0)
       [natsorted(df1.columns.get_level_values(0))]
      )

或者:

from natsort import index_natsorted

out = pd.concat([df1, df2], axis=1)
out = out.iloc[:, index_natsorted(out.columns)]

## or
# out = (pd.concat([df1, df2], axis=1)
#          .iloc[:, lambda x: index_natsorted(x.columns)]
#       )

输出结果:

  y1      y2      y11     
   0    1  0    1   0    1
0  1  1.5  4  4.5   7  7.5
1  2  2.5  5  5.5   8  8.5
2  3  3.5  6  6.5   9  9.5

撰写回答