如何使用另一个MultiIndex切割一个MultiIndex DataFrame

时间:2015-03-25 21:11:12

标签: python pandas dataframe multi-index

我有一个包含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('foo', 'can')级别('bar', 'baz')b的{​​{1}}的值:c

所以我想在第一级采用(1, 2, 5, 6, 9, 10),并在第二和第三级提取特定元组。

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

3 个答案:

答案 0 :(得分:20)

以下是获取此片段的方法:

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进行排序,然后才能对其进行切片。如果你需要这么做,熊猫就足以警告:

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

答案 1 :(得分: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

答案 2 :(得分:0)

我觉得有趣的是,这不起作用:

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

现在要按df的索引对other进行切片,我们可以使用.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