k-means クラスタリングでは、クラスタ数 $k$ をあらかじめ決める必要があります。Stata の `cluster stop` コマンドは Calinski-Harabasz(CH)指標を算出してくれるので、複数の $k$ を試して値を比較し、最適な $k$ を選ぶことができます。 エルボー法(WSS)による方法は [[Stata - k-meansクラスタリングのエルボー法]] を参照してください。 ## Calinski-Harabasz(カリンスキ・ハラバシュ)指標とは Calinski-Harabasz pseudo-$F$ は、クラスタ間の分散とクラスタ内の分散の比率です。 $CH = \frac{SS_B / (k - 1)}{SS_W / (N - k)}$ - $SS_B$:クラスタ間の平方和(グループ同士がどれだけ離れているか) - $SS_W$:クラスタ内の平方和(グループ内のばらつき) - $k$:クラスタ数 - $N$:観測数 値が大きいほど、クラスタがよく分離されていることを意味します。複数の $k$ で CH を計算して、最も大きい値の $k$ を選びます。 ## 注意:変数の標準化 k-means はユークリッド距離を使うため、スケールの異なる変数をそのまま投入すると、値の範囲が大きい変数がクラスタリング結果を支配してしまいます。 例えば `auto` データセットの `mpg`(12--41)と `weight`(1760--4840)をそのまま使うと、`weight` の差のほうが圧倒的に大きいため、`mpg` の違いはほぼ無視されます。 `egen` の `std()` 関数で平均 0、標準偏差 1 に標準化してからクラスタリングに投入します。 ## 完全なコード 以下のコードを do ファイルにコピーして実行してください。 ```stata clear all set more off sysuse auto, clear * --- 変数の標準化 --- * mpg と weight はスケールが大きく異なるため標準化が必要 egen std_mpg = std(mpg) egen std_weight = std(weight) summarize mpg weight std_mpg std_weight * --- K-means クラスタリング (k=3,4,5) --- cluster kmeans std_mpg std_weight, k(3) name(km3) cluster kmeans std_mpg std_weight, k(4) name(km4) cluster kmeans std_mpg std_weight, k(5) name(km5) * --- Calinski-Harabasz 指標で比較 --- * 値が大きいほどクラスタの分離が良好であることを示す cluster stop km3, rule(calinski) cluster stop km4, rule(calinski) cluster stop km5, rule(calinski) ``` ## コードの解説 ### 標準化 ```stata egen std_mpg = std(mpg) egen std_weight = std(weight) ``` `std()` は変数を平均 0、標準偏差 1 に変換します。`summarize` で確認すると、標準化後の変数は `Mean` がほぼ 0、`Std. dev.` が 1 になっています。 ``` . summarize mpg weight std_mpg std_weight Variable | Obs Mean Std. dev. Min Max -------------+--------------------------------------------------------- mpg | 74 21.2973 5.785503 12 41 weight | 74 3019.459 777.1936 1760 4840 std_mpg | 74 -7.00e-09 1 -1.606999 3.40553 std_weight | 74 -1.81e-09 1 -1.620522 2.342454 ``` ### name() オプション ```stata cluster kmeans std_mpg std_weight, k(3) name(km3) ``` `name()` を指定しないと、Stata は `_clus_1`、`_clus_2`、`_clus_3` のように自動で変数名をつけます。複数の $k$ を試す場合、どの変数がどの $k$ に対応するか分からなくなるため、`name(km3)` のように明示的に名前をつけておきます。 ### cluster stop ```stata cluster stop km3, rule(calinski) ``` `cluster stop` は指定したクラスタ解に対して stopping rule の指標を計算するコマンドです。`rule(calinski)` で CH 指標を指定しています。 k-means の場合、`cluster stop` は 1 つの解に対して 1 つの値しか返しません。そのため、$k$ ごとに別々に実行して結果を比較する必要があります。 ## 実行結果 ``` . cluster stop km3, rule(calinski) +---------------------------+ | | Calinski/ | | Number of | Harabasz | | clusters | pseudo-F | |-------------+-------------| | 3 | 107.67 | +---------------------------+ . cluster stop km4, rule(calinski) +---------------------------+ | | Calinski/ | | Number of | Harabasz | | clusters | pseudo-F | |-------------+-------------| | 4 | 76.06 | +---------------------------+ . cluster stop km5, rule(calinski) +---------------------------+ | | Calinski/ | | Number of | Harabasz | | clusters | pseudo-F | |-------------+-------------| | 5 | 102.26 | +---------------------------+ ``` | $k$ | CH pseudo-$F$ | |:---:|:---:| | 3 | 107.67 | | 4 | 76.06 | | 5 | 102.26 | $k = 3$ が最大値(107.67)を示しており、3 群への分割が最も良好なクラスタ分離を達成しています。 ## 階層的クラスタリングとの違い `cluster stop` は階層的クラスタリング(`cluster wardslinkage` 等)で使うと、1 回の実行で複数の $k$ に対する CH を一覧表示してくれます。 ```stata cluster wardslinkage std_mpg std_weight, name(ward) cluster stop ward, rule(calinski) ``` ``` +---------------------------+ | | Calinski/ | | Number of | Harabasz | | clusters | pseudo-F | |-------------+-------------| | 2 | 89.68 | | 3 | 107.67 | | 4 | 76.06 | | ... | ... | +---------------------------+ ``` 階層的クラスタリングでは 1 つのデンドログラムから複数の $k$ を切り出せるため、このような一括比較が可能です。一方、k-means は $k$ ごとに独立した解を作るため、1 解ずつ `cluster stop` を実行する必要があります。 ## よくある間違い ### 標準化を忘れる ```stata * 悪い例:標準化せずに実行 cluster kmeans mpg weight, k(3) ``` `weight` の値域(1760--4840)が `mpg`(12--41)より圧倒的に大きいため、クラスタリング結果がほぼ `weight` だけで決まってしまいます。 ### name() を指定しない ```stata * 悪い例:名前を指定しない cluster kmeans std_mpg std_weight, k(3) cluster kmeans std_mpg std_weight, k(4) cluster kmeans std_mpg std_weight, k(5) cluster stop _clus_4 * ← _clus_4 は存在しない ``` 3 回実行すると `_clus_1`、`_clus_2`、`_clus_3` が作られます。`_clus_4` は存在しないためエラーになります。`name()` で明示的に命名しておくと、このような混乱を避けられます。