【问题标题】:How to slice one MultiIndex DataFrame with the MultiIndex of another如何用另一个的 MultiIndex 分割一个 MultiIndex DataFrame
【发布时间】:2015-05-29 18:27:48
【问题描述】:

我有一个带有 3 级 MultiIndex 的 pandas 数据框。我正在尝试根据对应于两个级别的值列表提取此数据帧的行。

我有这样的事情:

ix = pd.MultiIndex.from_product([[1, 2, 3], ['foo', 'bar'], ['baz', 'can']], names=['a', 'b', 'c'])
data = np.arange(len(ix))
df = pd.DataFrame(data, index=ix, columns=['hi'])
print(df)

           hi
a b   c      
1 foo baz   0
      can   1
  bar baz   2
      can   3
2 foo baz   4
      can   5
  bar baz   6
      can   7
3 foo baz   8
      can   9
  bar baz  10
      can  11

现在我想获取索引级别“b”和“c”在此索引中的所有行:

ix_use = pd.MultiIndex.from_tuples([('foo', 'can'), ('bar', 'baz')], names=['b', 'c'])

hi 的值在级别 bc 中分别具有 ('foo', 'can')('bar', 'baz')(1, 2, 5, 6, 9, 10)

所以我想在第一层取slice(None),并在第二层和第三层提取特定的元组。

最初我认为将多索引对象传递给 .loc 会提取出我想要的值/级别,但这不起作用。做这种事情的最佳方法是什么?

【问题讨论】:

  • 我进行了几次不同的尝试以使其发挥作用。我想我已经找到了一个不错的解决方法,即目前这似乎有点困难。试试看!

标签: python pandas dataframe multi-index


【解决方案1】:

这是获取此切片的一种方法:

df.sort_index(inplace=True)
idx = pd.IndexSlice
df.loc[idx[:, ('foo','bar'), 'can'], :]

屈服

           hi
a b   c      
1 bar can   3
  foo can   1
2 bar can   7
  foo can   5
3 bar can  11
  foo can   9

请注意,您可能需要先对 MultiIndex 进行排序,然后才能对其进行切片。如果您需要这样做,pandas 会很友好地发出警告:

KeyError: 'MultiIndex Slicing requires the index to be fully lexsorted tuple len (3), lexsort depth (1)'

您可以在docs 中阅读有关如何使用切片器的更多信息

如果由于某种原因不能使用切片器,这里可以使用.isin() 方法获得相同的切片:

df[df.index.get_level_values('b').isin(ix_use.get_level_values(0)) & df.index.get_level_values('c').isin(ix_use.get_level_values(1))]

这显然不是那么简洁。

更新:

对于您在这里更新的条件是一种方法:

cond1 = (df.index.get_level_values('b').isin(['foo'])) & (df.index.get_level_values('c').isin(['can']))
cond2 = (df.index.get_level_values('b').isin(['bar'])) & (df.index.get_level_values('c').isin(['baz']))
df[cond1 | cond2]

制作:

           hi
a b   c      
1 foo can   1
  bar baz   2
2 foo can   5
  bar baz   6
3 foo can   9
  bar baz  10

【讨论】:

  • 这真的很接近我正在寻找的东西,但我应该更清楚地表达这个问题。实际上,我需要的是“c”级的值,该值取决于“b”级的值。例如,任何时候级别“b”是“foo”,我想要级别“c”是“can”的值,并且任何时候级别“b”是“bar”,我想要级别“c”的值是“巴兹”
  • 用这两个条件更新了答案,这应该可以说明如何处理这个问题。
【解决方案2】:

我会推荐the query() method,就像this Q&A一样。

简单的用这个,我觉得是比较自然的表达方式:

In [27]: df.query("(b == 'foo' and c == 'can') or (b == 'bar' and c == 'baz')")
Out[27]: 
           hi
a b   c      
1 foo can   1
  bar baz   2
2 foo can   5
  bar baz   6
3 foo can   9
  bar baz  10

【讨论】:

    【解决方案3】:

    我觉得这不起作用很有趣:

    In [45]: df.loc[(idx[:, 'foo', 'can'], idx[:, 'bar', 'baz']), ]
    Out[45]: 
               hi
    a b   c      
    1 bar baz   2
          can   3
      foo baz   0
          can   1
    2 bar baz   6
          can   7
      foo baz   4
          can   5
    3 bar baz  10
          can  11
      foo baz   8
          can   9
    

    它看起来有点像“应该”,不知何故。无论如何,这是一个合理的解决方法:

    让我们假设您要切片的元组位于另一个 DataFrame 的索引中(因为在您的情况下,它们可能!)。

    In [53]: ix_use = pd.MultiIndex.from_tuples([('foo', 'can'), ('bar', 'baz')], names=['b', 'c'])
    In [55]: other = pd.DataFrame(dict(a=1), index=ix_use)
    In [56]: other
    Out[56]: 
             a
    b   c     
    foo can  1
    bar baz  1
    

    现在通过other 的索引对df 进行切片,我们可以使用.loc/.ix 允许您提供元组列表(参见最后一个示例here)这一事实。

    首先让我们构建我们想要的元组列表:

    In [13]: idx = [(x, ) + y for x in df.index.levels[0] for y in other.index.values]
    In [14]: idx
    Out[14]: 
    [(1, 'foo', 'can'),
     (1, 'bar', 'baz'),
     (2, 'foo', 'can'),
     (2, 'bar', 'baz'),
     (3, 'foo', 'can'),
     (3, 'bar', 'baz')]
    

    现在我们可以将此列表传递给.ix.loc

    In [17]: df.ix[idx]
    Out[17]: 
               hi
    a b   c      
    1 foo can   1
      bar baz   2
    2 foo can   5
      bar baz   6
    3 foo can   9
      bar baz  10
    

    【讨论】:

      猜你喜欢
      • 2018-10-14
      • 2020-03-02
      • 1970-01-01
      • 1970-01-01
      • 2018-08-20
      • 1970-01-01
      • 2019-04-05
      • 2014-09-15
      • 2015-04-02
      相关资源
      最近更新 更多