我在 pandas 中有一个多索引数据框,索引中有 4 列,还有一些数据列。示例如下:
import pandas as pd
import numpy as np
cnames = ['K1', 'K2', 'K3', 'K4', 'D1', 'D2']
rdata = pd.DataFrame(np.random.randint(1, 3, size=(8, len(cnames))), columns=cnames)
rdata.set_index(cnames[:4], inplace=True)
rdata.sortlevel(inplace=True)
print(rdata)
D1 D2
K1 K2 K3 K4
1 1 1 1 1 2
1 1 2
2 1 2 1
2 1 2 2 1
2 1 2 1
2 1 2 2 2 1
2 1 2 1 1
2 1 1
[8 rows x 2 columns]
我想要做的是选择 K3 级别恰好有 2 个值的行。不是 2 行,而是两个不同的值。我已经找到了如何为我想要的东西生成一种掩码:
filterFunc = lambda x: len(set(x.index.get_level_values('K3'))) == 2
mask = rdata.groupby(level=cnames[:2]).apply(filterFunc)
print(mask)
K1 K2
1 1 True
2 True
2 1 False
2 False
dtype: bool
而且我希望,由于 rdata.loc[1, 2]
允许您仅匹配索引的一部分,因此可以使用 bool 向量执行相同的操作,例如这。不幸的是,rdata.loc[mask]
失败,并出现IndexingError: Unalignable boolean Series keyprovided
。
This question看起来很相似,但是给出的答案不适用于顶级索引之外的任何内容,因为 index.get_level_values 仅适用于单个级别,而不适用于多个级别。
按照建议here我成功地实现了我想要的
rdata[[mask.loc[k1, k2] for k1, k2, k3, k4 in rdata.index]]
但是,使用 len(set(index.get_level_values(...))) 获取不同值的计数并随后通过迭代每一行来构建 bool 向量感觉更像是我'我正在与框架进行斗争,以实现在多索引设置中看似简单的任务。有更好的解决办法吗?
这里使用 pandas 0.13.1。
最佳答案
可能有更好的东西,但您至少可以使用groupby-filter来绕过定义mask
:
rdata.groupby(level=cnames[:2]).filter(
lambda grp: (grp.index.get_level_values('K3')
.unique().size) == 2)
Out[83]:
D1 D2
K1 K2 K3 K4
1 1 1 1 1 2
1 1 2
2 1 2 1
2 1 2 2 1
2 1 2 1
[5 rows x 2 columns]
它比我之前的建议更快。它对于小型 DataFrame 来说效果非常好:
In [84]: %timeit rdata.groupby(level=cnames[:2]).filter(lambda grp: grp.index.get_level_values('K3').unique().size == 2)
100 loops, best of 3: 3.84 ms per loop
In [76]: %timeit rdata2.groupby(level=cnames[:2]).filter(lambda grp: grp.groupby(level=['K3']).ngroups == 2)
100 loops, best of 3: 11.9 ms per loop
In [77]: %timeit rdata2.groupby(level=cnames[:2]).filter(lambda grp: len(set(grp.index.get_level_values('K3'))) == 2)
100 loops, best of 3: 13.4 ms per loop
对于大型 DataFrame 来说仍然是最快的,尽管速度没有那么快:
In [78]: rdata2 = pd.concat([rdata]*100000)
In [85]: %timeit rdata2.groupby(level=cnames[:2]).filter(lambda grp: grp.index.get_level_values('K3').unique().size == 2)
1 loops, best of 3: 756 ms per loop
In [79]: %timeit rdata2.groupby(level=cnames[:2]).filter(lambda grp: grp.groupby(level=['K3']).ngroups == 2)
1 loops, best of 3: 772 ms per loop
In [80]: %timeit rdata2.groupby(level=cnames[:2]).filter(lambda grp: len(set(grp.index.get_level_values('K3'))) == 2)
1 loops, best of 3: 1 s per loop
关于python - 通过多索引的子集从 pandas 中选择行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22521414/