脱ヒストグラム!Google Sheets で滑らかな「カーネル密度推定」に入門
📊 関数で一撃。スプシでさくっとカーネル密度推定
例えば A1:A10 に、生徒 10 人分のテストの成績が入ってるとしよう。生徒たちのテストの点数分布の確率密度を図示するために、独自関数 KDE() を使ってみよう。

KDE() を使う様子KDE() は 3 つの引数を取る。
- データ。この場合は、生徒の点数
- 出力する行数。だいたい 50 くらいで十分
- 滑らかさ。基本的には 1 で
引数を設定して入力すると、引数 2 で指定した行数 (例えば 50 行) のデータがズラッと表示される。左列が点数、右列が確率密度だ。

出力の左列を x 軸に、右列を y 軸に折れ線グラフを描けば、推定された確率密度曲線を見れる。

見慣れたヒストグラムと比べてみよう。ヒストグラムも、データの分布を可視化するのに便利な図だけど、集計の始点やビンの幅などの設定を変えると、同じデータでも見た目が全然変わってしまう弱点がある。

それに対し KDE() は、このビンの影響を排除できるのが大きな強み。KDE() の結果は、ヒストグラムの「ビンの幅」に相当する3 つ目の引数 (滑らかさ) の設定に依存するが、ヒストグラムのようにビンの境界に結果が左右されることはなく、データが本来持つ滑らかな分布形状をより忠実に推定できる。
📝 コピペで動く。カスタム関数 KDE() を使ってみよう
Google Sheets の名前付き関数に、以下の関数を登録して使ってね。
= let(
_data, filter(data, isnumber(data)),
n, count(_data),
iqr, quartile(_data, 3) - quartile(_data, 1),
h, 0.9 * min(stdev(_data), iqr / 1.34) * power(n, -0.2),
lo, min(_data) - h * 3,
hi, max(_data) + h * 3,
xs, sequence(rows, 1, lo, (hi - lo) / (rows - 1)),
kernel, map(xs, lambda(x, sum(arrayformula(norm.dist(x, _data, h * smoothness, false))) / n)),
{xs, kernel}
)
設定画面はこんな感じ。引数は順番が大事なので注意してね。


🔍 知って納得。裏の仕組みを覗いてみよう
一見すると複雑な KDE() も、その仕組みは 2 行で説明できるくらい単純だ。
- 各データ点の周りに小さなコブを置く
- 各コブを全部積み重ねる
置いたコブは、正規分布。平均は各データ点で、分散は適当に調整されている。


🛠️ もっと知りたい!詳しい実装の解説
KDE() は、ある種のカーネル密度推定を実行する関数。カーネル関数に正規分布を使い、バンド幅に Silverman のルールを適用したもの。仕様は、R の関数 density() のデフォルトを参考にした。
| 項目 | R の density() のデフォルト | 自作の KDE() の動作 |
|---|---|---|
| カーネル | 正規分布 (ref) | 正規分布のみ |
| バンド幅 | Silverman のルール (ref, ref) | Silverman のルールのみ |
| 出力範囲の端点 | データ端からバンド幅 3 つ分 (ref, ref) | データ端からバンド幅 3 つ分のみ |
| 平滑化 | バンド幅 1 倍 (ref) | 引数で指定 (推奨: 1) |
| 出力点の数 | 512 点 (ref) | 引数で指定 (推奨: 50) |
density() と、自作関数 KDE() を比べてみるとGoogle Sheets で使うことを考えて、データを除いて引数は 2 つとシンプルに抑えてみた。
- 「出力点の数」は、選べないと不便だと思う。引数で指定できるのが自然でしょう
- 「平滑化」は、パラメータスタディする “楽しみ” のために入れてある。実用上は Silverman のルールで足りるでしょうから、不可欠ではないと思うけどね
それにしても、オープンソースの R は実装を参照できて素晴らしいね!統計ソフトで他に有名な Matlab は、商用ソフトなのでコードが非開示で研究できない。Matlab 互換な OSS GNU Octave は普及してるのかな?寡聞にして初めて知りました…😅

1件のコメント