カラムの値で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('カラム名')
を使う。
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
にする
... 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 # ...
... 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)
が使われたデータ構造となる。
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()を用いる。
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
df['切替'] = df['c2'] != df['c2'].shift() df['累積和'] = df['切替'].cumsum() grp = df.groupby('累積和')