目次

カラムの値で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

http://stackoverflow.com/questions/27900733/python-pandas-separate-a-dataframe-based-on-a-column-value

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('累積和')