【问题标题】:How do you update the levels of a pandas MultiIndex after slicing its DataFrame?切片 DataFrame 后如何更新 pandas MultiIndex 的级别?
【发布时间】:2015-02-27 19:08:43
【问题描述】:

我有一个带有 pandas MultiIndex 的数据框:

In [1]: import pandas as pd
In [2]: multi_index = pd.MultiIndex.from_product([['CAN','USA'],['total']],names=['country','sex'])
In [3]: df = pd.DataFrame({'pop':[35,318]},index=multi_index)
In [4]: df
Out[4]:
               pop
country sex
CAN     total   35
USA     total  318

然后我从那个 DataFrame 中删除一些行:

In [5]: df = df.query('pop > 100')

In [6]: df
Out[6]:
               pop
country sex
USA     total  318

但是当我查阅 MutliIndex 时,它的级别仍然包含两个国家。

In [7]: df.index.levels[0]
Out[7]: Index([u'CAN', u'USA'], dtype='object')

我可以用一种相当奇怪的方式自己解决这个问题:

In [8]: idx_names = df.index.names

In [9]: df = df.reset_index(drop=False)

In [10]: df = df.set_index(idx_names)

In [11]: df
Out[11]:
               pop
country sex
USA     total  318

In [12]: df.index.levels[0]
Out[12]: Index([u'USA'], dtype='object')

但这似乎相当混乱。我错过了更好的方法吗?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    从版本pandas 0.20.0+ 使用MultiIndex.remove_unused_levels

    print (df.index)
    MultiIndex(levels=[['CAN', 'USA'], ['total']],
               labels=[[1], [0]],
               names=['country', 'sex'])
    
    df.index = df.index.remove_unused_levels()
    
    print (df.index)
    MultiIndex(levels=[['USA'], ['total']],
               labels=[[0], [0]],
               names=['country', 'sex'])
    

    【讨论】:

      【解决方案2】:

      这是以前咬过我的东西。出于性能和哲学原因,删除列或行不会更改底层 MultiIndex,这正式不被视为错误 (read more here)。简短的回答是开发人员说“这不是 MultiIndex 的用途”。如果您需要修改后的 MultiIndex 级别的内容列表,例如用于迭代或检查是否包含某些内容,您可以使用:

      df.index.get_level_values(<levelname>)
      

      这将返回该索引级别内的当前活动值。

      所以我猜这里的“诀窍”是 API 原生方法是使用 get_level_values 而不仅仅是 .index 或 .columns

      【讨论】:

      • 哦,如果您不想重复,可以添加 .unique() 。默认级别值包括每次出现,因此您会在典型的多索引场景中看到很多重复项
      • 您还可以使用unique(data.index.values) 获取所有级别的值。
      【解决方案3】:

      如果有一种更“内置”的方式来消除未使用的国家/地区,而不是按照您正在做的方式(或类似方式)重新创建索引,我会感到惊讶。如果您查看切片前后的索引:

      In [165]: df.index
      Out[165]:
      MultiIndex(levels=[[u'CAN', u'USA'], [u'total']],
                 labels=[[0, 1], [0, 0]],
                 names=[u'country', u'sex'])
      
      In [166]: df = df.query('pop > 100')
      
      In [167]: df.index
      Out[167]:
      MultiIndex(levels=[[u'CAN', u'USA'], [u'total']],
                 labels=[[1], [0]],
                 names=[u'country', u'sex'])
      

      您可以看到标签(作为级别值的索引)已更新,但级别值未更新。这可能是一个不完美的类比,但令我印象深刻的是,级别值类似于数据库表中的枚举列,而标签类似于表中行的实际值。如果删除表中值为“CAN”的所有行,则不会改变“CAN”仍然是基于列定义的有效选择这一事实。要从枚举中删除“CAN”,您必须更改列定义;这相当于在 pandas 中重新索引数据框。

      【讨论】:

        猜你喜欢
        • 2014-05-24
        • 2017-03-28
        • 2020-01-16
        • 2020-09-10
        • 2017-05-08
        • 2016-03-05
        • 2013-07-07
        相关资源
        最近更新 更多