python - 使用具有特定值计数的 GroupBy 过滤 Pandas DataFrame
问题描述
我想将 pandas DataFrame 过滤到该特定行的组具有特定列值的最小计数的行。
例如,仅返回 ['c2','c3'] 组至少有 2 行 'c1' 值为 1 的 df 的行/组:
df = pd.DataFrame({'c1':[0,1,0,1,1,0], 'c2':[0,0,0,1,1,1], 'c3':[0,0,0,1,1,1]})
结果应该只返回索引为 3,4,5 的行,因为只有 [c2,c3] = [1,1] 组至少有 2 行 'c1' 值为 1。
df.groupby(['c2','c3']).filter(lambda x: x['c1'].count() >= 2)
不返回所需的结果。我需要将计数专门应用于 1 的计数,而不仅仅是“c1”的任何值。
以下工作,但我不知道如何使它更pythonic:
s = df.groupby(['c2','c3']).apply(lambda x: x[x['c1']==1].count() >= 2).all(axis=1)
df = df.reset_index().set_index(['c2','c3']).loc[s[s].index].reset_index().set_index(['index'])
解决方案
使用groupby
+transform
对布尔系列求和,我们用它来屏蔽原始 DataFrame。
m = df['c1'].eq(1).groupby([df['c2'], df['c3']]).transform('sum').ge(2)
# Alterntively assign the column
#m = df.assign(to_sum = df.c1.eq(1)).groupby(['c2', 'c3']).to_sum.transform('sum').ge(2)
df.loc[m]
# c1 c2 c3
#3 1 1 1
#4 1 1 1
#5 0 1 1
用过滤器,count
是不正确的逻辑。使用==
(或.eq()
)检查'c1'
与特定值相等的位置。对布尔系列求和,并检查您的过滤器每组是否至少出现 2 次此类事件。
df.groupby(['c2','c3']).filter(lambda x: x['c1'].eq(1).sum() >= 2)
# c1 c2 c3
#3 1 1 1
#4 1 1 1
#5 0 1 1
虽然对于小型 DataFrame 而言并不明显,但随着组数的增加,filter
使用 alambda
的速度非常慢。transform
很快:
import numpy as np
np.random.seed(123)
df = pd.DataFrame({'c1':np.random.randint(1,100,1000), 'c2':np.random.randint(1,100,1000),
'c3':np.random.choice([1,0], 1000)})
%%timeit
m = df['c1'].eq(1).groupby([df.c3, df.c3]).transform('sum').ge(2)
df.loc[m]
#5.21 ms ± 15.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit df.groupby(['c2','c3']).filter(lambda x: x['c1'].eq(1).sum() >= 2)
#124 ms ± 714 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
推荐阅读
- arrays - 用集合数组声明一个变量数组
- c - 表的指针表?
- python - 在 for 循环中访问过去的日期时间
- flutter - 是否可以在 Dart/Flutter 中创建对象数组
- authentication - Blazor - 使用带有本地 DB 存储库的 ADFS 保护:如何/何时挂钩 SQL
- javascript - Vanilla JS 隐藏/显示 DIV 切换器
- python - 如何检查 1 后面是否总是跟着 0
- function - 我无法理解 makefile 中 patsubst 的输出
- html - 响应式页脚设计 - 包装页脚项目
- c++ - 使用 netlink 获取进程 inode