使用 pandas groupby 和过滤器删除行

drop rows using pandas groupby and filter

我正在尝试从满足特定条件的 df 中删除行。在下面,我使用 C 列对值进行分组。对于每个唯一组,我想删除 A 小于 1B 大于 100 的所有行。不过,这必须发生在同一行。如果我使用 .any().all(),它不会 return 我想要的。

df = pd.DataFrame({
    'A' : [1,0,1,0,1,0,0,1,0,1], 
    'B' : [101, 2, 3, 1, 5, 101, 2, 3, 4, 5], 
    'C' : ['d', 'd', 'd', 'd', 'e', 'e', 'e', 'f', 'f',], 
    })

df.groupby(['C']).filter(lambda g: g['A'].lt(1) & g['B'].gt(100))

初始 df:

   A    B  C
0  1  101  d # A is not lt 1 so keep all d's
1  0    2  d
2  1    3  d
3  0    1  d
4  1    5  e
5  0  101  e # A is lt 1 and B is gt 100 so drop all e's
6  0    2  e
7  1    3  f
8  0    4  f
9  1    5  f

意在:

   A    B  C
0  1  101  d
1  0    2  d
2  1    3  d
3  0    1  d
7  1    3  f
8  0    4  f
9  1    5  f

为了获得更好的性能,获取所有 C 值匹配条件,然后通过 Series.isin in boolean indexing 使用反向掩码过滤原始列 C

df1 = df[~df['C'].isin(df.loc[df['A'].lt(1) & df['B'].gt(100), 'C'])]

另一种想法是使用 GroupBy.transform with GroupBy.any 来测试是否匹配至少一个值:

df1 = df[~(df['A'].lt(1) & df['B'].gt(100)).groupby(df['C']).transform('any')]

您的解决方案可以使用 anynot 作为标量,如果 DataFrame 很大,它应该很慢:

df1 = df.groupby(['C']).filter(lambda g:not ( g['A'].lt(1) & g['B'].gt(100)).any())

df1 = df.groupby(['C']).filter(lambda g: (g['A'].ge(1) | g['B'].le(100)).all())

print (df1)
   A    B  C
0  1  101  d
1  0    2  d
2  1    3  d
3  0    1  d
7  1    3  f
8  0    4  f
9  1    5  f