【问题标题】:Sorting multi-index pd.Series using pd.Categorical?使用 pd.Categorical 对多索引 pd.Series 进行排序?
【发布时间】:2020-11-01 17:10:58
【问题描述】:

问题

为什么使用分类索引时使用pd.Series.sort_index 的排序似乎不起作用?如何使用除字母/数字以外的其他排序顺序对多索引 pd.Series 的索引进行排序?

MWE

设置代码

import pandas as pd 
import numpy as np

d = {
    'Card': [
        'Visa', 'Visa', 'Master Card', 'Master Card', 'Visa', 'Master Card',
        'Visa', 'Visa', 'Master Card', 'Visa', 'Master Card', 'Visa', 'Visa',
        'Master Card', 'Master Card', 'Visa', 'Master Card', 'Visa', 'Visa',
        'Master Card', 'Visa', 'Master Card', 'Master Card', 'Master Card',
        'Master Card', 'Master Card', 'Master Card', 'Visa', 'Visa'
    ],
    'Year': [
        'Three', 'Three', 'Seven', 'Three', 'Three', 'Seven', 'Seven', 'Seven',
        'Three', 'Seven', 'Three', 'Three', 'Three', 'Seven', 'Three', 'Three',
        'Seven', 'Seven', 'Seven', 'Three', 'Seven', 'Three', 'Five', 'One',
        'One', 'Two', 'Four', 'Six', 'Six'
    ],
    'Value': [
        45, 13, 52, 321, 31, 1231, 876, 231, 4, 213, 123, 45, 321, 1, 123, 52,
        736, 35, 900, 301, 374, 9, 294, 337, 4465, 321, 755, 22, 8
    ]
}

df = pd.DataFrame(d)
grp_cols = ['Card', 'Year']
ser_val = df.groupby(grp_cols)['Value'].mean()

简单地使用sort_index,数据看起来像这样:

In [2]: ser_val.sort_index()
Out[2]:
Card         Year
Master Card  Five      294.000000
             Four      755.000000
             One      2401.000000
             Seven     505.000000
             Three     146.833333
             Two       321.000000
Visa         Seven     438.166667
             Six        15.000000
             Three      84.500000
Name: Value, dtype: float64

您可以看到列按字母顺序排序。现在,我想强制订购。为此,我尝试:

categories_order = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven']
categories = pd.Categorical(ser_val.index.levels[1].values,
                            categories=categories_order,
                            ordered=True)
ser_val.index.set_levels(categories, level='Year', inplace=True)

再次,排序后,数据看起来像这样(再次,按字母顺序)

In [3]: ser_val.sort_index()
Out[3]:
Card         Year
Master Card  Five      294.000000
             Four      755.000000
             One      2401.000000
             Seven     505.000000
             Three     146.833333
             Two       321.000000
Visa         Seven     438.166667
             Six        15.000000
             Three      84.500000
Name: Value, dtype: float64

我知道如果我将数据转换成 pandas.DataFrame 并在那里排序,它可以工作,如下所示:

df_val = ser_val.reset_index().sort_values(grp_cols)
df_val['Year'] = pd.Categorical(df_val['Year'].values,
                                categories_order,
                                ordered=True)
df_val = df_val.sort_values(grp_cols).set_index(grp_cols)


In [5]: df_val
Out[5]:
                         Value
Card        Year
Master Card One    2401.000000
            Two     321.000000
            Three   146.833333
            Four    755.000000
            Five    294.000000
            Seven   505.000000
Visa        Three    84.500000
            Six      15.000000
            Seven   438.166667

为什么 pd.Series 不使用分类数据排序?

我在 Python 3.7.3 64 位中使用 pandas 1.0.5

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    TLDR:您需要在您的groupby 中设置sort=False,并且您需要将您的Categorical 更改为CategoricalIndex。这是完整的工作示例:

    df = pd.DataFrame(d)
    grp_cols = ['Card', 'Year']
    ser_val = df.groupby(grp_cols, sort=False)['Value'].mean()
    
    categories_order = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven']
    categories = pd.CategoricalIndex(ser_val.index.levels[1].values,
                                     categories=categories_order,
                                     ordered=True)
    ser_val.index.set_levels(categories, level='Year', inplace=True)
    ser_val.sort_index(inplace=True)
    

    ser_val 现在是:

    Card         Year 
    Master Card  One      2401.000000
                 Two       321.000000
                 Three     146.833333
                 Four      755.000000
                 Five      294.000000
                 Seven     505.000000
    Visa         Three      84.500000
                 Six        15.000000
                 Seven     438.166667
    Name: Value, dtype: float64
    

    Longer Ramblings:你问为什么你所做的事情不起作用,我当然无法解释(我敢肯定这涉及到一些根源) ,但这是我找到解决方案的方式。

    看看下面的例子是否有效,从头开始构建一个玩具 MultiIndex 系列:

    lets = ['a','b','c']*3
    ids = ['MALE']*4 + ['FEMALE']*5
    s = pd.Series(range(9), index=[ids,lets])
    
    categories_order = ['b','a','c']
    categories = pd.CategoricalIndex(s.index.levels[1].values,
                                     categories=categories_order,
                                     ordered=True)
    s.index.set_levels(categories, level=1,inplace=True)
    s.sort_index(inplace=True)
    

    s 按我们的意愿排序:

    FEMALE  b    4
            b    7
            a    6
            c    5
            c    8
    MALE    b    1
            a    0
            a    3
            c    2
    dtype: int64
    

    你的例子和我的例子之间唯一显着的区别(我可以说)是你的例子是从groupby开始的groupby有一个sort参数:

    sort : bool, default True 对组键进行排序。关闭此功能可获得更好的性能。请注意,这不会影响每组内的观察顺序。 Groupby 保留每个组内的行顺序。

    所以看起来groupby 排序正在强制执行一些未被您的新分类顺序覆盖的顺序。

    但是仍然使用sort=False,您的代码无法正常工作。只是通过谷歌搜索,我发现CategoricalCategoricalIndex 有单独的类,显然后者是你需要的。果然,如果使用Categorical 而不是CategoricalIndex,我的示例也会失败。

    所以groupby 似乎是一个更奇怪的问题;这里的基本规则我不能告诉你,但也许有人可以详细说明。

    【讨论】:

      【解决方案2】:

      实际上,我认为您发现了一两个错误!

      错误 #1 - 使用 pd.Categorical 更改具有 set_levels 的 dtype 不起作用。

      import pandas as pd 
      import numpy as np
      
      d = {
          'Card': [
              'Visa', 'Visa', 'Master Card', 'Master Card', 'Visa', 'Master Card',
              'Visa', 'Visa', 'Master Card', 'Visa', 'Master Card', 'Visa', 'Visa',
              'Master Card', 'Master Card', 'Visa', 'Master Card', 'Visa', 'Visa',
              'Master Card', 'Visa', 'Master Card', 'Master Card', 'Master Card',
              'Master Card', 'Master Card', 'Master Card', 'Visa', 'Visa'
          ],
          'Year': [
              'Three', 'Three', 'Seven', 'Three', 'Three', 'Seven', 'Seven', 'Seven',
              'Three', 'Seven', 'Three', 'Three', 'Three', 'Seven', 'Three', 'Three',
              'Seven', 'Seven', 'Seven', 'Three', 'Seven', 'Three', 'Five', 'One',
              'One', 'Two', 'Four', 'Six', 'Six'
          ],
          'Value': [
              45, 13, 52, 321, 31, 1231, 876, 231, 4, 213, 123, 45, 321, 1, 123, 52,
              736, 35, 900, 301, 374, 9, 294, 337, 4465, 321, 755, 22, 8
          ]
      }
      
      df = pd.DataFrame(d)
      grp_cols = ['Card', 'Year']
      ser_val = df.groupby(grp_cols)['Value'].mean()
      
      #---------------------------------------------
      #Attempt 1st
      categories_order = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven']
      categories = pd.Categorical(ser_val.index.levels[1].values,
                                  categories=categories_order,
                                  ordered=True)
      ser_val.index.set_levels(categories, level=1, inplace=True)
      print(ser_val.index.levels[1].dtype)
      
      #--------------------------------------------
      #Attempt 2nd
      categories_order = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven']
      categories = pd.Categorical(ser_val.index.levels[1].values,
                                  categories=categories_order,
                                  ordered=True)
      ser_val.index = ser_val.index.set_levels(categories, level='Year')
      print(ser_val.index.levels[1].dtype)
      
      #----------------------------------
      #Attempt 3rd and success
      categories_order = ['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven']
      categoriesDtype = pd.CategoricalDtype(categories_order, ordered=True)
      ser_val.index = ser_val.index.set_levels(ser_val.index.levels[1].astype(categoriesDtype), level='Year')
      print(ser_val.index.levels[1].dtype)
      

      输出:

      object  *FAILED change type using inplace*
      object  *FAILED change type using reassignment*
      category  *SUCCESS change type using pd.CategoricalDtype*
      

      错误 #2 - 对 MultiIndex 级别 1 使用分类的 sort_index 不起作用

      这可能已经在这里作为open issue #24271 找到

      成功更改索引级别 1 的 dype 后:

       ser_val.index.levels[1]
      

      输出:

      CategoricalIndex(['Five', 'Four', 'One', 'Seven', 'Six', 'Three', 'Two'], categories=['One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven'], ordered=True, name='Year', dtype='category')
      

      现在,让我们使用 sort_index 对数据帧进行排序:

      ser_val.sort_index()
      

      输出(失败):

      Card         Year 
      Master Card  Five      294.000000
                   Four      755.000000
                   One      2401.000000
                   Seven     505.000000
                   Three     146.833333
                   Two       321.000000
      Visa         Seven     438.166667
                   Six        15.000000
                   Three      84.500000
      Name: Value, dtype: float64
      

      现在,只是为了测试和测试,让我们交换索引级别并再次尝试 sort_index。

      ser_val.swaplevel(0,1).sort_index()
      

      输出(成功):

      Year   Card       
      One    Master Card    2401.000000
      Two    Master Card     321.000000
      Three  Master Card     146.833333
             Visa             84.500000
      Four   Master Card     755.000000
      Five   Master Card     294.000000
      Six    Visa             15.000000
      Seven  Master Card     505.000000
             Visa            438.166667
      Name: Value, dtype: float64
      

      但是,如果我们显式设置排序级别...再次失败。

      ser_val.swaplevel(0,1).sort_index(level=[0,1])
      

      输出:

      Year   Card       
      Five   Master Card     294.000000
      Four   Master Card     755.000000
      One    Master Card    2401.000000
      Seven  Master Card     505.000000
             Visa            438.166667
      Six    Visa             15.000000
      Three  Master Card     146.833333
             Visa             84.500000
      Two    Master Card     321.000000
      Name: Value, dtype: float64
      

      【讨论】:

      • 我的熊猫版本是 1.0.4。它可能会在即将推出的 1.1.0 版中得到修复。
      猜你喜欢
      • 2013-02-07
      • 2022-07-05
      • 1970-01-01
      • 1970-01-01
      • 2016-06-11
      • 2018-12-19
      • 2020-07-10
      • 1970-01-01
      • 2021-01-13
      相关资源
      最近更新 更多