web-dev-qa-db-ja.com

パンダ:GroupBy .pipe()と.apply()

GroupByオブジェクトの新しい.pipe()メソッドに関する pandasドキュメント の例では、同じラムダを受け入れる.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オブジェクトのpipe機能がapplyとどのように異なるかがわかりますが、GroupByオブジェクトの場合は違います。 GroupByのpipeではなくapplyではできないことの説明や例はありますか?

19
foglerit

pipeが行うことは、pipeを呼び出したオブジェクトがcallableに渡されるオブジェクトであるという期待でcallableを渡すことを可能にすることです。

applyでは、applyを呼び出すオブジェクトに、applyに渡された呼び出し可能オブジェクトにそれぞれ渡されるサブコンポーネントがあると想定しています。 groupbyのコンテキストでは、サブコンポーネントはgroupbyと呼ばれるデータフレームのスライスであり、各スライスはデータフレーム自体です。これはシリーズgroupbyに似ています。

pipeコンテキストでgroupbyを使用してできることの主な違いは、groupbyオブジェクトのスコープ全体を呼び出し可能オブジェクトで使用できることです。適用については、ローカルスライスについてのみ知っています。

セットアップ
検討する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
33
piRSquared