如何使用另一个 MultiIndex 对一个 MultiIndex DataFrame 进行切片
How to slice one MultiIndex DataFrame with the MultiIndex of another
我有一个 pandas 数据框,其中包含 3 个级别的 MultiIndex。我正在尝试根据对应于两个级别的值列表提取此数据框的行。
我有这样的东西:
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
的值在级别 b
和 c
中分别具有 ('foo', 'can')
或 ('bar', 'baz')
:(1, 2, 5, 6, 9, 10)
.
所以我想在第一层取一个slice(None)
,在第二层和第三层提取特定的元组。
最初我认为将多索引对象传递给 .loc 会提取我想要的值/级别,但这不起作用。执行此类操作的最佳方法是什么?
这里有一个获取切片的方法:
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
我觉得这行不通很有趣:
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
有点像 "should"。无论如何,这是一个合理的解决方法:
让我们假设您想要切片的元组在另一个 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
我会推荐 the query()
method just like in 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
我有一个 pandas 数据框,其中包含 3 个级别的 MultiIndex。我正在尝试根据对应于两个级别的值列表提取此数据框的行。
我有这样的东西:
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
的值在级别 b
和 c
中分别具有 ('foo', 'can')
或 ('bar', 'baz')
:(1, 2, 5, 6, 9, 10)
.
所以我想在第一层取一个slice(None)
,在第二层和第三层提取特定的元组。
最初我认为将多索引对象传递给 .loc 会提取我想要的值/级别,但这不起作用。执行此类操作的最佳方法是什么?
这里有一个获取切片的方法:
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
我觉得这行不通很有趣:
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
有点像 "should"。无论如何,这是一个合理的解决方法:
让我们假设您想要切片的元组在另一个 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
我会推荐 the query()
method just like in 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