데이터분석/R

[ADP] K-평균 군집 (K-means Clustering)

버섯도리 2022. 1. 16. 11:21

> # 10. K-평균 군집 (K-means Clustering)


> # 군집 수에 따른 집단 내 제곱합(within group sum of squares)을 구하고 그래프를 그리는 함수 정의
wssplot <- function(data, nc=15, seed=1234) {
+   wss <- (nrow(data)-1)*sum(apply(data,2,var))
+   for (i in 2:nc) {
+     set.seed(seed)
+     wss[i] <- sum(kmeans(data, centers=i)$withinss)
+   }
+   plot(1:nc, wss, type='b', xlab='Number of Cluster', ylab='Within Group sum of squares')
+ }

> library(rattle)

> # 178개 이탈리아 와인에 대한 13가지의 화학적 성분을 측정한 자료이다.
> data("wine")
> head(wine)
  Type Alcohol Malic  Ash Alcalinity Magnesium Phenols Flavanoids Nonflavanoids Proanthocyanins Color  Hue Dilution Proline
1    1   14.23  1.71 2.43       15.6       127    2.80       3.06          0.28            2.29  5.64 1.04     3.92    1065
2    1   13.20  1.78 2.14       11.2       100    2.65       2.76          0.26            1.28  4.38 1.05     3.40    1050
3    1   13.16  2.36 2.67       18.6       101    2.80       3.24          0.30            2.81  5.68 1.03     3.17    1185
4    1   14.37  1.95 2.50       16.8       113    3.85       3.49          0.24            2.18  7.80 0.86     3.45    1480
5    1   13.24  2.59 2.87       21.0       118    2.80       2.69          0.39            1.82  4.32 1.04     2.93     735
6    1   14.20  1.76 2.45       15.2       112    3.27       3.39          0.34            1.97  6.75 1.05     2.85    1450

> # 변수의 측정 단위가 다르므로 군집분석 수행 전에 데이터 표준화
> df <- scale(wine[-1])

wssplot(df)

> # 군집수 3에서 오차제곱합이 크게 감소되었음을 알 수 있다.

> # NbClust 패키지로도 확인
> library(NbClust)
> set.seed(1234)
> nc <- NbClust(df, min.nc = 2, max.nc = 15, method = 'kmeans')
*** : The Hubert index is a graphical method of determining the number of clusters.
                In the plot of Hubert index, we seek a significant knee that corresponds to a 
                significant increase of the value of the measure i.e the significant peak in Hubert
                index second differences plot. 

 
*** : The D index is a graphical method of determining the number of clusters. 
                In the plot of D index, we seek a significant knee (the significant peak in Dindex
                second differences plot) that corresponds to a significant increase of the value of
                the measure. 


******************************************************************* 
* Among all indices:                                                
* 2 proposed 2 as the best number of clusters 
* 19 proposed 3 as the best number of clusters 
* 1 proposed 14 as the best number of clusters 
* 1 proposed 15 as the best number of clusters 

                   ***** Conclusion *****                            
 
* According to the majority rule, the best number of clusters is  3 
 
 
******************************************************************* 
> nc$Best.nc
                     KL    CH Hartigan     CCC    Scott                    Marriot  TrCovW   TraceW Friedman   Rubin Cindex
Number_clusters  3.0000  3.00   3.0000 14.0000   3.0000                          3     3.0   3.0000   3.0000  3.0000 2.0000
Value_Index     14.3567 70.94  41.7215  6.3155 374.2416 33048027677429170724246662 10183.3 305.3194  10.0771 -0.3046 0.3018
                    DB Silhouette   Duda PseudoT2   Beale Ratkowsky     Ball PtBiserial Frey McClain   Dunn Hubert SDindex
Number_clusters 3.0000     3.0000 3.0000   3.0000  3.0000    3.0000   3.0000     3.0000    1   2.000 3.0000      0  3.0000
Value_Index     1.4681     0.2849 1.5712 -33.0827 -3.1777    0.3748 401.2608     0.6154   NA   0.641 0.2323      0  1.0492
                Dindex    SDbw
Number_clusters      0 15.0000
Value_Index          0  0.3746
> # 최적의 군집수를 정하기 위해 사용되는 지수(총 30개 중 여기서는 26개 지수가 계산됨) 중에서 19개의 지수가 3을 최적의 군집수로 다수결 투표했음을 알 수 있다.

> par(mfrow=c(1,1))
barplot(table(nc$Best.nc[1,]), xlab='Number of Cluster', ylab='Number of Criteria', main='Number of Cluster Chosen by 26 Criteria')

> # 최적의 군집수에 대한 다수결 결과를 시각화

> set.seed(1234)
> # k-means 수행
> fit.km <- kmeans(df, 3, nstart = 25)
> # 각 군집의 크기 확인
> fit.km$size
[1] 62 65 51

> # 군집결과 시각화
> par(mfrow=c(1,1))
plot(df, col=fit.km$cluster)
> # 각 군집의 중심점 추가
points(fit.km$centers, col=1:3, pch=8, cex=1.5)


> # 군집결과 요약
aggregate(wine[-1], by=list(cluster=fit.km$cluster), mean)
  cluster  Alcohol    Malic      Ash Alcalinity Magnesium  Phenols Flavanoids Nonflavanoids Proanthocyanins    Color
1       1 13.67677 1.997903 2.466290   17.46290 107.96774 2.847581  3.0032258     0.2920968        1.922097 5.453548
2       2 12.25092 1.897385 2.231231   20.06308  92.73846 2.247692  2.0500000     0.3576923        1.624154 2.973077
3       3 13.13412 3.307255 2.417647   21.24118  98.66667 1.683922  0.8188235     0.4519608        1.145882 7.234706
        Hue Dilution   Proline
1 1.0654839 3.163387 1100.2258
2 1.0627077 2.803385  510.1692
3 0.6919608 1.696667  619.0588
> # 각 군집별 변수의 평균값을 확인할 수 있다.

> # k-평균군집의 정오분류표 확인
> ct.km <- table(wine$Type, fit.km$cluster)
> ct.km
   
     1  2  3
  1 59  0  0
  2  3 65  3
  3  0  0 48
> # 대각선 방향이 정분류이므로 정확도는 172/178*100=96.6%이다.

> library(flexclust)
randIndex(ct.km)
     ARI 
0.897495 
> # flexclust 패키의 randIndex() 함수를 이용하면 실제 와인의 종류(Type)와 군집 간의 일치도(agreement)를 나타내는 수정된 랜드지수를 구할 수 있다. 이 지수는 -1(no agreement)과 1(perfect agreement) 사이의 값을 가진다.

 

 

 

 

 

출처 : 2020 데이터 분석 전문가 ADP 필기 한 권으로 끝내기