1. ホーム
  2. パイソン

[解決済み】複数のgroupbyカラムに複数の関数を適用する

2022-03-26 22:26:24

質問

その ドキュメント は、出力カラム名をキーとする dict を使って、groupby オブジェクトに一度に複数の関数を適用する方法を示しています。

In [563]: grouped['D'].agg({'result1' : np.sum,
   .....:                   'result2' : np.mean})
   .....:
Out[563]: 
      result2   result1
A                      
bar -0.579846 -1.739537
foo -0.280588 -1.402938

ただし、これは Series の groupby オブジェクトに対してのみ有効です。また、groupbyのDataFrameに同様にdictが渡された場合、キーは関数が適用されるカラム名であることが期待されます。

私がやりたいことは、複数の列に対して複数の関数を適用することです(ただし、特定の列は複数回操作されます)。また いくつかの関数は、groupbyオブジェクト内の他のカラムに依存します。 (sumif関数のような)。私の現在の解決策は、列ごとに移動し、他の行に依存する関数にラムダを使用して、上記のコードのようなことを行うことです。しかし、これには長い時間がかかります(groupbyオブジェクトを繰り返し処理するのに長い時間がかかるのだと思います)。私は一回の実行でgroupbyオブジェクト全体を反復処理するように変更する必要がありますが、これをある程度きれいに行うための組み込みの方法がpandasにあるのかどうか疑問に思っています。

例えば、次のようなことをやってみた。

grouped.agg({'C_sum' : lambda x: x['C'].sum(),
             'C_std': lambda x: x['C'].std(),
             'D_sum' : lambda x: x['D'].sum()},
             'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)

が、案の定 KeyError が発生します (キーがカラムでなければならないのは agg はDataFrameから呼び出されます)。

私がやりたいことを行うための組み込みの方法、またはこの機能が追加される可能性はありますか、それとも手動でgroupbyを繰り返し実行する必要がありますか?

どのように解決するのですか?

現在受け入れられている回答の後半は、時代遅れで、2つの非推奨事項があります。まず、最も重要なことですが、辞書の辞書を agg groupby メソッドを使用します。次に、決して .ix .

もし、2つの別々のカラムを同時に操作したい場合は apply このメソッドでは、適用される関数に暗黙のうちに DataFrame が渡されます。上の例と同じようなデータフレームを使ってみましょう。

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df

          a         b         c         d  group
0  0.418500  0.030955  0.874869  0.145641      0
1  0.446069  0.901153  0.095052  0.487040      0
2  0.843026  0.936169  0.926090  0.041722      1
3  0.635846  0.439175  0.828787  0.714123      1

列名から集約関数にマッピングされたディクショナリーは、集約を実行するための完全に良い方法です。

df.groupby('group').agg({'a':['sum', 'max'], 
                         'b':'mean', 
                         'c':'sum', 
                         'd': lambda x: x.max() - x.min()})

              a                   b         c         d
            sum       max      mean       sum  <lambda>
group                                                  
0      0.864569  0.446069  0.466054  0.969921  0.341399
1      1.478872  0.843026  0.687672  1.754877  0.672401

もし、ラムダカラムの名前が気に入らない場合は、通常の関数を使用して、カスタム名を特別に指定することができます。 __name__ 属性はこのようになります。

def max_min(x):
    return x.max() - x.min()

max_min.__name__ = 'Max minus Min'

df.groupby('group').agg({'a':['sum', 'max'], 
                         'b':'mean', 
                         'c':'sum', 
                         'd': max_min})

              a                   b         c             d
            sum       max      mean       sum Max minus Min
group                                                      
0      0.864569  0.446069  0.466054  0.969921      0.341399
1      1.478872  0.843026  0.687672  1.754877      0.672401


使用方法 apply を作成し、Seriesを返します。

さて、複数のカラムが相互に作用する必要がある場合、以下のような方法は使えません。 agg これは暗黙のうちにSeriesを集約関数に渡すことになります。この場合 apply グループ全体がDataFrameとして関数に渡されます。

すべての集約をSeriesとして返すカスタム関数を1つ作ることをお勧めします。シリーズのインデックスを新しいカラムのラベルとして使用します。

def f(x):
    d = {}
    d['a_sum'] = x['a'].sum()
    d['a_max'] = x['a'].max()
    d['b_mean'] = x['b'].mean()
    d['c_d_prodsum'] = (x['c'] * x['d']).sum()
    return pd.Series(d, index=['a_sum', 'a_max', 'b_mean', 'c_d_prodsum'])

df.groupby('group').apply(f)

         a_sum     a_max    b_mean  c_d_prodsum
group                                           
0      0.864569  0.446069  0.466054     0.173711
1      1.478872  0.843026  0.687672     0.630494

MultiIndexesに惚れ込んでいる人は、こんな感じでSeriesを返すこともできます。

    def f_mi(x):
        d = []
        d.append(x['a'].sum())
        d.append(x['a'].max())
        d.append(x['b'].mean())
        d.append((x['c'] * x['d']).sum())
        return pd.Series(d, index=[['a', 'a', 'b', 'c_d'], 
                                   ['sum', 'max', 'mean', 'prodsum']])

df.groupby('group').apply(f_mi)

              a                   b       c_d
            sum       max      mean   prodsum
group                                        
0      0.864569  0.446069  0.466054  0.173711
1      1.478872  0.843026  0.687672  0.630494