Rで階層的クラスタリングする基本

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法による階層的クラスタリング結果

最遠法による階層的クラスタリング結果

最近法による階層的クラスタリング結果

このデータではwardとcompleteが同じくらいに良い。
ward法の方がクラスタを明確に分割しようとする傾向がある(デンドログラムの落差が激しい)
singleはポンコツ。参考文献にあったとおり。

対象

階層的クラスタリングは、当たり前だがクラスタ構造として階層的なモデルを採用している。
よって基本的には階層的なクラスタ構造を持つデータに適用したい。
しかし階層的なクラスタ構造を持つ対象というものはそれほど多いわけではない。
irisみたいに遺伝系統を持つようなデータだと階層構造になるのかな。

参考文献

コメントを残す