如何使用groupby过滤数据框中的重复项?

How to filter duplicates in a dataframe using groupby?

我有一个数据框 df(cfg, x, rounds) 是唯一的,其余的都不是。

      cfg   x     rounds  score  rewards  
0  f63c2c   a          1   0.01       10  
1  f63c2c   a          2   0.02       15  
2  f63c2c   b          3   0.03       30  
3  f63c2c   b          4   0.04       13  
4  f63c2c   b          5   0.05        8  
5  37fb26   a          1   0.08        8  
6  35442a   a          5   0.19        8  
7  bb8460   b          2   0.05        9  

我想以这种方式过滤数据框,结果中只有 cfg, x, max(rounds) 行,即

      cfg  x  rounds  score  rewards  
1  f63c2c  a       2   0.02       15  
4  f63c2c  b       5   0.05        8  
5  37fb26  a       1   0.08        8  
6  35442a  a       5   0.19        8  
7  bb8460  b       2   0.05        9  

为此,我确定最大使用:

gf = df.groupby(["cfg", "x"]).max().loc[:,["rounds"]]

但是,我还没有想出使用 gf 作为谓词提供程序来过滤 df 的方法。有什么想法吗?

解决方案不是使用 groupby(或者更准确地说,最简单的解决方案不是使用 gorupby),而是使用 drop_duplicates。 默认情况下 drop_duplicates 保留任何重复值的第一行,因此您可以对数据框进行排序,然后使用以下内容删除重复项:

gf = df.sort_values(by = 'rounds',ascending = [True,False]).\
                                 drop_duplicates(subset = ['cfg','x'])

    cfg     x   rounds  score   rewards
6   35442a  a   5       0.19    8
5   37fb26  a   1       0.08    8
7   bb8460  b   2       0.05    9
4   f63c2c  b   5       0.05    8

你也可以等效地做:

gf = df.sort_values(by = 'rounds',ascending = True).\
            drop_duplicates(subset = ['cfg','x'],keep = 'last')

编辑:Timeit

令人惊讶的是,我在他的回答中没有得到与 coldspeed 相同的时间:

df_test = pd.concat([df] * 100000) 
%timeit df_test.sort_values(by = ['cfg','rounds'],ascending = True).\
        drop_duplicates(subset = ['cfg'],keep = 'last')
%timeit df_test.groupby('cfg').rounds.apply(np.max).reset_index().\
        merge(my_df2, on=['cfg', 'rounds'])

62 ms ± 163 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
70.6 ms ± 28.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

这似乎不取决于核心数量(我已经在 8 和 12 核心上启动它产生相同的排名),也不取决于数据帧的大小(我试过 df_test 10 000、100 000 和 1 000 000 df 的大小,排名不变)。

所以我想这一定取决于你的硬件,你只需要尝试这两种方法,看看哪种适合你的电脑。

感谢 coldspeed 指出这一点

确实可以使用df.groupbydf.merge:

n [231]: df.groupby(['cfg', 'x']).rounds\
     ...:             .apply(np.max).reset_index()\
     ...:             .merge(df, on=['cfg', 'x', 'rounds'])
Out[231]: 
      cfg  x  rounds  score  rewards
0  35442a  a       5   0.19        8
1  37fb26  a       1   0.08        8
2  bb8460  b       2   0.05        9
3  f63c2c  a       2   0.02       15
4  f63c2c  b       5   0.05        8

并且,使用 df.sort_values:

In [237]: df.sort_values(by = ['cfg','x', 'rounds'],ascending = [True, True, False])\
            .drop_duplicates(subset = ['cfg', 'x'])
Out[237]: 
      cfg  x  rounds  score  rewards
6  35442a  a       5   0.19        8
5  37fb26  a       1   0.08        8
7  bb8460  b       2   0.05        9
1  f63c2c  a       2   0.02       15
4  f63c2c  b       5   0.05        8

性能

df_test = pd.concat([df] * 100000) # Setup

使用df.merge:

%timeit df_test.sort_values(by = ['cfg','x', 'rounds'],ascending = [True, True, False])
               .drop_duplicates(subset = ['cfg', 'x'])
1 loop, best of 3: 229 ms per loop

使用 df.sort_valuesdf.drop_duplicates:

%timeit df_test.groupby(['cfg', 'x']).rounds\
                 .apply(np.max).reset_index()\
                 .merge(df, on=['cfg', 'x', 'rounds'])
10 loops, best of 3: 129 ms ms per loop