pandas

【pandas】DataFrameをgroupbyでグループ化して集約する方法

【pandas】DataFrameをgroupbyでグループ化して集約する方法

pandasのDataFrameでgroupbyを使ってグループ化した後に集約する方法について解説します。

groupbyによる集約

pandasのDataFrameでデータを扱っているときに集約処理(合計、平均等)をすることがよくあります。データの集約の際には、単純にDataFrame全体を集約をすればよいわけではなく、ある区分ごとのグループ単位で集約処理を実施したい場面に多く直面します。

SQLを少し学んだことがある人であれば「groupby」がすぐ頭に浮かぶと思いますが、pandasのDataFrameでも同様にgroupbyによる集約が可能です。

本記事では、pandasのDataFrameをgroupbyでグループ化して集約する方法について基本的な方法を解説します。以降では、以下の簡単なデータを用いてpandasのDataFrameでどのようにgroupbyによる集約ができるのかを紹介していきます。

pandas DataFrame groupby サンプルデータ

上記データのイメージは、製造業で各シリアルナンバーごとに蓄積されている品質データです。データとしては、各シリアル(serial_no)に対して、品質項目1(quality_val1)と品質項目2(quality_val2)があるような形です。

品目(item_code)は各製品の種類を表すような区分になりますが、今回はこの品目(item_code)をキーにしてグループ化し、各品質データを集約をするということをやってみようと思います。

groupbyの集約の考え方
「分割(split) – 適用(apply) – 結合(combine)」

groupbyによる集約では、「分割(split) – 適用(apply) – 結合(combine)」に分けて処理がされているということを理解しておくとよいかと思います。以下の図を見てみてください。

この例は今回のサンプルデータに対してitem_codeをキーとしてgroupbyする際の処理の適用イメージです。

pandas DataFrame groupby 分割(split) - 適用(apply) - 結合(combine) 考え方

groupbyでの処理は大きく以下のような手順で実行されます。

  1. 分割(split):指定したキー(上記例ではitem_code)に応じてDataFrameを分割してグループを作ります。
  2. 適用(apply):各グループのデータ(上記例ではquality_val1)に対して集約処理を実施します。この例では合計(sum)の例を示していますが、他の集約処理でも同様です。
  3. 結合(combine):各グループの処理結果を結合して、集約結果を完成させます。

上記のような図をイメージしてもらいつつ、以降の具体例を見ていくと、データ処理をイメージしやすいかと思います。では、集約処理をpandasで具体的にどのようにコーディングするかの例を見ていきましょう。

groupbyによるDataFrameの集約

groupbyによるDataFrameの集約の基本的な使い方

groupbyによるDataFrameの集約の基本的な使い方は以下のようになります。

import pandas as pd

quality_result = pd.DataFrame(
    {'serial_no': ['S001', 'S002', 'S003', 'S004', 'S005',
                   'S006', 'S007', 'S008', 'S009', 'S010'],
     'item_code': ['A', 'A', 'B', 'B', 'C', 'A', 'A', 'B', 'C', 'C'],
     'quality_val1': [100, 150, 20, 10, 50, 120, 80, 5, 55, 40],
     'quality_val2': [1, 2, 100, 120, 20, 5, 7, 90, 10, 10]}
)
print(quality_result, '\n')

# item_codeでグループ化
gr = quality_result.groupby('item_code')
print(gr, '\n')

# グループごとに集約を計算
# print(quality_result.groupby('item_code').sum())
print(gr.sum())
【実行結果】
  serial_no item_code  quality_val1  quality_val2
0      S001         A           100             1
1      S002         A           150             2
2      S003         B            20           100
3      S004         B            10           120
4      S005         C            50            20
5      S006         A           120             5
6      S007         A            80             7
7      S008         B             5            90
8      S009         C            55            10
9      S010         C            40            10 

<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001A31B0E3EE0> 

           quality_val1  quality_val2
item_code                            
A                   450            15
B                    35           310
C                   145            40

対象となるDataFrameのgroupbyメソッドにキーとなる列名を与えることで、DataFrameGroupByオブジェクト(pandas.core.groupby.generic.DataFrameGroupBy)が生成されます。

このオブジェクトは上記で説明した分割(split)の処理までがされているものになっているので、当該DataFrameGroupByオブジェクトに対して集約メソッドを実行することで後続の適用(apply)、結合(combine)を処理し、グループ単位の集約処理が実行できます。

上記例では一度grという変数に入れて、合計のsumを呼び出しています。quality_result.groupby(‘item_code’).sum()というように一行で書いても問題ありませんが、DataFrameGroupByをオブジェクト化して用意しておけば、max()等の他の集約関数を必要に応じて簡単に呼び出すことが可能です。

列名を指定して集約する方法

上記の例では、対象となるDataFrame全体に対して処理をしたため、集約メソッドが適用できる列が複数ある場合にはすべての列に対して処理が行われます。今回の例でいうと「quality_val1」「quality_val2」の両方にsumが適用されました。

しかし、実際にはquality_val1のみでよいような場合も多くあると思います。そのような時にgroupbyオブジェクトは、列インデックスの要領で対象を絞り込んで集約することができます。以下の例で見てみましょう。

import pandas as pd

quality_result = pd.DataFrame(
    {'serial_no': ['S001', 'S002', 'S003', 'S004', 'S005',
                   'S006', 'S007', 'S008', 'S009', 'S010'],
     'item_code': ['A', 'A', 'B', 'B', 'C', 'A', 'A', 'B', 'C', 'C'],
     'quality_val1': [100, 150, 20, 10, 50, 120, 80, 5, 55, 40],
     'quality_val2': [1, 2, 100, 120, 20, 5, 7, 90, 10, 10]}
)
print(quality_result, '\n')

# 列を指定して集約する
print(quality_result.groupby('item_code')['quality_val1'].sum())
【実行結果】
  serial_no item_code  quality_val1  quality_val2
0      S001         A           100             1
1      S002         A           150             2
2      S003         B            20           100
3      S004         B            10           120
4      S005         C            50            20
5      S006         A           120             5
6      S007         A            80             7
7      S008         B             5            90
8      S009         C            55            10
9      S010         C            40            10 

item_code
A    450
B     35
C    145
Name: quality_val1, dtype: int64

上記のように[]で対象となる列名を指定することで対象列を絞って集約メソッドの実行ができます。結果はpandasのSeriesオブジェクトになっています。

グループごとに繰り返し処理をする方法

DataFrameGroupByオブジェクトはグループに対する反復をサポートしているため、以下のようにfor文でグループごとに繰り返し処理をすることができます。

import pandas as pd

quality_result = pd.DataFrame(
    {'serial_no': ['S001', 'S002', 'S003', 'S004', 'S005',
                   'S006', 'S007', 'S008', 'S009', 'S010'],
     'item_code': ['A', 'A', 'B', 'B', 'C', 'A', 'A', 'B', 'C', 'C'],
     'quality_val1': [100, 150, 20, 10, 50, 120, 80, 5, 55, 40],
     'quality_val2': [1, 2, 100, 120, 20, 5, 7, 90, 10, 10]}
)
print(quality_result, '\n')

# グループごとに取り出して処理をする
for (item_code, group) in quality_result.groupby('item_code'):
    print(f'item_code: {item_code}')
    print(f'group_data:\n{group}')
    print(f"group['quality_val1'].sum() = {group['quality_val1'].sum()}", '\n')
【実行結果】
  serial_no item_code  quality_val1  quality_val2
0      S001         A           100             1
1      S002         A           150             2
2      S003         B            20           100
3      S004         B            10           120
4      S005         C            50            20
5      S006         A           120             5
6      S007         A            80             7
7      S008         B             5            90
8      S009         C            55            10
9      S010         C            40            10 

item_code: A
group_data:
  serial_no item_code  quality_val1  quality_val2
0      S001         A           100             1
1      S002         A           150             2
5      S006         A           120             5
6      S007         A            80             7
group['quality_val1'].sum() = 450 

item_code: B
group_data:
  serial_no item_code  quality_val1  quality_val2
2      S003         B            20           100
3      S004         B            10           120
7      S008         B             5            90
group['quality_val1'].sum() = 35 

item_code: C
group_data:
  serial_no item_code  quality_val1  quality_val2
4      S005         C            50            20
8      S009         C            55            10
9      S010         C            40            10
group['quality_val1'].sum() = 145 

for文のinにDataFrameGroupByオブジェクトを渡すと「キー」と「グループに該当するDataFrame」が返却されます。

上記の例では、返却されてきたグループのDataFrameに対して、集約関数のsum()を適用することで合計をとっています。このようにグループごとに処理を繰り返したい場合にもgroupbyは便利です。

便利な集約メソッド ~ describe ~

上記でgroupbyの基本的な使い方を見てきましたが、便利な集約メソッドとしてdescribeメソッドを紹介しておきます。describeメソッドは他の集約メソッドと使い方は同様で以下のように使用することができます。

import pandas as pd

quality_result = pd.DataFrame(
    {'serial_no': ['S001', 'S002', 'S003', 'S004', 'S005',
                   'S006', 'S007', 'S008', 'S009', 'S010'],
     'item_code': ['A', 'A', 'B', 'B', 'C', 'A', 'A', 'B', 'C', 'C'],
     'quality_val1': [100, 150, 20, 10, 50, 120, 80, 5, 55, 40],
     'quality_val2': [1, 2, 100, 120, 20, 5, 7, 90, 10, 10]}
)
print(quality_result, '\n')

# describeメソッド
print(quality_result.groupby('item_code')['quality_val1'].describe())
【実行結果】
  serial_no item_code  quality_val1  quality_val2
0      S001         A           100             1
1      S002         A           150             2
2      S003         B            20           100
3      S004         B            10           120
4      S005         C            50            20
5      S006         A           120             5
6      S007         A            80             7
7      S008         B             5            90
8      S009         C            55            10
9      S010         C            40            10 

           count        mean        std   min   25%    50%    75%    max
item_code                                                               
A            4.0  112.500000  29.860788  80.0  95.0  110.0  127.5  150.0
B            3.0   11.666667   7.637626   5.0   7.5   10.0   15.0   20.0
C            3.0   48.333333   7.637626  40.0  45.0   50.0   52.5   55.0

上記例で見ると分かるように、describeメソッドはcount, mean, std, min, 25%, 50%, 75%, maxというようにデータの特徴を見る際によく確認する項目をまとめて集計します。describeを覚えておくと手軽にデータの特徴を確認することができます。

pandasに組み込みで使用できる便利な集約メソッドには上記で紹介したものの他にも、以下のようなものがあります。必要に応じて選択して使用してください。

集約メソッド概要説明
describe()データの統計情報をまとめて計算する。
(count, mean, std, min, 25%, 50%, 75%, max)
sum()合計を計算する。
count()要素数を計算する。
mean()平均値を計算する。
median()中央値を計算する。
std()標準偏差を計算する。
var()分散を計算する。
min()最小値を計算する。
max()最大値を計算する。
first()最初の要素を計算する。
last()最後の要素を計算する。
Note

pandas.DataFrame.groupbyの公式ドキュメントの記載はこちらを参照してください。

また、groupyに関連したピボットテーブルの扱いについても「DataFrameのpivot_tableでピボットテーブルを扱う」でまとめていますので興味があれば参考にしてください。