pandas:在GroupBy上,使用.pipe()能做到什么,而使用.apply()做不到的呢?

38
在关于GroupBy对象的新.pipe()方法的pandas文档示例中,接受相同lambda的.apply()方法将返回相同的结果。
In [195]: import numpy as np

In [196]: n = 1000

In [197]: df = pd.DataFrame({'Store': np.random.choice(['Store_1', 'Store_2'], n),
   .....:                    'Product': np.random.choice(['Product_1', 'Product_2', 'Product_3'], n),
   .....:                    'Revenue': (np.random.random(n)*50+10).round(2),
   .....:                    'Quantity': np.random.randint(1, 10, size=n)})

In [199]: (df.groupby(['Store', 'Product'])
   .....:    .pipe(lambda grp: grp.Revenue.sum()/grp.Quantity.sum())
   .....:    .unstack().round(2))

Out[199]: 
Product  Product_1  Product_2  Product_3
Store                                   
Store_1       6.93       6.82       7.15
Store_2       6.69       6.64       6.77

我可以看出DataFrame对象中的管道功能与应用功能的区别,但对于GroupBy对象却不清楚。有人能解释一下或者给出一些使用管道功能而不能使用应用功能的GroupBy的例子吗?
2个回答

76

pipe 的作用是允许你传递一个可调用对象,并期望调用 pipe 的对象被传递给可调用对象。

使用 apply,我们假设调用 apply 的对象具有子组件,每个子组件将被传递给传递给 apply 的可调用对象。在 groupby 上下文中,子组件是调用 groupby 的 dataframe 的切片,其中每个切片本身是一个 dataframe。对于 series 的 groupby 也是类似的。

pipegroupby 上下文中的主要区别在于,你可以在可调用对象中使用整个 groupby 对象。而对于 apply,则只知道本地切片的信息。

设置
考虑 DataFrame df

df = pd.DataFrame(dict(
    A=list('XXXXYYYYYY'),
    B=range(10)
))

   A  B
0  X  0
1  X  1
2  X  2
3  X  3
4  Y  4
5  Y  5
6  Y  6
7  Y  7
8  Y  8
9  Y  9

示例1
使整个'B'列的总和为1,同时每个子组的总和相等。这需要计算时了解存在多少组。使用apply无法做到这一点,因为apply不知道存在多少个组。

s = df.groupby('A').B.pipe(lambda g: df.B / g.transform('sum') / g.ngroups)
s

0    0.000000
1    0.083333
2    0.166667
3    0.250000
4    0.051282
5    0.064103
6    0.076923
7    0.089744
8    0.102564
9    0.115385
Name: B, dtype: float64

注意:

s.sum()

0.99999999999999989

并且:

s.groupby(df.A).sum()

A
X    0.5
Y    0.5
Name: B, dtype: float64

例子2
从一个组的均值中减去另一个组的值。同样,这不能通过apply完成,因为apply不知道其他组。

df.groupby('A').B.pipe(
    lambda g: (
        g.get_group('X') - g.get_group('Y').mean()
    ).append(
        g.get_group('Y') - g.get_group('X').mean()
    )
)

0   -6.5
1   -5.5
2   -4.5
3   -3.5
4    2.5
5    3.5
6    4.5
7    5.5
8    6.5
9    7.5
Name: B, dtype: float64

请纠正我如果我错了,但管道(pipe)总是胜过 apply。这也体现在管道底层使用向量化,而 apply 不会。这大大缩短了操作时间。 - Andrew Anderson

-5
print(df.groupby(['A'])['B'].apply(lambda l: l/l.sum()/df.A.nunique()))

4
此回答被标记为低质量,需要解释说明。以下是如何撰写优质回答的指南:How do I write a good answer?。仅提供代码的回答不被认为是好的回答,且可能会因为对学习者社区的帮助较少而被踩和/或删除。这只是对你自己而言显而易见的。请解释它的功能,并阐明其与现有解答的不同之处及更好之处 - Trenton McKinney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接