Pandas中的多重索引排序

25

我有一个多重索引的DataFrame,是通过groupby操作创建的。我正在尝试使用索引的多个级别进行复合排序,但似乎找不到符合需求的排序函数。

初始数据集看起来像这样(各种产品每日销售计数):

         Date Manufacturer Product Name Product Launch Date  Sales
0  2013-01-01        Apple         iPod          2001-10-23     12
1  2013-01-01        Apple         iPad          2010-04-03     13
2  2013-01-01      Samsung       Galaxy          2009-04-27     14
3  2013-01-01      Samsung   Galaxy Tab          2010-09-02     15
4  2013-01-02        Apple         iPod          2001-10-23     22
5  2013-01-02        Apple         iPad          2010-04-03     17
6  2013-01-02      Samsung       Galaxy          2009-04-27     10
7  2013-01-02      Samsung   Galaxy Tab          2010-09-02      7

我使用groupby对日期范围进行求和:
> grouped = df.groupby(['Manufacturer', 'Product Name', 'Product Launch Date']).sum()
                                               Sales
Manufacturer Product Name Product Launch Date       
Apple        iPad         2010-04-03              30
             iPod         2001-10-23              34
Samsung      Galaxy       2009-04-27              24
             Galaxy Tab   2010-09-02              22

目前为止,一切都很顺利!

现在我想做的最后一件事是按发布日期对每个制造商的产品进行排序,但仍然在制造商下分层组织 - 这就是我要做的全部内容:

                                               Sales
Manufacturer Product Name Product Launch Date       
Apple        iPod         2001-10-23              34
             iPad         2010-04-03              30
Samsung      Galaxy       2009-04-27              24
             Galaxy Tab   2010-09-02              22

当我尝试使用sortlevel()函数时,我失去了之前漂亮的按公司层级排列的效果:

> grouped.sortlevel('Product Launch Date')
                                               Sales
Manufacturer Product Name Product Launch Date       
Apple        iPod         2001-10-23              34
Samsung      Galaxy       2009-04-27              24
Apple        iPad         2010-04-03              30
Samsung      Galaxy Tab   2010-09-02              22

sort()和sort_index()操作失败:

grouped.sort(['Manufacturer','Product Launch Date'])
KeyError: u'no item named Manufacturer'

grouped.sort_index(by=['Manufacturer','Product Launch Date'])
KeyError: u'no item named Manufacturer'

看起来似乎是个简单的操作,但我还不能完全理解。

我不一定非要使用MultiIndex,但既然groupby()返回的就是它,那我就用它来工作。

顺便提一下,用于生成初始DataFrame的代码如下:

data = {
  'Date': ['2013-01-01', '2013-01-01', '2013-01-01', '2013-01-01', '2013-01-02', '2013-01-02', '2013-01-02', '2013-01-02'],
  'Manufacturer' : ['Apple', 'Apple', 'Samsung', 'Samsung', 'Apple', 'Apple', 'Samsung', 'Samsung',],
  'Product Name' : ['iPod', 'iPad', 'Galaxy', 'Galaxy Tab', 'iPod', 'iPad', 'Galaxy', 'Galaxy Tab'], 
  'Product Launch Date' : ['2001-10-23', '2010-04-03', '2009-04-27', '2010-09-02','2001-10-23', '2010-04-03', '2009-04-27', '2010-09-02'],
  'Sales' : [12, 13, 14, 15, 22, 17, 10, 7]
}
df = DataFrame(data, columns=['Date', 'Manufacturer', 'Product Name', 'Product Launch Date', 'Sales'])

3
数据将按选择的级别进行词典排序,然后按照其他级别(顺序排列) - Andy Hayden
5个回答

11

一个技巧是改变级别的顺序:

In [11]: g
Out[11]:
                                               Sales
Manufacturer Product Name Product Launch Date
Apple        iPad         2010-04-03              30
             iPod         2001-10-23              34
Samsung      Galaxy       2009-04-27              24
             Galaxy Tab   2010-09-02              22

In [12]: g.index = g.index.swaplevel(1, 2)

Sortlevel会按顺序对MultiIndex层次进行排序(正如你所发现的):

In [13]: g = g.sortlevel()

然后进行交换:

In [14]: g.index = g.index.swaplevel(1, 2)

In [15]: g
Out[15]:
                                               Sales
Manufacturer Product Name Product Launch Date
Apple        iPod         2001-10-23              34
             iPad         2010-04-03              30
Samsung      Galaxy       2009-04-27              24
             Galaxy Tab   2010-09-02              22

我认为sortlevel不应该按顺序对其余标签进行排序,因此会创建一个github问题。 :) 但值得一提的是文档注释中关于“排序需求”的内容。

注意:您可以通过重新排序初始分组的顺序来避免第一个swaplevel

g = df.groupby(['Manufacturer', 'Product Launch Date', 'Product Name']).sum()

1
这个文档注释建议需要对级别进行排序,尽管显然这只是一种实现细节。目前并不清楚这是否意味着它们必须按照最高到最低的索引级别进行分层排序。 - BrenBarn
@BrenBarn 这是一个好观点,我之前听Jeff也说过这个... :) - Andy Hayden
顺便问一下,你不能通过在交换后的顺序中进行初始分组(然后在分组后仅交换级别)来消除解决方案中的额外交换/排序吗? - BrenBarn
@BrenBarn 谢谢(会提到这个)! :) - Andy Hayden

7
这个简短的代码适用于我:
In [1]: grouped.sortlevel(["Manufacturer","Product Launch Date"], sort_remaining=False)

                                               Sales
Manufacturer Product Name Product Launch Date       
Apple        iPod         2001-10-23              34
             iPad         2010-04-03              30
Samsung      Galaxy       2009-04-27              24
             Galaxy Tab   2010-09-02              22

请注意,这也可以正常工作:

groups.sortlevel([0,2], sort_remaining=False)

这在两年前您最初发布时是行不通的,因为默认情况下sortlevel按所有索引排序会混乱公司层次结构。去除这种行为的sort_remaining于去年添加。以下是参考的提交链接: https://github.com/pydata/pandas/commit/3ad64b11e8e4bef47e3767f1d31cc26e39593277

1
感谢您发布更新的答案。我有一个三级多索引,只想按前两个排序。这个方法完美解决了我的问题。 - Arjun Kumar

6
要按照“索引列”(即级别)对MultiIndex进行排序,您需要使用.sort_index()方法并设置其level参数。如果您想按多个级别排序,则需要将该参数设置为按顺序排列的级别名称列表。
这应该会给您所需的DataFrame:
df.groupby(['Manufacturer',
            'Product Name', 
            'Launch Date']
          ).sum().sort_index(level=['Manufacturer','Launch Date'])

2
你也可以使用布尔值列表来设置 ascending 参数,以控制每个级别的排序方向。例如:.sort_index(level=['Manufacturer','LaunchDate'], ascending=[True,False]) - fpersyn

0

如果您想要尝试避免在非常深的MultiIndex中进行多次交换,您也可以尝试使用以下方法:

  1. 通过级别X进行切片(使用列表推导式+ .loc + IndexSlice)
  2. 对所需级别进行排序(sortlevel(2))
  3. 连接每个级别X索引组

这里是代码:

import pandas as pd
idx = pd.IndexSlice
g = pd.concat([grouped.loc[idx[i,:,:],:].sortlevel(2) for i in grouped.index.levels[0]])
g

0

如果您不关心保留索引(我通常更喜欢使用任意整数索引),则可以使用以下一行代码:

grouped.reset_index().sort(["Manufacturer","Product Launch Date"])

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