如何在连接多重索引数据框时保持列的顺序?
我有两个多重索引的 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")
这几乎可以实现,但第一层的顺序变得乱七八糟(按字母顺序排列),变成了 y1
、y11
、y2
。
>>> 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