python - 通过多索引的子集从 pandas 中选择行

标签 python pandas

我在 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/

相关文章:

python - 使用 ESMTP 和 Twisted to GMail 发送电子邮件就在那里(没有错误或任何东西)

python - 使用 ElementTree 读取 .xml 等电子表格

python - Pandas 数据框唯一元素的累计数量

python - 在 Pandas 中,如何将 bool 列转换为分类列?

python - Scrapy Crawler - 如何指定要抓取的链接

javascript - 如果在 javascript 中返回,如何抓取搜索结果(使用 python)

python - Python索引错误: list assignment index out of range ,

python - Pandas Dataframe 匹配行之间的日期

python - 如何进行groupKfold验证并获得平衡的数据?

python - Pandas 在 groupby.apply(..) 之后删除组列