今回はPandasでSQLと同じようなGROUPBYを行う方法について紹介したいと思います。
pandasで年齢データを10歳区切りの年齢データに再集計する
年齢データをpandasで年齢層ごとに集計しようとしたら思いのほか手間取ったので、メモを残しておきます。
今回処理したかったのはこんな感じのデータを20~30,30~40みたいな10歳区切りの年齢層データに集計しなおすことです。軽いデータだったので、for文ごり押しでやってもよかったのですが、pandasのデータフレームでfor文を使うのは大規模データだと悪手なので、今後のことも考えて関数で処理するようにしました。
まずはリストをpandasの一次元配列であるpd.Seriesにしておきます。
import pandas as pd ages = [23, 22, 23, 22, 24, 20, 22, 24, 29, 28, 25, 25, 26, 27, 28, 27, 25, 25, 27, 25, 25, 32, 32, 32, 33, 33, 32, 33, 32, 30, 33, 32,43,47,49,42, 35, 39, 38, 38, 37, 35, 38, 35, 35, 38, 35, 37,67,62,64,56,58,43,61,56,67, 43, 44, 40, 41, 44, 41, 40, 43, 44, 41, 41, 44, 43, 42, 40, 44, 42, 41, 42, 47, 49, 49, 46, 48, 45, 49, 49, 49, 49, 49, 48, 46, 49, 45, 48, 49, 48, 54, 52, 54, 53, 53, 54, 50, 51, 52, 54, 58, 56, 58, 58, 55, 57, 56, 56, 55] ages = pd.Series(ages)
value_counts()で列に対してgroupbyを行う
列に対してのgroupbyはpd.Seriesに用意されているメソッドである.value_counts()
で頻度集計します。これだけインデックスが頻度基準になるため、.sort_index()
で年齢ごと降順で頻度を表示するようにします。
ages = ages.value_counts() ages = ages.sort_index()
これで普通ならばデータ処理完了なわけですが、この処理だと21,23みたいに1歳区切りに頻度集計してしまうので、今回のように5歳区切りの年齢層で分類したいときなど頻度を一定間隔んして分類したいときには使えません。
一定間隔で頻度を区切ったgroupbyは.cutでビニング
こういう場合は.cutでビニング処理をします。1歳ごとに集計された値を10歳ごとにまとめます。以下のようにpandas
の cut
で階級を設定します
import numpy as np c = pd.cut(ages.index,np.arange(20, 60, 5))
そして.cutでビニングした頻度をgroupby
の引数に指定して集計すると頻度を一定間隔でまとめて区切った表が作れます。
h_ages = ages.groupby(c).sum() ages = pd.DataFrame(ages.value_counts().sort_index())
ラベルがバグっている場合はcutの部分で引数にラベルを指定するといいみたいです。
labels = [ "{0} - {1}".format(i, i + 5) for i in range(20, 70, 5) ]
labelの指定が少しめんどくさくてlen(label) = len(bins)-1にしないといけないみたいです。
ages=pd.DataFrame(ages.value_counts().sort_index()) labels = [ "{0} - {1}".format(i, i + 5) for i in range(20, 70, 5) ] c=pd.cut(ages.index,bins=np.arange(20, 75, 5),labels=labels) h_ages=ages.groupby(c).sum() h_ages
すると↓のような感じに集計できます。
0 | |
---|---|
20 – 25 | 13 |
25 – 30 | 8 |
30 – 35 | 15 |
35 – 40 | 10 |
40 – 45 | 21 |
45 – 50 | 19 |
50 – 55 | 11 |
55 – 60 | 10 |
60 – 65 | 3 |
65 – 70 | 2 |
参考:https://qiita.com/kshigeru/items/bfa8c11d1e6487c791d3
コメント