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を使って集約処理を具体的にどのようにコーディングするか、例を使って見ていきましょう。

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)が生成されます。今回の例では、item_codeを集約のキーとして与えています。

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

上記例では一度分割までされている情報をgrという変数に入れ、合計のsumを呼び出しています。もちろん、quality_result.groupby('item_code').sum()というように一行で書いても問題ありませんが、DataFrameGroupByをオブジェクト化して用意しておけば、sum()以外にも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

上記のように[]で対象となる列名を指定することで対象列を絞って集約メソッドの実行ができます。上記例では、quarlity_val1を指定することで結果としてはquality_val1のみのsum()による集約結果が得られています。

上記結果では、1列だけ取得してきているので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文のinDataFrameGroupByオブジェクトを渡すと「キー」と「グループに該当するDataFrame」が返却されます。for文内当該繰り返しのグループに対するキーがitem_codeに、グループのDataFrameがgroupに入ります。

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

集約メソッド

いろいろな集約メソッド

上記の例では、集約処理のメソッドとしてsum()を使った例でgroupbyの使い方を紹介してきました。pandasでは、多くの便利な集約メソッドが用意されています。集約メソッドの中には以下のようなものがあります。必要に応じて選択して使用してください。

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

便利な集約メソッド describe

いろいろな集約メソッドということで表で紹介しましたが、その中でも便利な集約メソッドとして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を対象データに対して使ってみるだけで、手軽にデータの特徴を確認することができます。

ピボットテーブルの作成

データをグループ化した使う方法としては、ピボットテーブルもあります。ピボットテーブルは、groupbyを用いることで作成可能ですが、pandasでは、pivot_tableメソッドが用意されていて簡単に作成できます。

ピボットテーブルについては「DataFrameのpivot_tableでピボットテーブルを扱う」でまとめていますので参考にしてください。

まとめ

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

集約処理(合計、平均等)の際には、ある区分ごとのグループ単位で集約処理をしたくなりますが、groupbyを使うことで簡単に実現が可能です。本記事では、まず集約処理の考え方を説明して、基本的なgroupbyの使い方を紹介しました。

集約処理は、データ分析で非常に重要な処理の一つです。是非groupbyをうまく使いこなしてほしいと思います。

Note

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