从Pandas 0.22版本开始,除了apply
方法外还有一个替代方案:pipe
。相比于使用apply
方法,pipe
方法可以更快地运行(您也可以查看此问题以了解两种功能之间的更多差异)。
对于您的例子:
df = pd.DataFrame({"my_label": ['A','B','A','C','D','D','E']})
my_label
0 A
1 B
2 A
3 C
4 D
5 D
6 E
apply
方法版本
df.groupby('my_label').apply(lambda grp: grp.count() / df.shape[0])
给予
my_label
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
和 pipe
版本
df.groupby('my_label').pipe(lambda grp: grp.size() / grp.size().sum())
产出。
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
因此,这些值是相同的,然而时间差异非常大(至少对于这个小数据框来说):
%timeit df.groupby('my_label').apply(lambda grp: grp.count() / df.shape[0])
100 loops, best of 3: 5.52 ms per loop
和
%timeit df.groupby('my_label').pipe(lambda grp: grp.size() / grp.size().sum())
1000 loops, best of 3: 843 µs per loop
将其包装成一个函数也很简单:
def get_perc(grp_obj):
gr_size = grp_obj.size()
return gr_size / gr_size.sum()
现在您可以调用
df.groupby('my_label').pipe(get_perc)
产出
my_label
A 0.285714
B 0.142857
C 0.142857
D 0.285714
E 0.142857
然而,对于这种特殊情况,您甚至不需要使用groupby
,而只需像这样使用value_counts
:
df['my_label'].value_counts(sort=False) / df.shape[0]
产出
A 0.285714
C 0.142857
B 0.142857
E 0.142857
D 0.285714
Name: my_label, dtype: float64
对于这个小数据框,速度非常快
%timeit df['my_label'].value_counts(sort=False) / df.shape[0]
1000 loops, best of 3: 770 µs per loop
正如@anmol所指出的那样,最后一条语句也可以简化为
df['my_label'].value_counts(sort=False, normalize=True)
/ df.shape[0]
,而在第二个代码片段中使用了/ grp.size().sum()
。为什么呢?我发现如果你把第一个替换成第二个,就会出现“int is not callable”的错误。我读了与管道/应用程序差异有关的链接问题,但这不是关于组间的事情——它似乎像管道将对象包装在列表或其他东西中,而应用程序则没有... - alexey