pandas の cut でビニング(階級分け)する

連続値を区間に分けてカテゴリ化することを「ビニング」や「階級分け」といいます。pandas の cut を使えば、数値データを簡単にグループ分けできます。

基本的な使い方

cut は値を指定した区間に分類します。

import pandas as pd

df = pd.DataFrame({
    'score': [35, 55, 72, 88, 95, 45, 68]
})

df['grade'] = pd.cut(df['score'], bins=[0, 40, 60, 80, 100])
print(df)

bins で境界値を指定すると、その区間ごとにデータが分類されます。結果は (0, 40](40, 60] のような区間のカテゴリになります。カッコの向きは「その値を含まない」、角括弧は「含む」を意味します。

ラベルを付ける

区間の表示名をカスタマイズするには labels を使います。

df['grade'] = pd.cut(
    df['score'], 
    bins=[0, 40, 60, 80, 100],
    labels=['不可', '', '', '']
)
print(df)

labels の数は bins で作られる区間の数と一致させる必要があります。区間が 4 つなら labels も 4 つです。

区間の数を自動で決める

境界値を直接指定する代わりに、分割数だけを指定することもできます。

# 4 等分する
df['quartile'] = pd.cut(df['score'], bins=4)

この場合、最小値から最大値までを均等に 4 分割します。

境界値の扱いを変更する

デフォルトでは左側が開区間(含まない)、右側が閉区間(含む)です。right=False で逆にできます。

# 左を含み、右を含まない [0, 40), [40, 60), ...
df['grade'] = pd.cut(df['score'], bins=[0, 40, 60, 80, 100], right=False)

また、最小値が最初の区間に含まれない問題を避けるには include_lowest=True を使います。

df['grade'] = pd.cut(df['score'], bins=[0, 40, 60, 80, 100], include_lowest=True)

qcut で分位数に基づいて分割する

qcut を使うと、データの分位数に基づいて分割できます。

# 4 分位数(四分位数)で分割
df['quartile'] = pd.qcut(df['score'], q=4)

cut は区間の幅が均等になりますが、qcut は各区間に含まれるデータ数が均等になります。

cut

指定した境界値で分割する。各区間の幅は均等だが、データ数は偏る可能性がある。

qcut

分位数で分割する。各区間のデータ数は均等だが、区間の幅は異なる。

# cut: 区間幅が均等
pd.cut(df['score'], bins=4)

# qcut: データ数が均等
pd.qcut(df['score'], q=4)

実用例:年齢を年代に変換する

df = pd.DataFrame({
    'age': [23, 35, 47, 52, 18, 65, 41]
})

df['age_group'] = pd.cut(
    df['age'],
    bins=[0, 20, 30, 40, 50, 60, 100],
    labels=['10代', '20代', '30代', '40代', '50代', '60代以上']
)

ビニングは機械学習の前処理でも頻繁に使われます。連続値をカテゴリに変換することで、モデルが学習しやすくなるケースがあります。