为什么pd.MultiIndex.from_tuples会改变元组的顺序

3

使用from_tuples创建MultiIndex时,创建的索引对象与输入的元组顺序不同。

我正在尝试使用pd.MultiIndex.from_tuples方法向数据帧添加列级别,但级别与我的预期不同。

df = pd.DataFrame({'x_1':[1, 2], 'x_2':[3, 4], 'x_10':[3, 4], 'y_1':[5, 6], 'y_2':[7, 8], 'y_10':[1, 2]})
df = df.reindex(columns=['x_1', 'x_2', 'x_10', 'y_1', 'y_2', 'y_10'])
index = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df.columns])
print(index)

MultiIndex(levels=[['x', 'y'], ['1', '10', '2']],
           labels=[[0, 0, 0, 1, 1, 1], [0, 2, 1, 0, 2, 1]])

当我向数据框添加级别并执行堆叠操作时,顺序并不是我想要的。
df.columns = index
df.stack()

      x  y
0 1   1  5
  10  3  1
  2   3  7
1 1   2  6
  10  4  2
  2   4  8

我希望指数水平看起来像这样:

MultiIndex(levels=[['x', 'y'], ['1', '2', '10']])

并且堆叠将会如下所示:
df.stack()
      x  y
0 1   1  5
  2   3  7
  10  3  1
1 1   2  6
  2   4  8
  10  4  2

这不是根据它是字符串还是整数来排序吗?例如,'1','10',然后'2'按此顺序排序,因为它们是字符串。 - EdChum
我同意这是正确的,有没有办法使它按特定顺序? - C. Tanaka
您可以在事后重新索引以实现所需的顺序,但是这会引入性能问题,因为索引应该是有序的。如果您有整数列,则这将起作用,具体取决于您。 - EdChum
2个回答

2
你可以在调用stack之前,使用列的级别值对特定级别进行reindex

请参考reindex

"Original Answer"翻译成"最初的回答"。
In[177]:
df.stack().reindex(df.columns.get_level_values(1).unique(), level=1)
Out[177]: 
      x  y
0 1   1  5
  2   3  7
  10  3  1
1 1   2  6
  2   4  8
  10  4  2

注意,这会影响性能,因为索引需要排序以实现快速查找。最初的回答。

1
你构建的索引实际上是按照指定顺序排序的。当你使用print(index)时,你看到的是Pandas内部存储索引的方式。使用index.values可以展开这个表示,给出一个与你的数据框对齐的索引数组。
print(index.values)
# array([('x', '1'), ('x', '2'), ('x', '10'), ('y', '1'), ('y', '2'),
#        ('y', '10')], dtype=object)

df.columns = index 
print(df)    
#    x        y      
#    1  2 10  1  2 10
# 0  1  3  3  5  7  1
# 1  2  4  4  6  8  2

实际问题是pd.DataFrame.stack会进行排序,由于您定义了字符串,'10'会排在'2'之前。为了在stack之后保持所需的顺序,请确保使用整数:
def splitter(x):
    strng, num = x.split('_')
    return strng, int(num)

index = pd.MultiIndex.from_tuples(df.columns.map(splitter))

df.columns = index 
print(df.stack())
#       x  y
# 0 1   1  5
#   2   3  7
#   10  3  1
# 1 1   2  6
#   2   4  8
#   10  4  2

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