Hierarchical Clustering, 屬於資料分群的一種方法。資料分群屬於非監督式學習,處理的資料是沒有正確答案/標籤/目標變數可參考的。常見的分群方法包括著名的kmeans, hierarchical clustering,分別使用不同分群演算邏輯。本篇將介紹階層式分群的實作與特色。
資料分群簡介
- 資料分群是一個將資料分割成數個子集合的方法,主要目的包括:
- 找出資料中相近的群聚(clusters),
- 找出各群的代表點。代表點可以是群聚中的中心點(centroids)或是原型(prototypes)。
 
- 透過各群的代表點,可以達到幾個目標:
- 資料壓縮
- 降低雜訊
- 降低計算量
 
-  資料分群將依據資料自身屬性計算彼此間的相似度(物以類聚),而「相似度」主要有兩種型態:
- 「Compactness」:目標是讓子集合間差異最大化,子集合內差異最小化。如階層式分群和K-means分群。
- 「Connectedness」:目標是將可串連在一起的個體分成一群。如譜分群(Spectral Clustering)。
 
- 資料分群屬於非監督式學習法(Unsupervised Learning),即資料沒有標籤(unlabeled data)或沒有標準答案,無法透過所謂的目標變數(response variable)來做分類之訓練。也因為資料沒有標籤之緣故,與監督式學習法和強化式學習法不同,非監督式學習法無法衡量演算法的正確率。
資料分群系列文章會依序介紹以下幾種常見的分群方法:
- 階層式分群(hierarchical clustering)
- 聚合式階層分群法 Agglomerative Hierarchical Clustering
- 分裂式階層分群法 Divisive Hierarchical Clustering
- 最佳分群群數(Determining Optimal Clusters)
 
- 切割式分群(partitional clustering)
- K-means
- K-medoid
- 最佳分群群數(Determining Optimal Clusters)
 
- 譜分群(Spectral Clustering)
1. 階層式分群(Hierarchical clustering)
- 階層分群法的概念是在分群中建立分群,並不需要預先設定分群數。
- 產生的分群結果為一目瞭然的樹狀結構圖(又稱作dendrogram)。
- 群數(number of clusters)可由大變小(divisive hierarchical clustering),或是由小變大(agglomerative hierarchical clustering),透過群聚反覆的分裂和合併後,在選取最佳的群聚數。
- 階層式分群兩種演算法:
- 當採行聚合法,又稱作AGNES(Agglomerative Nesting),資料會由樹狀結構的底部開始開始逐次合併 (bottom-up),在R語言中所使用的函數為hclust()[in stat package]或agnes[in cluster package],擅於處理與識別小規模群聚
- 當採行分裂法,又稱作DIANA(Divisive Analysis),資料則會由樹狀結構的頂部開始逐次分裂(top-down),在R語言中使用的套件為diana()[in cluster package],擅於處理與識別大規模群聚。
 
- 階層分群可被用運用數值與類別資料。
1-1. 聚合式階層群聚法(AGNES, bottom-up)
我們使用Iris資料集-經典的鳶尾花資料來進行聚合式階層群聚法之說明。(*注意,資料必須先預處理遺失值的部分,比如說使用na.omit(data)。)
| 1 2 3 4 5 6 7 8 9 | head(iris) #   Sepal.Length Sepal.Width Petal.Length Petal.Width Species # 1          5.1         3.5          1.4         0.2  setosa # 2          4.9         3.0          1.4         0.2  setosa # 3          4.7         3.2          1.3         0.2  setosa # 4          4.6         3.1          1.5         0.2  setosa # 5          5.0         3.6          1.4         0.2  setosa # 6          5.4         3.9          1.7         0.4  setosa | 
因為分群法為非監督式學習法,專處理無標籤(un-labeled)或無標準答案的資料分群,故我們將分類結果欄位Species從我們的inputData刪除。
| 1 2 3 4 5 6 7 8 9 10 | inputData  head(inputData) #   Sepal.Length Sepal.Width Petal.Length Petal.Width # 1          5.1         3.5          1.4         0.2 # 2          4.9         3.0          1.4         0.2 # 3          4.7         3.2          1.3         0.2 # 4          4.6         3.1          1.5         0.2 # 5          5.0         3.6          1.4         0.2 # 6          5.4         3.9          1.7         0.4 | 
在進行聚合式群聚法前,我們先簡單介紹幾個hclust()函數中的重要參數:
- d : 由dist()函數計算出來資料兩兩間的相異度矩陣(dissimilarity matrix),即兩兩資料間的距離矩陣。
- method: 群(clusters)聚合或連結的方式。包括:single(單一)、complete(完整)、average(平均)、Ward’s(華德)和 centroid(中心)等法。其中又以average(平均)聚合方法被認為是最適合的。不同方法對階層分群結果亦有極大影響。
R語言hclust()套件中提供群聚距離演算法來衡量兩群聚的不相似度,最常見的幾種演算法如下:
- 單一連結聚合演算法(single-linkage agglomerative algorithm): 群與群的距離定義為不同群聚中最近的兩個點的距離。傾向產生較於緊緻(compact)的群聚。
 $$d(C_{i},C_{j})=\min_{a\in C_{i},b\in C_{j}}d(a,b)$$
- 完整連結聚合演算法(complete-linkage agglomerative algorithm): 群與群的距離定義為不同群聚中最遠的兩個點的距離。傾向產生較於長(long)、鬆散(loose)的群聚。
 $$d(C_{i},C_{j})=\max_{a\in C_{i},b\in C_{j}}d(a,b)$$
- 平均連結聚合演算法(average-linkage agglomerative algorithm): 群與群的距離定義為不同群聚中各點與各點距離總和的平均。
 $$d(C_{i},C_{j})=\sum_{a\in C_{i},b\in C_{j}}\frac{d(a,b)}{|C_{i}||C_{j}|}$$
 其中,\(|C_{i}|\)和\(|C_{j}|\)分別為群聚\(C_{i}\)和\(C_{j}\)的大小。
- 中心連結聚合演算法(centroid-linkage agglomerative algorithm): 群與群的距離定義為不同群聚中心點之間的距離。
 $$d(A,B)= d(\bar{X}_{A},\bar{X}_{B})=\|\bar{X}_{A}-\bar{X}_{B}\|^2$$
- 華德最小變異法(Ward’s Minimum Variance): 最小化各群聚內變異加總(minimize the total within-cluster variance)。主要用來尋找緊湊球型的群聚。反覆比較每對資料合併後的群內總變異數的增量,並找增量最小的組別優先合併。越早合併的子集表示其間的相似度越高。而使用華德最小變異法的前提為,初始各點資料距離必須是歐式距離的平方和(Squared Euclidean Distance)。在R套件hclust將參數設為method = “ward.D2″(不是”ward.D”)。(華德法參考文獻)
我們先來試試,不同的資料點間距離矩陣計算之效果。即調整dist()中參數method。
- 歐式距離(以二維空間為例):最常用也是最重要的距離計算法。
 $$d(P_{1},P_{2})=\sqrt{(x_{1}-x_{2})^2+(y_{1}-y_{2})^2}$$
- 曼哈頓距離(以二維空間為例):
 $$d(P_{1},P_{2})=|x_{1}-x_{2}|+|y_{1}-y_{2}|$$
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 由於階層式分群是依據個體間的「距離」來計算彼此的相似度。 # 我們會先使用dist()函數,來計算所有資料個體間的「距離矩陣(Distance Matrix)」 # 而「距離」的算法又有:(1)歐式距離(2)曼哈頓距離 E.dist  M.dist  # 讓圖形以一行兩欄的排版方式呈現,如要還原請用dev.off() par(mfrow=c(1,2)) # 將以上資料間距離作為參數投入階層式分群函數:hclust() # 使用歐式距離進行分群 h.E.cluster  plot(h.E.cluster, xlab="歐式距離",family="黑體-繁 中黑") # 使用曼哈頓距離進行分群 h.M.cluster  plot(h.M.cluster, xlab="曼哈頓距離", family="黑體-繁 中黑") | 
可發現不同資料距離計算會有不同分群結果。
- Height表示n-1組實數值。各值為使用特定聚合方法計算出來的標準數值。

比較不同歐式距離搭配不同聚合演算法的分群結果。
| 1 2 3 4 5 6 7 | dev.off() par(mfrow= c(3,2),family="黑體-繁 中黑") plot(hclust(E.dist, method="single"),xlab = "最近聚合法:single-linkage")   # 最近法 plot(hclust(E.dist, method="complete"), xlab = "最遠聚合法:complete-linkage")  # 最遠法 plot(hclust(E.dist, method="average"), xlab = "平均聚合法: average-linkage")  # 平均法 plot(hclust(E.dist, method="centroid"), xlab = "中心法: centroid-linkage") # 中心法 plot(hclust(E.dist, method="ward.D2"), xlab = "華德法: Ward's Method")  # 華德法 | 
從下圖可以發現群聚數結構的幾個特徵:
- Single-linkage法具有「大者恆大」之效果。
- 而 complete linkage 和 average linkage 比較具有「齊頭並進」之效果。

接著,我們聚焦在採用歐式距離搭配華德最小變異聚合演算法。可透過設定hclust()參數method=”ward.D2″。
| 1 2 3 | dev.off() par(family="黑體-繁 中黑") plot(hclust(E.dist, method="ward.D2"), xlab = "華德法: Ward's Method") | 

以上結果也可以使用anges()函數來執行。此函數跟hclust()很類似,不同之處是該函數能另外計算聚合係數(agglomerative coefficient)。聚合係數是衡量群聚結構被辨識的程度,聚合係數越接近1代表有堅固的群聚結構(strong clustering structure)。在這個使用歐式距離搭配華德連結演算法的群聚係數有高達99%的表現。
| 1 2 3 4 5 6 | # Compute with agnes library(cluster) hc2  # Agglomerative coefficient hc2$ac ## [1] 0.9908772 | 
我們可以用聚合係數來比較多組分群連結演算法的效果。此範例中又以華德法表現最好。
| 1 2 3 4 5 6 7 8 9 10 11 12 | # methods to assess m  names(m)  # function to compute coefficient ac    agnes(E.dist, method = x)$ac } map_dbl(m, ac) #Apply a function to each element of a vector  #   average    single  complete      ward  # 0.9300174 0.8493364 0.9574622 0.9908772 | 
若遇將agnes()產生的樹狀圖繪出可使用函數pltree()。可以發現結果大致上與hclust()結果差不多。
| 1 2 3 | dev.off() hc2  pltree(hc2, cex = 0.6, hang = -1, main = "Dendrogram of agnes") | 

對階層分群結果產生的樹狀結構進行截枝可以將觀測值分成數群。截肢方法有兩種:
- 指定所要的分群數: rect.hclust(k=…)、cutree(k=…)
- 指定截枝的位置: rect.hclust(h=…)
比如說,指定將資料分成3群與13群。
| 1 2 3 4 | h.E.Ward.cluster  plot(h.E.Ward.cluster) rect.hclust(tree =h.E.Ward.cluster, k = 3, border = "red") rect.hclust(tree =h.E.Ward.cluster, k = 13, border = "blue") | 

比如說,指定截枝高度分別為4和10,可分別將資料分成5組跟3組。
| 1 2 3 4 | h.E.Ward.cluster  plot(h.E.Ward.cluster) rect.hclust(tree =h.E.Ward.cluster, h = 4, border = "red") rect.hclust(tree =h.E.Ward.cluster, h = 10, border = "blue") | 

如果要將資料標記上分群結果,可使用cutree(),並指定參數k為所欲截枝群樹。
| 1 2 3 4 5 6 7 | h.E.Ward.cluster  cut.h.cluster  cut.h.cluster #   [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 2 2 #  [56] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 3 3 3 3 2 3 3 3 # [111] 3 3 3 2 2 3 3 3 3 2 3 2 3 2 3 3 2 2 3 3 3 3 3 2 2 3 3 3 2 3 3 3 2 3 3 3 2 3 3 2 | 
分群結果和實際結果比較。
| 1 2 3 4 5 6 | table(cut.h.cluster, iris$Species) # cut.h.cluster setosa versicolor virginica #             1     50          0         0 #             2      0         49        15 #             3      0          1        35 | 
將資料分群的混淆矩陣視覺化。
| 1 | plot(table(iris$Species, cut.h.cluster), main = "Confusion Matrix for Species Clustering", xlab = "Species", ylab = "Cluster") | 
可以發現,屬於setosa種類的都被很好的分到第一群,屬於versicolor主要被分到第二群,但virginica似乎沒有分得很好。

如果回頭看一下原始資料分佈長相,可以發現versicolor和virginica其實部分資料靠得很近,因此兩種Species被分到第二和第三群也是合理的。
| 1 2 | ggplot(data = iris,mapping = aes(x = Petal.Length, y = Petal.Width)) +   geom_point(aes(col = Species)) | 

1-2. 分裂式階層群聚法(DIANA, top-down)
我們使用R內建的USArrests資料集來進行說明。進行分群前,我們先將資料預處理,包括遺失值忽略以及標準化(0~1)。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | head(USArrests) #            Murder Assault UrbanPop Rape # Alabama      13.2     236       58 21.2 # Alaska       10.0     263       48 44.5 # Arizona       8.1     294       80 31.0 # Arkansas      8.8     190       50 19.5 # California    9.0     276       91 40.6 # Colorado      7.9     204       78 38.7 inputData    USArrests %>%    na.omit() %>%    scale() # scaling/standardizing the data | 
diana()函數運作方式很類似agnes(),只差diana沒有method參數。
| 1 2 3 4 5 6 7 8 9 | # compute divisive hierarchical clustering diana_clust  # Divise coefficient; amount of clustering structure found diana_clust$dc ## [1] 0.8514345 # plot dendrogram pltree(diana_clust, cex = 0.6, hang = -1, main = "Dendrogram of diana") | 
由分裂式階層分群演算法產出的樹狀圖結果如下:
- 葉節點(末梢節點)代表個別資料點。
- 垂直座標軸的Height代表群聚間的(不)相似度((dis)similarity), 群聚的高度(height)越高,代表觀測值間越不相似(組內變異越大)。需要注意的是,要總結兩個觀測值的相似度,只能用兩觀測值何時被第一次合併的群聚高度來做判斷,而不能以水平軸的距離來評估。

另外,我們可以透過縱軸的群聚高度來決定分群數目。我們可以使用cutree()函數來得到各觀測值被分類到的群。
| 1 2 3 4 5 | # Cut diana() tree into 4 groups diana_clust  group  group # [1] 1 2 2 3 2 2 3 3 2 1 3 4 2 3 4 3 4 1 4 2 3 2 4 1 2 4 4 2 4 3 2 2 1 4 3 3 3 3 3 1 4 1 2 3 4 3 3 4 4 3 | 
我們使用factoextra套件中的fviz_clusterc函數來將分群結果視覺化。
| 1 2 | library(factoextra) fviz_cluster(list(data = inputData, cluster = group)) | 

1-3. 決定階層式分群最佳群聚數
1-3-1. Elbow Method
使用函數fviz_nbclust(),並將參數FUN指定為hcut(表階層式分群法)。wss代表組內平方誤差。
| 1 | fviz_nbclust(inputData, FUN = hcut, method = "wss") | 
由結果圖可知根據Elbow法,最佳分群數目為4群。

1-3-2. Average Silhouette Method
將method參數改為silhouette。
| 1 | fviz_nbclust(x = inputData,FUNcluster = hcut, method = "silhouette") | 
根據Average Silhouette Width,最佳分群數目為2 群。

1-3-3. Gap Statistic Method
使用clusGap()函數。
| 1 2 | gap_stat  fviz_gap_stat(gap_stat) | 
根據Gap(k)統計量,最佳分群數為3群。(*\(Gap(k)\geq Gap(k+1) – s_{k+1}\))

總結
- 階層分群主要受:資料個體兩兩間距離矩陣衡量方法與群與群連結方法所影響。
-  階層分群優點包括:
- 概念簡單且樹狀圖(dendrogram)結果一目瞭然。
- 只需要資料點間距離即可產出分群結果。
- 可以應用在數值或類別資料。
 
- 但缺點就是運算速度,以及不適用處理大量資料。運算速度方面,可以考慮其他R套件如fastcluster中的hclust函數,執行上會比一般hclust更有效率。
更多統計模型筆記連結:
- Linear Regression | 線性迴歸模型 | using AirQuality Dataset
- Regularized Regression | 正規化迴歸 – Ridge, Lasso, Elastic Net | R語言
- Logistic Regression 羅吉斯迴歸 | part1 – 資料探勘與處理 | 統計 R語言
- Logistic Regression 羅吉斯迴歸 | part2 – 模型建置、診斷與比較 | R語言
- Decision Tree 決策樹 | CART, Conditional Inference Tree, Random Forest
- Regression Tree | 迴歸樹, Bagging, Bootstrap Aggregation | R語言
- Random Forests 隨機森林 | randomForest, ranger, h2o | R語言
- Gradient Boosting Machines GBM | gbm, xgboost, h2o | R語言
- Hierarchical Clustering 階層式分群 | Clustering 資料分群 | R統計
- Partitional Clustering | 切割式分群 | Kmeans, Kmedoid | Clustering 資料分群
- Principal Components Analysis (PCA) | 主成份分析 | R 統計
參考:



