How to slice one MultiIndex DataFrame with the MultiIndex of another
我有一个包含3个MultiIndex级别的pandas数据帧。 我试图根据对应于两个级别的值列表拉出此数据帧的行。
我有这样的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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'在此索引中的所有行:
1 | ix_use = pd.MultiIndex.from_tuples([('foo', 'can'), ('bar', 'baz')], names=['b', 'c']) |
即,
所以我想在第一级采用
最初我认为将多索引对象传递给.loc会拉出我想要的值/级别,但这不起作用。 做这样的事情最好的方法是什么?
以下是获取此切片的方法:
1 2 3 | df.sort_index(inplace=True) idx = pd.IndexSlice df.loc[idx[:, ('foo','bar'), 'can'], :] |
生产
1 2 3 4 5 6 7 8 | 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进行排序,然后才能对其进行切片。如果你需要这么做,熊猫就足够警告了:
1 | KeyError: 'MultiIndex Slicing requires the index to be fully lexsorted tuple len (3), lexsort depth (1)' |
您可以在文档中阅读有关如何使用切片器的更多信息
如果由于某种原因使用切片器不是一个选项,这是一种使用
1 | 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))] |
这显然不那么简洁。
更新:
对于您在此处更新的条件是一种方法:
1 2 3 | 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] |
生产:
1 2 3 4 5 6 7 8 | 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 3 4 5 6 7 8 9 10 | 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 |
我觉得有趣的是这不起作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 |
不知何故,它看起来像"应该"。无论如何,这是一个合理的解决方法:
让我们假设您想要切片的元组位于另一个
1 2 3 4 5 6 7 8 | 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 |
现在要按
首先让我们构建我们想要的元组列表:
1 2 3 4 5 6 7 8 9 | 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')] |
现在我们可以将此列表传递给
1 2 3 4 5 6 7 8 9 10 | 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 |