K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

数値データのビニング

01.3.1

数値データのビニング

最終更新 2025-11-03 読了時間 2 分
まとめ
ビニング(離散化)は、連続値の特徴量をいくつかの区間(ビン)に区切り、各値を「どの区間に入るか」という順序付きカテゴリに置き換える操作です。区切り方は 2 通りで、pandas.cut は区間の幅を等しくする等幅ビン、pandas.qcut は各区間の件数を等しくする等頻度ビンを作ります。このページでは右に長く裾を引く所得データ 1,000 件を 5 分割し、等頻度ビンの境界線がヒストグラムの山の高いところに密に並ぶこと、等幅ビンでは端の区間がほぼ空になることを確認します。読み終えると、「所得五分位」のような説明しやすい区分を作るときに qcut を選ぶ理由と、線形モデルに非線形効果を持たせる使い方を説明できるようになります。

直感 #

スマホの 1 日の使用時間を 10 人分集めたら 30, 45, 50, 60, 70, 80, 95, 120, 180, 600 分でした。これを「レベル 1〜5」に分けるとき、幅をそろえて 114 分ずつ区切ると、レベル 1 に 8 人、レベル 2 に 1 人、レベル 5 に 1 人、レベル 3 と 4 は 0 人になります。人数をそろえて 2 人ずつ区切ると、境界は 50, 70, 95, 180 分とばらばらになりますが、どのレベルにも 2 人ずつ入ります。前者が pandas.cut、後者が pandas.qcut です。年収や売上のように右に裾を引くデータでは、1 人の極端な値が等幅ビンの幅を決めてしまうため、等頻度ビンのほうが安定します。

等幅ビンと等頻度ビン #

数値特徴量 \(x_1, \dots, x_n\) を区間 \(I_k\) に分割し、各 \(x_i\) を該当する区間ラベルに置き換えます。等幅ビン(pandas.cut)は \(x_{\min}\) から \(x_{\max}\) までを \(k\) 等分するので、境界は \(x_{\min} + j (x_{\max} - x_{\min}) / k\) に並び、区間の長さの意味(1 区間 = 100 万円など)が保たれます。等頻度ビン(pandas.qcut)は並べ替えたデータを同件数になるよう分割するので、境界は第 \(100j/k\) パーセンタイル、つまり四分位数・五分位数の仲間です。重い裾を持つ分布では等頻度ビンが安定し、距離の概念を保ちたい場合は等幅ビンが向いています。

何が起きているか(ステップで見る) #

コードを読む前に、この手法が図の上で何をしているかを追ってみましょう。「再生」を押すか、スライダーを動かすと 1 手順ずつ進みます。

見るポイント:同じデータでも階級の幅で山の見え方が変わる。階級数 4・8・16・40 を試して形が安定する幅を選ぶ。ビニングの「ビン」はこの階級そのもの。

分位ビンを可視化する #

次のコードは対数正規分布に従う所得 1,000 件を作り、等頻度 5 分割の境界 4 本をヒストグラムに重ねて描きます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

rng = np.random.default_rng(18)
income = rng.lognormal(mean=10, sigma=0.55, size=1_000)
quantile_edges = np.quantile(income, np.linspace(0, 1, 6))

plt.hist(income, bins=40, color="steelblue", alpha=0.85)
for edge in quantile_edges[1:-1]:
    plt.axvline(edge, color="darkorange", linestyle="--", alpha=0.8)
plt.title("等頻度(5分割)のビン境界")
plt.xlabel("所得")
plt.ylabel("件数")
plt.show()

分位ビンを可視化するの図

qcut と cut の比較 #

次のコードは同じ所得データを qcut と cut で 5 分割し、ビンごとの件数を棒グラフで並べます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
quantile_bins = pd.qcut(income, q=5)
width_bins = pd.cut(income, bins=5)

quantile_counts = pd.Series(quantile_bins).value_counts(sort=False)
width_counts = pd.Series(width_bins).value_counts(sort=False)
indices = np.arange(len(quantile_counts))

fig, ax = plt.subplots(figsize=(7, 4))
ax.bar(indices - 0.2, quantile_counts.values, width=0.4, color="seagreen", alpha=0.8, label="qcut(等頻度)")
ax.bar(indices + 0.2, width_counts.values, width=0.4, color="firebrick", alpha=0.6, label="cut(等幅)")
ax.set_xticks(indices)
ax.set_xticklabels([f"ビン {i}" for i in indices])
ax.set_ylabel("サンプル数")
ax.legend()
ax.set_title("ビンごとのサンプル数")
plt.tight_layout()
plt.show()

qcut と cut の比較

実行結果の読み方 #

等頻度ビン(緑)は各区間で件数がほぼ 200 件に揃います。一方、等幅ビン(赤)では中央値付近を含む最初の 1〜2 区間にサンプルが集中し、裾の区間はほぼ空になります。cut の 5 本の棒の高さがばらばらなのは、最大値側の数件が区間の幅を決めているためで、所得五分位のような「各区分に同じ人数」という説明をしたいなら qcut を選びます。

使いどころと落とし穴 #

等幅ビンを使う場合は極端な外れ値をあらかじめ np.clip などで上限で切っておかないと、ほとんどのビンが空になります。訓練時に得たビン境界は retbins=True で取り出して保存し、推論時にも同じ境界を pd.cut(bins=...) で適用して再現性を保ちます。決定木系モデルは分割点を自分で探すのでビニングは必須ではありませんが、線形モデルやロジスティック回帰に「所得が高い区分ほど効果が大きい」のような非線形効果を持たせたいときに、ビンを One-Hot 化して渡す方法が有効です。同じ値が大量にあるデータでは qcut の境界が重なって ValueError になるので、duplicates="drop" を指定するか区間数を減らします。