カラムの値で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('累積和')

