カラムの値でDataFrameをグループ分割 - pandas
こうしたい
元のデータ c1=hogeのみのデータ c1=piyoのみのデータ c1=hugaのみのデータ c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 0 hoge 0 1 0 hoge 0 1 2 piyo 4 5 4 huga 8 9 1 hoge 2 3 1 hoge 2 3 5 piyo 10 11 , 2 piyo 4 5 > 3 hoge 6 7 , 3 hoge 6 7 4 huga 8 9 5 piyo 10 11
DataFrame.groupby('カラム名')
を使う。
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 |
import pandas as pd df = pd.DataFrame({ 'c1' : [ 'hoge' , 'hoge' , 'piyo' , 'hoge' , 'huga' , 'piyo' ], 'c2' : [ 0 , 2 , 4 , 6 , 8 , 10 ], 'c3' : [ 1 , 3 , 5 , 7 , 9 , 11 ], }) for c1, sdf in df.groupby( 'c1' ): print (c1) print (sdf) # => # hoge # c1 c2 c3 # 0 hoge 0 1 # 1 hoge 2 3 # 3 hoge 6 7 # huga # c1 c2 c3 # 4 huga 8 9 # piyo # c1 c2 c3 # 2 piyo 4 5 # 5 piyo 10 11 |
行番号が飛び飛びになってるのを振り直したければ、reset_index()
を使う。その際、通常は新規カラムindex
を追加して旧番号を残し、新番号を振るという形になる。旧番号が不要ならdrop=True
にする
1 2 3 4 5 6 7 8 9 10 11 |
... for c1, sdf in gp: sdf = sdf.reset_index() print (sdf) # => # index c1 c2 c3 # 0 0 hoge 0 1 # 1 1 hoge 2 3 # 2 3 hoge 6 7 # ... |
1 2 3 4 5 6 7 8 9 10 11 |
... for c1, sdf in gp: sdf = sdf.reset_index(drop = True ) print (sdf) # => # c1 c2 c3 # 0 hoge 0 1 # 1 hoge 2 3 # 2 hoge 6 7 # ... |
複数のカラムでグループ分け
複数のカラムでグループ分けしたければ、groupby()
にリストを渡す。
返ってくるデータはMultiIndexとなる。要は、forで回したときに、行のインデックスとして、グループ分けに使用したカラムのタプル (c1, c2)
が使われたデータ構造となる。
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 |
import pandas as pd df = pd.DataFrame([[ 'hoge' , 0 , 1 ], [ 'hoge' , 2 , 3 ], [ 'piyo' , 4 , 5 ], [ 'hoge' , 6 , 7 ], [ 'huga' , 2 , 1 ], [ 'piyo' , 1 , 1 ], [ 'hoge' , 0 , 2 ], [ 'hoge' , 0 , 5 ], [ 'hoge' , 0 , 5 ]], columns = [ 'c1' , 'c2' , 'c3' ]) gp = df.groupby([ 'c1' , 'c2' ]) for idx, sdf in gp: print (idx) print (sdf) # => # ('hoge', 0) # c1 c2 c3 # 0 hoge 0 1 # 6 hoge 0 2 # 7 hoge 0 5 # 8 hoge 0 5 # ('hoge', 2) # c1 c2 c3 # 1 hoge 2 3 # ... |
グループ毎に集計
GroupByオブジェクトに対し、sum()やcount()などの集計関数を用いると、グループ毎に合計値やレコード数などを算出できる。
その際、結果の型は<pandas.core.frame.DataFrame>だが、print()すると、グループに用いたカラムのヘッダだけ1段下がったような表示になる。これは、そのカラムがデータフレームのindexとして指定されている状態であることを示す。
(意図的にDataFrameに対しindex列を指定するには、df.set_index('col_name')を用いる。その際、カラムの値は全てユニークでなければ無視される)
集計後、これを元に戻すには、reset_index()を用いる。
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 28 29 30 31 32 33 34 35 36 37 38 |
import pandas as pd df = pd.DataFrame([[ 'hoge' , 0 , 1 ], [ 'hoge' , 2 , 3 ], [ 'piyo' , 4 , 5 ], [ 'hoge' , 6 , 7 ], [ 'huga' , 2 , 1 ], [ 'piyo' , 1 , 1 ], [ 'hoge' , 0 , 2 ], [ 'hoge' , 0 , 5 ], [ 'hoge' , 0 , 5 ]], columns = [ 'c1' , 'c2' , 'c3' ]) gps = df.groupby( 'c1' ). sum () print (gps) # c2 c3 # c1 # hoge 8 23 # huga 2 1 # piyo 5 6 gps = df.groupby([ 'c1' , 'c2' ]). sum () print (gps) # c3 # c1 c2 # hoge 0 13 # 2 3 # 6 7 # huga 2 1 # piyo 1 1 # 4 5 gps = gps.reset_index() print (gps) # c1 c2 c3 # 0 hoge 0 13 # 1 hoge 2 3 # 2 hoge 6 7 # 3 huga 2 1 # 4 piyo 1 1 # 5 piyo 4 5 |
同じ値が連続する区間毎に分割
c2が連続する区間ごとに分割 c1 c2 c1 c2 c1 c2 c1 c2 0 zero 0 0 zero 0 3 three 1 5 five 0 1 one 0 1 one 0 4 four 1 , 6 six 0 2 two 0 > 2 two 0 , 3 three 1 4 four 1 5 five 0 6 six 0
単にgroupby('c2')
すると、(0,1,2)と(5,6)が同じグループになってしまう。
値を1行ずらすshift()
と元を比較することで、値の切り替わる行を1、同じ行を0とした新規列を作れる。その累積和に対してgroupby()
するとよい。
c1 c2 切替 累積和 0 zero 0 NaN 0 1 one 0 0 0 2 two 0 0 0 3 three 1 1 1 4 four 1 0 1 5 five 0 1 2 6 six 0 0 2
1 2 3 |
df[ '切替' ] = df[ 'c2' ] ! = df[ 'c2' ].shift() df[ '累積和' ] = df[ '切替' ].cumsum() grp = df.groupby( '累積和' ) |