Rで階層的クラスタリングを行う方法を自分用にまとめておきます。
理論は省略。
メイン関数stats::hclust
stats
は基本ロードされるので個別にライブラリを入れる必要は無し。- 第一引数に分析対象から作った
dist
オブジェクト。 - 第二引数にクラスタ間の距離の定義方法
予備処理stats::dist
- 第一引数に分析対象の数値行列。ただし数値行列に変換できるデータフレームでも可
- 第二引数で観測点の間の距離の定義。デフォルトはユークリッド距離
- 分析対象に距離の構造を入れたオブジェクトを生成すると思えばよい
as.dist
でもオブジェクトを作成できる。
この場合は入力データが二点間の距離を表す数値行列である必要がある。
例
iris.dist <- dist(iris[-5]) hc.ward <- hclust(iris.dist, "ward.D") #ward法によるクラスタ間距離の定義 hc.complete <- hclust(iris.dist, "complete") #最遠点によるクラスタ間距離の定義 hc.single <- hclust(iris.dist, "single") #最近点によるクラスタ間距離の定義 #距離を相関で定義することもできる iris.cordist <- as.dist(1-cor(t(iris[-5]))) hc.cor.ward <- hclust(iris.cordist, "ward.D")
注意書き:距離
要するにクラスタリングにおける位相は二層構造になっていて
- 観測の間の距離
- クラスタの間の距離
の二つを定義してやる必要がある。
観測の間の距離はdist
で作れて、オプションでユークリッド距離などLpノルムを設定できる。
ノルムではそれぞれの変数値の差の絶対値が大きく影響する。
絶対値は違ったとしても同じ傾向を持っている観測、たとえば購買頻度や数量は違うが購買傾向が同じ顧客を一つのクラスタにまとめたい場合は、ノルムではなく相関で定義した距離を使う。
結果取得
関数cutree
でクラスタリングする水準を指定しながらクラスタリング結果を取得する。
#クラスタ数で指定 cutree(hc.ward, k = 3) # [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 2 3 2 # [58] 3 2 3 3 3 3 2 3 2 3 3 2 3 2 3 2 2 2 2 2 2 2 3 3 3 3 2 3 2 2 2 3 3 3 2 3 3 3 3 3 2 3 3 2 2 2 2 2 2 3 2 2 2 2 2 2 2 #[115] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 #高さ(距離)でも指定できる cutree(hc.ward, h = 4) #分類の精度確認 library(dplyr) library(tidyr) iris %>% mutate(hc = cutree(hc.ward, k = 3)) %>% count(Species, hc) %>% spread(hc, n, fill = 0) ## A tibble: 3 x 4 # Species `1` `2` `3` # <fct> <dbl> <dbl> <dbl> #1 setosa 50 0 0 #2 versicolor 0 50 0 #3 virginica 0 14 36 # 相関による距離。精度は若干改善している iris %>% mutate(hc = cutree(hc.cor.ward, 3)) %>% count(Species, hc) %>% spread(hc, n, fill = 0) ## A tibble: 3 x 4 # Species `1` `2` `3` # <fct> <dbl> <dbl> <dbl> #1 setosa 50 0 0 #2 versicolor 0 39 11 #3 virginica 0 0 50 iris %>% mutate(hc = cutree(hc.complete, k = 3)) %>% count(Species, hc) %>% spread(hc, n, fill = 0) ## A tibble: 3 x 4 # Species `1` `2` `3` # <fct> <dbl> <dbl> <dbl> #1 setosa 50 0 0 #2 versicolor 0 23 27 #3 virginica 0 49 1 # 相関。こちらも若干改善 hc.cor.comp <- hclust(iris.cordist, "complete") iris %>% mutate(hc = cutree(hc.cor.comp, 3)) %>% count(Species, hc) %>% spread(hc, n, fill = 0) ## A tibble: 3 x 4 # Species `1` `2` `3` # <fct> <dbl> <dbl> <dbl> #1 setosa 50 0 0 #2 versicolor 0 28 22 #3 virginica 0 0 50 #ポンコツ iris %>% mutate(hc = cutree(hc.single, k = 3)) %>% count(Species, hc) %>% spread(hc, n, fill = 0) ## A tibble: 3 x 4 # Species `1` `2` `3` # <fct> <dbl> <dbl> <dbl> #1 setosa 50 0 0 #2 versicolor 0 50 0 #3 virginica 0 48 2
可視化
汎用関数plot
でデンドログラムを書ける。簡単。
plot(hc.ward) plot(hc.comp) plot(hc.sgl)
このデータではwardとcompleteが同じくらいに良い。
ward法の方がクラスタを明確に分割しようとする傾向がある(デンドログラムの落差が激しい)
singleはポンコツ。参考文献にあったとおり。
対象
階層的クラスタリングは、当たり前だがクラスタ構造として階層的なモデルを採用している。
よって基本的には階層的なクラスタ構造を持つデータに適用したい。
しかし階層的なクラスタ構造を持つ対象というものはそれほど多いわけではない。
irisみたいに遺伝系統を持つようなデータだと階層構造になるのかな。