DataFrame の groupby は、縦方向にも横方向にもグルーピングできます。

SQL からの連想で縦方向の groupby はイメージしやすいですが、横方向はピンとこなかったりします。
ということで、ここでは、横方向の groupby に絞ってみていきます。
グループ化のキーは列名
グループ化されるのは「列名」が同じ列です。上の図(右側)でいうと、緑の列、黄色の列にそれぞれ同じ名前が付いていると考えてください。
実際のサンプルで試してみます。列名が重複する次のようなデータがあったとして
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import pandas as pd from pandas import DataFrame df=DataFrame([[1,2,3],[4,5,6],[7,8,9]], columns=['aa','bb','bb'], index=['AA','BB','CC']) #-------------------- # aa bb bb ← 列名 bb が重複 # AA 1 2 3 # BB 4 5 6 # CC 7 8 9 #-------------------- |
axis=1 で groupby をかけると bb の2列が集約されます。
1 2 3 4 5 6 7 8 9 10 11 |
list(df.groupby(level=0, axis=1)) #-------------------- # [('aa', aa # AA 1 # BB 4 # CC 7), # ('bb', bb bb # AA 2 3 # BB 5 6 # CC 8 9)] #-------------------- |
集約された結果の sum や mean で、bb 2列分の集計が行われます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
df.groupby(level=0, axis=1).sum() #----------------- # aa bb # AA 1 5 # BB 4 11 # CC 7 17 #----------------- df.groupby(level=0, axis=1).mean() #----------------- # aa bb # AA 1 2.5 # BB 4 5.5 # CC 7 8.5 #----------------- |
オプションが2つ出てきますが
- level オプションは、列が階層化されている場合に 0、1 、2 と指定します。この例では階層化がされていないので 0 固定です。
- axis=1 で「横方向」を指示しています。
列が階層化されている場合
国語と数学のテスト結果があるとします。
テストは、中間テストと期末テストの2回です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
AAさん、BBさん、CCさんのテスト結果 multi = pd.MultiIndex.from_arrays([['Language','Language','Math','Math'], ['test1','test2','test1','test2']]) df = DataFrame([[80,70,60,50],[70,75,90,85],[40,30,50,55]], index=['AA','BB','CC'],columns=multi) #------------------------------------- # Language Math # test1 test2 test1 test2 # AA 80 70 60 50 # BB 70 75 90 85 # CC 40 30 50 55 #------------------------------------- |
列が
- 国語(Language)、数学(Math)
- 中間テスト(test1)、期末テスト(test2)
の2階層になっています。
教科で groupby
教科でグルーピングし、平均点を確認してみます。
集約キーは level オプションで指定、上側の列なら 0、下側の列なら 1 です。
教科は上側なので 0 を指定し
1 2 3 4 5 6 7 |
df.groupby(level=0, axis=1).mean() #------------------------------------- # Language Math # AA 75.0 55.0 # BB 72.5 87.5 # CC 35.0 52.5 #------------------------------------- |
できました。
キーを変えてみる
中間テスト、期末テストで集計したければ level オプション を 1 にします。
1 2 3 4 5 6 7 |
df.groupby(level=1, axis=1).mean() #--------------------------------------- # test1 test2 # AA 70.0 60.0 # BB 80.0 80.0 # CC 45.0 42.5 #--------------------------------------- |
groupby を使わずに
実は、上の処理は groupby を使わずもっと簡単に記述できます。
1 2 3 4 5 6 7 |
df.mean(level=0, axis=1) #------------------------------------- # Language Math # AA 75.0 55.0 # BB 72.5 87.5 # CC 35.0 52.5 #------------------------------------- |
mean() にオプションを指定するだけです。
内部では groupby が動いているらしいのですが(参考:pythonによるデータ分析入門 P173)、ソース上に groupby は現れません。こちらのほうが見た目はすっきりします。ただ、知らない人が見たら何を平均しているのか判りづらいかもしれません。
階層が深い場合
列が3重、4重に階層化されている場合、 level オプションを配列で指定できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
multi = pd.MultiIndex.from_arrays([['Language','Language','Language','Language','Math','Math'], ['test1','test1','test2','test2','test1','test2'], ['1st','2nd','1st','2nd','1st','1st']]) df = DataFrame([[80,70,60,50,10,20],[70,75,90,85,10,20],[40,30,50,55,10,20]], index=['AA','BB','CC'],columns=multi) #--------------------------------------- # Language Math ← level 0 # test1 test2 test1 test2 ← level 1 # 1st 2nd 1st 2nd 1st 1st ← level 2 # AA 80 70 60 50 10 20 # BB 70 75 90 85 10 20 # CC 40 30 50 55 10 20 #--------------------------------------- # # level オプションを配列で指定 # df.groupby(level=[0, 1], axis=1).sum() #--------------------------------------- # Language Math # test1 test2 test1 test2 # AA 150 110 10 20 # BB 145 175 10 20 # CC 70 105 10 20 #--------------------------------------- |