如何使用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.groupby
和df.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_values
和 df.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
我有一个数据框 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.groupby
和df.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_values
和 df.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