Courses
分布を可視化しようとして、ビン幅を変えるたびに形が変わるヒストグラムに悩まされたことはありませんか?
よくある流れはこうです。ビンを10にすると滑らかな曲線に見えるのに、30にすると複数のピークが現れる。データは同じでも、ビン数が違えば解釈も変わります。これがヒストグラム最大の問題点です。分布そのものではなく、その一つの見え方しか示さないのです。そしてその見え方は、任意に設定したパラメータに左右されます。
KDE(カーネル密度推定)は別のアプローチを取ります。データをビンに分割するのではなく、各データ点に小さな滑らかな曲線を載せ、それらを合計します。こうして背後にある分布の単一で連続的な推定が得られます。
この記事では、KDEの直感的な理解、数式のウォークスルー、スムーズさを制御するバンド幅の説明、そしてPythonとRでの実践例を紹介します。
ヒストグラムが初めての方は、まず包括的ガイド「Frequency Histograms」をご覧ください。
カーネル密度推定とは?
カーネル密度推定は、データセットの確率密度関数を推定するためのノンパラメトリック手法です。
ノンパラメトリックである点が肝です。
パラメトリック手法では、データが特定の分布(正規分布、指数分布など)に従うと仮定し、その仮定に合わせてパラメータを当てはめます。この仮定が間違っていれば、モデルも間違います。KDEはそのような仮定を置きません。データに語らせ、観測値から直接、背後にある分布の推定を構築します。
出力は、値がどこに現れやすいか、そしてその程度を示す滑らかな曲線です。曲線の高い部分は密度が高い領域、低い部分は疎な領域を意味します。
なぜカーネル密度推定を使うのか?
ヒストグラムは分布の可視化におけるデフォルトの道具ですが問題があります。見える形が、選んだビン数に依存する点です。そしてそのビンの数は自分で決めなければなりません。同じデータセットでも、異なるビン数を選ぶだけで、まったく違う結論になることがあります。
KDEでは、データを無理にビンに押し込むのではなく、事前に設定した恣意的なパラメータに左右されない滑らかで連続的な曲線を生成します。
そのため、次のような場面で便利です。
- ビン幅バイアスなしで分布の全体像を可視化する
- 複数のピーク、歪み、重い裾といったパターンの検出
- 同じプロット上でグループ間の分布を比較する
- モデリング前にデータの姿をより明確に把握する
KDEの直感
各データ点の上に小さく滑らかな曲線を置きます。この曲線をカーネルと呼びます。そして、それら個々の曲線をすべて足し合わせます。
すると、データの密度を示す単一の滑らかな曲線が得られます。点が密集するところでは複数のカーネルが重なって積み上がるため曲線は高くなり、データが疎なところでは重なりがほとんどなく低くなります。すべての点が最終的な推定に等しく寄与します。
たとえば、あるクラスの期末試験の得点を記録したとします。ヒストグラムでビン分けする代わりに、KDEは各得点に小さな滑らかな曲線を配置します。得点が集まる(たとえば70〜75あたり)と曲線が積み上がって推定値が高くなります。95点を取った1人の学生は、テールに小さな山を一つ加えるだけです。
下の図がまさにそれを示しています。ほとんどの学生は平均付近に分布し、1人だけ非常に高得点でした。

KDEの可視化
カーネル密度推定の数式
KDEの数式は見た目ほど怖くありません。

KDEの数式
各記号の意味は次のとおりです。
-
nはデータ点の数 -
x_iはデータセット内の各データ点 -
Kはカーネル関数(各点に置く滑らかな曲線) -
hはバンド幅(各カーネルの幅を制御) -
xは密度を評価する位置
平たく言えば、この数式は次のことを言っています。任意の点 x について、各データ点 x_i がどれだけ近いかを見て、その近さをカーネル関数 K で重み付けし、すべての n 個の点で平均します。これを範囲内のすべての x について行えば、密度曲線全体が得られます。
バンド幅 h は K 内の分数の分母にあります。h が小さいほどカーネルは細くなり、ごく近くの点だけが推定に影響します。h が大きいほど影響は広がります。詳細は後ほど説明します。
カーネル関数とは?
カーネルは各データ点に置く滑らかな曲線で、その点の影響が近傍にどう広がるかを定義します。
すべてのカーネルはデータ点を中心にし、距離に基づいて重みを割り当てます。中心に近い点には高い重み、遠い点には低い重み、あるいはゼロが与えられます。その重み付けの形は選ぶカーネルによって変わります。
代表的な選択肢は3つあります。
- ガウシアン: いわゆるベル型の曲線です。データセット内のすべての点に重みを割り当て、距離とともに影響が減衰します。多くのライブラリでデフォルトになっており、実務でもうまく機能します。
- 一様: 平らな長方形です。一定距離内の点には等しい重みを与え、それ以外はゼロにします。単純ですが、推定はあまり滑らかになりません。
- エパネチニコフ: 放物線状で、平均二乗誤差の観点で数学的に最適です。ガウシアンより効率的ですが、実務では見た目の差はほとんどありません。
多くの場合、カーネルの選択は大差ありません。同じバンド幅で異なるカーネルを適用しても、得られる曲線はほぼ同じです。より重要なのはバンド幅で、次にそれを見ていきます。
KDEにおけるバンド幅の役割
バンド幅は、KDEの出力に最も大きな影響を与える唯一のパラメータで、カーネルの種類以上に重要です。
各カーネルの幅を制御します。狭いカーネルは近傍の点しか取り込みません。広いカーネルは影響を広範囲に広げます。その結果、データに忠実に追随する曲線にも、データを滑らかにならす曲線にもなり得ます。
小さいバンド幅
バンド幅が小さいと各カーネルはタイトで細くなります。推定は各データ点に鋭敏に反応し、データの実構造もノイズも拾います。
実際には、小さなピークがいくつもあるトゲトゲした曲線になります。ピークの一部は本物のクラスターですが、平滑化不足による見かけのピークも混ざります。どれがどれか見分けにくいのが問題です。

小さいバンド幅のKDE
大きいバンド幅
バンド幅が大きいと各カーネルは広がります。隣り合うカーネルが重なり、最終的な曲線は滑らかになります。
滑らかすぎると実際の構造が失われます。異なる2つのクラスターが一つの曲線にぼやけたり、重い裾の分布が対称に見えたりします。可視化が本質を隠してしまう可能性があります。

大きいバンド幅のKDE
適切なバンド幅の見つけ方
普遍的に正しいバンド幅はありません。ノイズを除去できる程度に滑らかでありつつ、実在のパターンを消してしまわない値を探すのが目標です。
ほとんどのライブラリは自動バンド幅選択法を備えています。最も一般的なのはSilvermanの経験則です。サンプルサイズと標準偏差に基づいてバンド幅を選びます。概ね正規分布のときはうまく機能しますが、多峰性の分布では過度に平滑化することがあります。
迷う場合は、複数のバンド幅を試して曲線を比較してください。その違いからデータの性質が多く見えてきます。
KDEとヒストグラムの比較
ヒストグラムもKDEもデータの分布を示しますが、その方法は大きく異なります。
ヒストグラム
ヒストグラムはデータを離散的なビンに分割し、各ビンに入る点の数を数えます。高速で直感的、非技術者にも説明しやすい手法です。
問題はビンに対する感度です。ビン数を変えると形が変わります。客観的に正しいビン数というものはなく、その一つの選択だけで異なる結論に至り得ます。
また、ヒストグラムは段差のある不連続な形状になります。ざっと見るには十分ですが、真の基礎的な分布を覆い隠すことがあります。
KDE
KDEはビンを使わず、滑らかで連続的な曲線を与えます。分布の実際の形、すなわち歪み、複数のピーク、重い裾などを、ビン選択に依存して見落としたり誤表現したりすることなく明らかにしやすくなります。
トレードオフとして、KDEは独自のパラメータ(バンド幅)を導入し、計算コストも高くなります。またy軸がカウントではなく確率密度を示すため、概念に不慣れな読者には直感的に説明しづらい側面があります。
使い分けの目安
データの手早く解釈しやすい要約が必要なとき、あるいは受け手が密度推定に慣れていないときはヒストグラムを使ってください。分布の形が重要なとき、たとえばグループ比較や多峰性の検出が目的のときはKDEが適しています。

ヒストグラムとKDEの比較
実務では、両者を併用することがよくあります。カウントはヒストグラムで、形はKDE曲線を上に重ねます。
Pythonでのカーネル密度推定
Pythonでは、すばやくプロットを作るか、推定そのものを細かく制御するかに応じて、KDEの計算・描画にいくつかの方法があります。
seabornでKDEを可視化する
最速でKDEプロットを作るには seaborn.kdeplot() を使います。必要なのはこれだけです。
import seaborn as sns
import numpy as np
np.random.seed(42)
scores = np.concatenate([
np.random.normal(65, 4, 60),
np.random.normal(80, 3, 40)
])
sns.kdeplot(scores, bw_adjust=1)

seabornによるKDE
bw_adjust は自動選択されたバンド幅にスケールをかけるパラメータです。1未満で曲線はタイトに、1より大きいと滑らかになります。seabornが内部で選ぶバンド幅に乗算されるため、生のバンド幅を自分で設定する必要はありません。
y軸はカウントではなく確率密度を示します。曲線は分布の中で相対的にどの値が起こりやすいかを伝えます。高いほど、その付近にデータが集中していることを意味します。
scipyでKDEを計算する
プロットだけでなく実際の密度値が必要な場合は、scipy.stats.gaussian_kde を使います。任意の点で評価できる呼び出し可能オブジェクトが得られます。
from scipy.stats import gaussian_kde
import numpy as np
import matplotlib.pyplot as plt
kde = gaussian_kde(scores, bw_method="scott")
x = np.linspace(45, 100, 500)
density = kde(x)
plt.plot(x, density)
bw_method="scott" はスコットのルールでバンド幅を自動選択します。多くのケースで良いデフォルトです。スカラー値を渡して手動でバンド幅を設定することもできます。

scipyとmatplotlibによるKDE
Rでのカーネル密度推定
Rでは、KDEはベース言語に組み込まれており、追加のパッケージは不要です。
density()でKDEを計算する
density() 関数は数値ベクトルを受け取り、KDEオブジェクトを返します。
set.seed(42)
scores <- c(rnorm(60, mean = 65, sd = 4),
rnorm(40, mean = 80, sd = 3))
kde <- density(scores, bw = "SJ")
bw引数はバンド幅の選択を制御します。"SJ" はSheather-Jones法を用い、デフォルトより多峰性の分布をうまく扱います。数値を渡して手動でバンド幅を設定することもできます。
結果は、主に2つの要素を持つリストオブジェクトです。
kde$x: 密度を評価した点の並びkde$y: 対応する密度値
曲線をプロットする
結果をそのまま plot() に渡してください。
plot(kde,
main = "Exam Score Distribution",
xlab = "Score",
ylab = "Density")

RでのKDEプロット
KDEをヒストグラムに重ねるには、まず freq = FALSE を指定して hist() を呼び、次に lines() で曲線を追加します。
hist(scores, freq = FALSE, main = "Histogram + KDE", xlab = "Score")
lines(kde, col = "blue", lwd = 2)

Rでのヒストグラム+KDE
freq = FALSE はヒストグラムを密度スケールにするため、バーと曲線が同じy軸を共有できます。
KDEの利点と限界
KDEは本当に有用な可視化ですが、ヒストグラムの代わりに使う前に知っておくべきトレードオフもあります。
利点
最大の利点は、データの分布について仮定を置かないことです。正規分布や指数分布などを事前に決める必要がありません。形はデータ自体から導かれるため、多峰性の分布など標準的なパラメトリック形状に当てはまらないものにも柔軟に対応できます。
また、出力は段差のある近似ではなく滑らかな連続曲線です。そのため、ヒストグラムではビン選択次第で隠れてしまう、複数のピークや長い裾といったパターンを見つけやすくなります。
さらに、KDEはモデル当てはめを前提とせず生データに対して機能するため、探索的分析の第一歩として有用です。
限界
最大の弱点はバンド幅の選択です。誤ると、推定はノイズを追いかけるか、データの実パターンをならし過ぎてしまいます。Silvermanの経験則のような自動手法は概ね正規的なデータではうまく働きますが、複雑な分布では誤解を招くことがあります。結果を信頼する前に、何種類かのバンド幅を手動で確認するのが一般的です。
スケールが大きいと性能面の問題が出ます。KDEは各評価点ごとに全データ点についてカーネル関数を評価するため、データセットが大きくなるにつれて計算量が急増します。多くの探索的作業では問題になりませんが、数十万件規模のデータでは遅くなり得ます。
境界効果という、より微妙な問題もあります。標準的なKDEはデータが両方向に無限に広がると仮定します。下限が0のように厳密な境界がある場合、推定はその外側へ確率質量が漏れ、端付近の曲線が不自然に低くなります。境界補正付きのKDEもありますが、標準ライブラリではあまり実装されていません。
まとめ
KDEはヒストグラムよりもデータの分布をすっきりと見せてくれます。ビンの選択もパラメトリックな仮定も不要で、データセットに実際に含まれているものを示す滑らかな曲線だけです。
本当に重要なのはバンド幅です。いくつかの値を試し、曲線を比較し、自動オプションも活用しつつ、結論を出す前に推定がデータに関する知見と整合しているかを確認してください。
KDEへの直感を養う最良の方法は、実データで動かしてみることです。よく知っているデータセットを選び、KDEを適用し、ヒストグラムと見比べて、見落としていた点を確かめてください。
データ可視化に関心がありますか?Pythonを使うならコース「Data Visualization with Seaborn」、Rを使うなら「Data Visualization with ggplot2」をご覧ください。
FAQs
カーネル密度推定は何に使われますか?
KDEは、データが正規分布や指数分布のような特定の形に従うと仮定せずに、その確率分布を推定するために使われます。モデリング前にデータの分布を可視化したい探索的データ分析でよく用いられます。異常検知、グループ間の分布比較、統計パイプラインにおける平滑化ステップとしても活用されます。
KDEはヒストグラムとどう違いますか?
ヒストグラムはデータを離散的なビンに分割し、各ビンに入る点の数を数えます。得られる形は選んだビン数に依存するため、恣意的なパラメータに敏感です。KDEは各データ点に滑らかな曲線を置いて合計することで、ビン数に依存しない連続的な推定を提供します。
KDEの利用には大きなデータセットが必要ですか?
KDEは小規模データでも動作しますが、点が非常に少ない場合は推定の信頼性が下がります。観測が少ないと各観測の影響が最終曲線に大きく表れ、実構造とノイズの区別が難しくなります。データセットが大きくなるにつれ、推定は安定し、真の分布をより正確に反映します。
不適切なバンド幅を選ぶとどうなりますか?
バンド幅が小さすぎると、各点に反応しすぎるトゲトゲした曲線になり、データの全体像が見えにくくなります。大きすぎると過度に平滑化され、別々のピークが一つの山に融合して本来の構造が隠れてしまいます。多くのライブラリはSilvermanの経験則のような自動選択法を備えていますが、結論を出す前にいくつかの値を手動で試すのが良い実践です。
データの境界付近でもKDEはうまく機能しますか?
標準的なKDEはデータが両方向に無限に広がりうると仮定します。そのため、年齢・所得・0未満にならないカウントなど、下限や上限がある変数では問題が生じます。境界付近では有効範囲外へ確率質量が漏れ、端で曲線が不自然に低くなります。境界補正つきのKDEもありますが、すべてのライブラリで利用できるわけではなく、追加の設定が必要です。