【问题标题】:Multi-Index Sorting in PandasPandas 中的多索引排序
【发布时间】:2013-06-19 00:59:15
【问题描述】:

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

初始数据集如下所示(各种产品的每日销售额):

         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'])

【问题讨论】:

  • “数据将按所选级别按字典顺序排序随后是其他级别(按顺序)”(这很糟糕......)

标签: python sorting pandas multi-index


【解决方案1】:

要按“索引列”(又名级别)对 MultiIndex 进行排序,您需要使用 .sort_index() 方法并设置其 level 参数。如果要按多个级别排序,则需要将参数设置为按顺序排列的级别名称列表。

这应该会为您提供所需的 DataFrame:

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

【讨论】:

  • 您还可以使用布尔值列表设置ascending 参数,以分别控制每个级别的方向。例如.sort_index(level=['Manufacturer','LaunchDate'], ascending=[True,False]).
【解决方案2】:

这一个班轮适合我:

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

【讨论】:

  • 感谢您发布更新的答案。我有一个三级多索引,只想按前两个排序。这非常有效。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    如果您想尝试避免在非常深的 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
    

    【讨论】:

      【解决方案5】:

      改变关卡的顺序是一种技巧:

      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 问题。 :) 虽然值得一提的是关于 "the need for sortedness" 的文档说明。

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

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

      【讨论】:

      • This doc note 建议需要对级别进行排序,尽管这显然只是一个实现细节。目前尚不清楚这是否意味着它们必须按索引级别从最高到最低进行分层排序。
      • @BrenBarn 这是一个很好的观点,我之前听说过 Jeff 继续说... :)
      • 顺便说一句,您不能通过按交换顺序执行初始 groupby 来消除解决方案中的额外交换/排序(然后只是在组之后的交换级别)吗?
      • @BrenBarn 谢谢(会提到这一点)! :)
      猜你喜欢
      • 2017-08-29
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 2019-08-16
      • 2017-06-16
      • 1970-01-01
      • 2016-01-16
      相关资源
      最近更新 更多