怠惰な努力家

くいっぱぐれないように日々勉強していることを記すブログ

Python Pandas groupbyは便利

はじめに

データ分析していると、「集計後のデータがある条件にマッチする行だけ抽出したい」、「集計後のデータを集計前の対応する行に追加したい」などと思う場面があると思います。
それって、Pandasのgroupbyを使うと簡単にできます。みなさんご存じかもしれませんが、ちょっと使っていて便利だと思ったので共有したいと思います。

集計後のデータがある条件ににマッチする行だけ抽出したい

言葉で説明するとわかりにくいので実際に以下を例にしてみます。
Labelごとで集計してValueの合計が100より大きい場合、元のデータから対象の行を抽出する。
f:id:shoulders-of-giants:20190909031432p:plain
AのValueの合計は190(>100)、BののValueの合計は190(>100ではない)、CのValueの合計は120(>100)。このため、Label AとLabel Cの行を抽出するという動きです。
これは簡単にDataFrame.groupby.filterを使うことで実現できます。

df_test=pd.DataFrame([['A',50],['A',100],['A',40],['B',80],['B',10],['C',120]],columns=['Label','Value'])

df_test.groupby(by=["Label"]).filter(lambda x: sum(x["Value"])>100)

  Label Value
0 A 50
1 A 100
2 A 40
5 C 120 

filterを知らずにやろうとするといったん集計したデータを作ってjoinさせた上で特定の条件で絞り込む必要があります。

集計後のデータを使って集計前の対応する行ごとで計算したい

各グループごとでその個別の行の割合を出したいときですね。
f:id:shoulders-of-giants:20190909035030p:plain
これはDataFrame.groupby.applyを使うことで実現できます。

 def calcPercent(x):
    x['Percentage']=x['Value']/x['Value'].sum()
    return x

df_test.groupby(by=["Label"]).apply(calcPercent)
  Label Value Percentage
0 A 50 0.263158
1 A 100 0.526316
2 A 40 0.210526
3 B 80 0.888889
4 B 10 0.111111
5 C 120 1.000000

集計後のデータを集計前の対応する行に追加したい

こちらも言葉で説明するとわかりにくいので実際に以下を例にしてみます。
Labelごとで集計した合計値を元のデータフレームに追加する。
f:id:shoulders-of-giants:20190909033455p:plain
さきほどよりシンプルですが、これはDataFrame.groupby.transformを使うことで実現できます。

df_test["Total"]=df_test.groupby(by=["Label"])["Value"].transform('sum')
  Label Value Total
0 A 50 190
1 A 100 190
2 A 40 190
3 B 80 90
4 B 10 90
5 C 120 120

列ごとで異なる集計をしたい

言葉の通りですね。
f:id:shoulders-of-giants:20190909040055p:plain
これはDataFrame.groupby.aggregateを使うことで実現できます。
列名と集計方法のDictonaryをaggregateに渡します。

df_test=pd.DataFrame([['A',50,10],['A',100,20],['A',40,30],['B',80,40],['B',10,50],['C',120,60]],columns=['Label','Value','Value2'])
df_test.groupby(by=["Label"]).aggregate({'Value':'sum','Value2':'mean'})
   Value Value2
Label
A   190 20
B   90 45
C   120 60

一つの列に対して異なる集計をしたい

f:id:shoulders-of-giants:20190909040407p:plain
これもDataFrame.groupby.aggregateを使うことで実現できます。
集計方法のListをaggregateに渡します。

df_test=pd.DataFrame([['A',50],['A',100],['A',40],['B',80],['B',10],['C',120]],columns=['Label','Value'])
 df_test.groupby(by=["Label"]).aggregate(['sum','max'])
    Value
  sum max
Label
A  190 100
B  90 80
C  120 120

以上です。