pandas の cut、qcutは配列データの分類に使います。分類の方法は
- cut境界値を指定して分類する。(ヒストグラムのビン指定と言ったほうが判りやすいかもしれません)
- qcut値の大きさ順にn等分する
という違いがあります。
cut で分類
たとえば、数字が適当に並べてあったとします。
1 2 3 4 5 6 |
import numpy as np data = np.random.randint(1, 11, 6) #--------------------------------------- # array([10, 1, 9, 3, 4, 8]) #--------------------------------------- |
この数値を以下の 3 つのレンジに分類したいとします。
その場合、cutの境界値として [0, 3, 7, 10] を指定します。
境界値を決めて実行
実行してみます。
1 2 3 4 5 6 |
import pandas as pd pd.cut(data, [0, 3, 7, 10]) #----------------------------------------------------- # [(7, 10], (0, 3], (7, 10], (0, 3], (3, 7], (7, 10]] #----------------------------------------------------- |
茶色の部分が出力結果ですが、ちょっと判りづらいですね。(3, 7] とか (0, 3] とか・・・
これはレンジを表しており
- (0, 3] は 1~3 を意味します。
- (3, 7] は 4~7 を意味します。
- (7, 10] は 8~10 を意味します。
” ( ” は下限値を含まない、” ] ” は上限値を含むという意味です。
cut は、6個の各データ(10、1、9、3、4、8)をレンジに振り分けてるわけです。出力の意味は
1 2 3 4 5 6 |
10 1 9 3 4 8 ・・・ データ ⇣ ⇣ ⇣ ↓ ↓ ↓ #-------------------------------------------------- # 8~10, 1~3, 8~10, 1~3, 4~7, 8~10 ・・・ レンジ # (7, 10], (0, 3], (7, 10], (0, 3], (3, 7], (7, 10] #-------------------------------------------------- |
ということです。
補足
1 2 3 4 |
pd.cut(data, [0, 3, 7, 10], right=False) #-------------------------------------------------- # [NaN, [0, 3), [7, 10), [3, 7), [3, 7), [7, 10)] #-------------------------------------------------- |
[0, 3) は 0~2 を意味します。
[3, 7) は 3~6 を意味します。
[7, 10) は 7~9 を意味します。
NaN が出ているのは、数値 10 がどのレンジにも属さないためです。
分類結果の使い道
なんのために分類したかといえば、レンジごとの件数を数えたいからです。value_counts を使って
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
pd.cut(data, [0, 3, 7, 10]).value_counts() #----------------- # (0, 3] 2 # (3, 7] 1 # (7, 10] 3 #----------------- # 件数はそれぞれ、2件、1件、3件でした。 # # 見やすいようにラベルを付けられます # labels=['Low', 'Middle', 'High'] pd.cut(data, [0, 3, 7, 10], labels=labels).value_counts() #----------------- # Low 2 # Middle 1 # High 3 #----------------- |
ヒストグラムのビン指定と同じです。cut の出力を DataFrame と組み合わせて groupby のキーにすることもできます。(下に例あり)
qcut で分類
qcut は値の大きさ順にデータをn等分してくれます。が、データに重複があるとちょっと怪しくなります。
重複がないケース
まず、重複がない正常ケースを見てみます。
1 2 3 4 5 6 7 8 9 10 11 12 |
data = [1, 10, 100, 2, 20, 200, 3, 30, 300] # # n等分の n は第二引数で指定 # pd.qcut(data, 3).value_counts() #---------------------- # [1, 7.667] 3 # (7.667, 53.333] 3 # (53.333, 300] 3 #---------------------- |
期待通り9個のデータが3等分されています。
重複があるケース
でも、重複があると
1 2 3 4 5 6 7 8 |
data = [1, 10, 2, 2, 2, 2, 2, 30, 300] pd.qcut(data, 3).value_counts() #---------------------- # [1, 2] 6 # (2, 4.667] 0 # (4.667, 300] 3 #---------------------- |
ちょっと偏ります。
さらに重複データを増やすと
1 2 3 4 5 6 7 |
data = [1, 2, 2, 2, 2, 2, 2, 2, 300] pd.qcut(data, 3).value_counts() #------------------------------------------------ # ValueError: Bin edges must be unique: array([ 1., 2., 2., 300.]) #------------------------------------------------ |
なんと、エラーになります。(注 Python 2.7.10 で確認)
データが偏って重複の多い場合は使わないほうがいいのかもしれません。
DataFrame で cut を応用
DataFrame のグルーピング(groupby)キーを cut で作ってみます。
学生の身長データがあったとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from pandas import DataFrame df = DataFrame([['AA',170],['BB',184],['CC',162], ['DD',173],['EE',158],['FF',175], ['GG',190],['HH',154],['II',168], ['JJ',180]], columns=['Student','Height']) #--------------------- # Student Height # 0 AA 170 # 1 BB 184 # 2 CC 162 # 3 DD 173 # 4 EE 158 # 5 FF 175 # 6 GG 190 # 7 HH 154 # 8 II 168 # 9 JJ 180 #--------------------- |
このデータを 小柄、普通、長身 の3つに分類して、その人数を数えたいとします。
分類は cut で
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
labels=['Small', 'Middle', 'High'] pd.cut(df.Height, [150, 165, 175, 200], labels=labels) #--------------------- # 0 Middle # 1 High # 2 Small # 3 Middle # 4 Small # 5 Middle # 6 High # 7 Small # 8 Middle # 9 High #--------------------- |
結果を df にマージして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 |
df['key'] = pd.cut(df.Height, [150, 165, 175, 200], labels=labels) #---------------------------- # Student Height key # 0 AA 170 Middle # 1 BB 184 High # 2 CC 162 Small # 3 DD 173 Middle # 4 EE 158 Small # 5 FF 175 Middle # 6 GG 190 High # 7 HH 154 Small # 8 II 168 Middle # 9 JJ 180 High #---------------------------- # # 人数を数えます # df.groupby('key').size() #---------------------------- # key # Small 3 # Middle 4 # High 3 #---------------------------- |
df にマージしたくなければ
1 2 3 4 5 6 7 8 9 |
key = pd.cut(df.Height, [150, 165, 175, 200], labels=labels) df.groupby(key).size() #---------------------------- # Height # Small 3 # Middle 4 # High 3 #---------------------------- |
結果は同じです。仮想的に key がマージされ、その結果が groupby されます。