docs は、出力列名をキーとして辞書を使用して、一度に複数の関数を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オブジェクトでのみ機能します。また、dictがgroupby DataFrameに同様に渡される場合、キーは関数が適用される列名であると想定します。
私がしたいのは、複数の関数を複数の列に適用することです(ただし、特定の列は複数回操作されます)。また、一部の関数はgroupbyオブジェクトの他の列に依存します(sumif関数など)。私の現在の解決策は、列ごとに行き、上記のコードのようなことをして、他の行に依存する関数にラムダを使用することです。しかし、これには長い時間がかかります(groupbyオブジェクトを反復処理するには長い時間がかかると思います)。 1回の実行で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つの別々の列を操作したい場合は、適用される関数に暗黙的にDataFrameを渡すapply
メソッドを使用することをお勧めします。上と同じデータフレームを使用しましょう
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.__= '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
を使用してシリーズを返す相互作用する必要がある複数の列がある場合、agg
を使用できません。これは、暗黙的にSeriesを集約関数に渡します。 apply
を使用すると、DataFrameとしてのグループ全体が関数に渡されます。
すべての集計のシリーズを返す単一のカスタム関数を作成することをお勧めします。 Seriesインデックスを新しい列のラベルとして使用します。
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
最初の部分では、キーの列名の辞書と値の関数のリストを渡すことができます。
In [28]: df
Out[28]:
A B C D E GRP
0 0.395670 0.219560 0.600644 0.613445 0.242893 0
1 0.323911 0.464584 0.107215 0.204072 0.927325 0
2 0.321358 0.076037 0.166946 0.439661 0.914612 1
3 0.133466 0.447946 0.014815 0.130781 0.268290 1
In [26]: f = {'A':['sum','mean'], 'B':['prod']}
In [27]: df.groupby('GRP').agg(f)
Out[27]:
A B
sum mean prod
GRP
0 0.719580 0.359790 0.102004
1 0.454824 0.227412 0.034060
更新1:
集計関数はシリーズで機能するため、他の列名への参照は失われます。これを回避するには、完全なデータフレームを参照し、ラムダ関数内のグループインデックスを使用してインデックスを作成します。
ハックな回避策は次のとおりです。
In [67]: f = {'A':['sum','mean'], 'B':['prod'], 'D': lambda g: df.loc[g.index].E.sum()}
In [69]: df.groupby('GRP').agg(f)
Out[69]:
A B D
sum mean prod <lambda>
GRP
0 0.719580 0.359790 0.102004 1.170219
1 0.454824 0.227412 0.034060 1.182901
ここでは、結果の「D」列は合計された「E」値で構成されています。
更新2:
これが、あなたが尋ねるすべてを行う方法だと思います。最初にカスタムラムダ関数を作成します。以下では、gはグループを参照します。集約すると、gはシリーズになります。 g.index
をdf.ix[]
に渡すと、dfから現在のグループが選択されます。次に、列Cが0.5未満かどうかをテストします。返されたブールシリーズはg[]
に渡され、基準を満たす行のみが選択されます。
In [95]: cust = lambda g: g[df.loc[g.index]['C'] < 0.5].sum()
In [96]: f = {'A':['sum','mean'], 'B':['prod'], 'D': {'my name': cust}}
In [97]: df.groupby('GRP').agg(f)
Out[97]:
A B D
sum mean prod my name
GRP
0 0.719580 0.359790 0.102004 0.204072
1 0.454824 0.227412 0.034060 0.570441
Ted Petrouの答えの代替案(主に美学)として、私は少しコンパクトなリストを好むことがわかりました。それを受け入れることを考えないでください、それはTedの答えに対するさらに詳細なコメントとコード/データです。 Python/pandasは私の最初の/ベストではありませんが、これがよく読めることがわかりました。
df.groupby('group') \
.apply(lambda x: pd.Series({
'a_sum' : x['a'].sum(),
'a_max' : x['a'].max(),
'b_mean' : x['b'].mean(),
'c_d_prodsum' : (x['c'] * x['d']).sum()
})
)
a_sum a_max b_mean c_d_prodsum
group
0 0.530559 0.374540 0.553354 0.488525
1 1.433558 0.832443 0.460206 0.053313
dplyr
パイプとdata.table
チェーンコマンドを連想させるものです。彼らがより良いと言っているのではなく、私にもっと馴染んでいます。 (私は確かに、これらのタイプの操作に対してより正式なdef
関数を使用することのパワーと、多くの人にとっての好みを認識しています。これは単なる代替であり、必ずしも良いとは限りません。)
Tedと同じ方法でデータを生成しました。再現性のためにシードを追加します。
import numpy as np
np.random.seed(42)
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.374540 0.950714 0.731994 0.598658 0
1 0.156019 0.155995 0.058084 0.866176 0
2 0.601115 0.708073 0.020584 0.969910 1
3 0.832443 0.212339 0.181825 0.183405 1
テッドの答えは素晴らしいです。誰かが興味を持っている場合に備えて、私は結局それの小さなバージョンを使用することになりました。複数の列の値に依存する1つの集計を探している場合に便利です。
df=pd.DataFrame({'a': [1,2,3,4,5,6], 'b': [1,1,0,1,1,0], 'c': ['x','x','y','y','z','z']})
a b c
0 1 1 x
1 2 1 x
2 3 0 y
3 4 1 y
4 5 1 z
5 6 0 z
df.groupby('c').apply(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())
c
x 2.0
y 4.0
z 5.0
私はまだ集計を使用できるので、このアプローチが好きです。おそらく、グループで集計を行う際に複数の列を取得するために適用が必要な理由を私に知らせてくれるでしょう。
今では明らかなように見えますが、目的の列groupbyの直後を選択しない限り、集計関数内からデータフレームのすべての列にアクセスできます。
df.groupby('c')['a'].aggregate(lambda x: x[x>1].mean())
df.groupby('c').aggregate(lambda x: x[(x['a']>1) & (x['b']==1)].mean())['a']
df.groupby('c').aggregate(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())
これがお役に立てば幸いです。
Pandas >= 0.25.0
、名前付き集計pandasバージョン0.25.0
以降では、辞書ベースの集約と名前変更から離れ、Tuple
を受け入れる 名前付き集約 に向かっています。これで、より有益な列名に集約+名前変更を同時に行うことができます。
例:
df = pd.DataFrame(np.random.Rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
a b c d group
0 0.521279 0.914988 0.054057 0.125668 0
1 0.426058 0.828890 0.784093 0.446211 0
2 0.363136 0.843751 0.184967 0.467351 1
3 0.241012 0.470053 0.358018 0.525032 1
名前付き集約でGroupBy.agg
を適用します。
df.groupby('group').agg(
a_sum=('a', 'sum'),
a_mean=('a', 'mean'),
b_mean=('b', 'mean'),
c_sum=('c', 'sum'),
d_range=('d', lambda x: x.max() - x.min())
)
a_sum a_mean b_mean c_sum d_range
group
0 0.947337 0.473668 0.871939 0.838150 0.320543
1 0.604149 0.302074 0.656902 0.542985 0.057681