도찐개찐

[머신러닝-비지도] 04. Hierarchical clustering 본문

PYTHON/데이터분석

[머신러닝-비지도] 04. Hierarchical clustering

도개진 2023. 1. 4. 10:44

Hierarchical clustering

(한글 : 계층적 군집 분석) 은 비슷한 군집끼리 묶어 가면서 최종 적으로는 하나의 케이스가 될때까지 군집을 묶는 클러스터링 알고리즘이다.

ex)

“진돗개,세퍼드,요크셔테리어,푸들, 물소, 젖소" 를 계층적 군집 분석을 하게 되면

첫번째는 중형견, 소형견, 소와 같은 군집으로 3개의 군집으로 묶일 수 있다.

이를 한번 더 군집화 하게 되면 [진돗개,셰퍼드] 와 [요크셔테리어,푸들] 군집은 하나의 군집(개)로 묶일 수 있다.

마지막으로 한번 더 군집화를 하게 되면 전체가 한군집(동물)으로 묶이게 된다.

이렇게 단계별로 계층을 따라가면서 군집을 하는 것을 계층적 군집 분석이라고 한다.

계층적 군집 분석은 Dendrogram이라는 그래프를 이용하면 손쉽게 시각화 할 수 있다.

(출처 : https://bcho.tistory.com/1204)

군집 알고리즘 별 상세

계층적 군집

  • 군집트리, 덴드로그램을 생성하여 다양한 데이터를 그룹화
  • 비슷한 군집끼리 묶어 가면서 최종적으로 하나의 군집단으로 묶는 기법
    • 즉, 군집간의 거리를 기반으로 군집화하는 방식으로
    • 기존의 군집기법(kmeans)에 비해 군집수를 지정x
  • 계층적 군집의 종류
    • 응집형bottom-up : 개별데이터 군집 => 군집단 형성
    • 분리형top-down : 데이터 전체를 하나의 군집 => 세부적으로 여러 군집으로 나눔

응집형 군집의 예

import mglearn

mglearn.plots.plot_agglomerative_algorithm()

덴드로그램 시각화

  • 각 개체들이 결합되는 순서를 나타내는 트리형태의 다이어그램
  • linkage 함수의 method 속성으로 각 군집간의 연결방식을 지정
    • single/average/complete/centeroid/ward
    • 최단연결single : 각 군집을 대상으로 최소거리 기준 (가까운것부터 군집)
    • 평균연결average : 각 군집을 대상으로 평균거리 기준
    • 최장연결complete : 각 군집을 대상으로 최대거리 기준 (먼것부터 군집)
    • 중심연결centeroid : 각 군집내 중심점 기준
    • 와드연결ward : 군집간 SSW(응집도), SSB(분리도)간의 차이를 이용(일정한 크기의 군집)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
X = np.array([ [5,3], [10,5], [15,12],
               [24,10], [30,30], [85,70],
               [71,80], [60,78], [70,55],
               [80,91] ])
plt.plot(X[:, 0], X[:, 1], 'ro')
[<matplotlib.lines.Line2D at 0x7fafa2e3cbe0>]

from scipy.cluster.hierarchy import dendrogram
from scipy.cluster.hierarchy import linkage, fcluster

# 거리계산방식 지정
linked = linkage(X, method='single')

덴드로그램 시각화

  • dendrogram(연결방식, 옵션)
dendrogram(linked, orientation='top')
plt.ylabel('distance') # 군집간 거리
plt.xlabel('instance') # 개체
plt.axhline(20, color='red', linestyle='--')
plt.show()

적절한 군집수 알아보기

  • 덴드로그램을 보고 n개의 군집을 나눌려고 할때
    적절한 distance는 어떻게 알아볼까?
  • 덴드로그램에서 밑에서 위로 올라갈수록
    군집을 의미하는 선의 갯수가 줄어둠 (응집형 군집!)
  • scipy에서는 fcluster함수를 이용해서
    distance가 특정값일때 군집정보를 알려줌
    • fcluster(연결방식, 거리, 거리측정방식)
# 군집간 거리가 20일때 군집의 수는?
clusters = fcluster(linked, 20, criterion='distance')
clusters
array([1, 1, 1, 1, 2, 3, 3, 3, 4, 3], dtype=int32)
# unique : 중복값 제외하고 한번만 카운트 하는 함수
np.count_nonzero(np.unique(clusters))
4

iris를 이용한 계층형 군집 분석

  • AgglomerativeClustering(군집수, 거리측정방식, 연결방식)
  • affinity : 거리측정방식 (euclidean, mahattan, cosine등)
from sklearn.datasets import load_iris
iris = load_iris()


# linked = linkage(iris.data, method='single')
# linked = linkage(iris.data, method='complete')
# linked = linkage(iris.data, method='average')
linked = linkage(iris.data, method='ward')
# linked = linkage(iris.data, method='centroid')

plt.figure(figsize=(25,10))
dendrogram(linked, orientation='top', leaf_font_size=11, leaf_rotation=0)
plt.ylabel('distance') # 군집간 거리
plt.xlabel('instance') # 개체
plt.axhline(6.5, color='red', linestyle='--')
plt.show()

# 군집간 거리측정방식을 ward로 했을때 3개 군집으로 나눌 경우
# 군집간 거리는?

clusters = fcluster(linked, 6.7, criterion='distance')
clusters

# unique : 중복값 제외하고 한번만 카운트 하는 함수
np.count_nonzero(np.unique(clusters))
3

iris를 이용한 응집형 군집 분석

  • AgglomerativeClustering(군집수, 거리측정방식, 연결방식)
from sklearn.cluster import AgglomerativeClustering
groups = AgglomerativeClustering(n_clusters=3,
                                affinity='euclidean', linkage='ward'
                                )
groups.fit(iris.data)

AgglomerativeClustering

AgglomerativeClustering(n_clusters=3)
groups.labels_
array([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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2,
       2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 2, 0, 2, 0, 2, 2, 0, 0, 2, 2, 2, 2,
       2, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 0])
# from yellowbrick.cluster import SilhouetteVisualizer
# from yellowbrick.cluster import KElbowVisualizer
# 실루엣 계수 확인
from sklearn.metrics import silhouette_score
silhouette_score(iris.data[:, [0, 1]], groups.labels_)
0.3576978019804449
# 실루엣 계수 확인
from sklearn.metrics import silhouette_score
silhouette_score(iris.data[:, [2, 3]], groups.labels_)
0.6398270745578704
data = iris.data
from yellowbrick.cluster import SilhouetteVisualizer
from sklearn.cluster import KMeans
# ax[행][열] : 그래프 출력 위치 지정
# https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html

fig, ax = plt.subplots(2, 3, figsize=(15, 8))
for i in [2, 3, 4, 5, 6, 7]:
    ac = AgglomerativeClustering(n_clusters=i, affinity='euclidean', linkage='ward')
    q, mod = divmod(i, 3)
    visualizer = SilhouetteVisualizer(ac, ax=ax[q-1][mod])
    visualizer.fit_predict(data)

sss = []
for k in range(2, 10+1):
    ac = AgglomerativeClustering(n_clusters=k, affinity='euclidean', linkage='ward')
    ac.fit(data)
    ss = silhouette_score(data, ac.labels_, metric='euclidean')
    sss.append(ss)
plt.plot(range(2, 10 + 1), sss, 'ro--')
plt.show()

from yellowbrick.cluster import KElbowVisualizer
ac = AgglomerativeClustering()
visualizer = KElbowVisualizer(ac, k=(1, 10), timings=False)
visualizer.fit(data)
             estimator=AgglomerativeClustering(n_clusters=9), k=(1, 10),
             timings=False)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class="sk-container" hidden><div class="sk-item sk-dashed-wrapped"><div class="sk-label-container"><div class="sk-label sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-2" type="checkbox" ><label for="sk-estimator-id-2" class="sk-toggleable__label sk-toggleable__label-arrow">KElbowVisualizer</label><div class="sk-toggleable__content"><pre>KElbowVisualizer(ax=&lt;AxesSubplot:&gt;,
             estimator=AgglomerativeClustering(n_clusters=9), k=(1, 10),
             timings=False)</pre></div></div></div><div class="sk-parallel"><div class="sk-parallel-item"><div class="sk-item"><div class="sk-label-container"><div class="sk-label sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-3" type="checkbox" ><label for="sk-estimator-id-3" class="sk-toggleable__label sk-toggleable__label-arrow">estimator: AgglomerativeClustering</label><div class="sk-toggleable__content"><pre>AgglomerativeClustering(n_clusters=9)</pre></div></div></div><div class="sk-serial"><div class="sk-item"><div class="sk-estimator sk-toggleable"><input class="sk-toggleable__control sk-hidden--visually" id="sk-estimator-id-4" type="checkbox" ><label for="sk-estimator-id-4" class="sk-toggleable__label sk-toggleable__label-arrow">AgglomerativeClustering</label><div class="sk-toggleable__content"><pre>AgglomerativeClustering(n_clusters=9)</pre></div></div></div></div></div></div></div></div></div></div>

visualizer = KElbowVisualizer(ac, k=(2, 10), metric='silhouette', timings=False)
visualizer.fit(data)
visualizer.show()
findfont: Font family ['sans-serif'] not found. Falling back to DejaVu Sans.
findfont: Generic family 'sans-serif' not found because none of the following families were found: Arial, Liberation Sans, Bitstream Vera Sans, sans-serif
findfont: Font family ['sans-serif'] not found. Falling back to DejaVu Sans.
findfont: Generic family 'sans-serif' not found because none of the following families were found: Arial, Liberation Sans, Bitstream Vera Sans, sans-serif

ac = AgglomerativeClustering(n_clusters=k, affinity='euclidean', linkage='ward')
visualizer = KElbowVisualizer(ac, k=(2, 10), timings=False)
visualizer.fit(data)
visualizer.show()

농구선수 게임데이터를 이용해서 포지션 군집 분석

bbp = pd.read_csv('../data/bbplayer.csv')
data = bbp.iloc[:, 2:8]

linked = linkage(data, method='ward')
bbp.head()
  Player Pos 3P 2P TRB AST STL BLK
0 Alex Abrines SG 1.4 0.6 1.3 0.6 0.5 0.1
1 Steven Adams C 0.0 4.7 7.7 1.1 1.1 1.0
2 Alexis Ajinca C 0.0 2.3 4.5 0.3 0.5 0.6
3 Chris Andersen C 0.0 0.8 2.6 0.4 0.4 0.6
4 Will Barton SG 1.5 3.5 4.3 3.4 0.8 0.5
plt.figure(figsize=(25,10))
dendrogram(linked, orientation='top', leaf_font_size=10, leaf_rotation=90)
plt.ylabel('distance')  # 군집간 거리
plt.xlabel('instance')  # 개체
plt.show()

ac = AgglomerativeClustering(affinity='euclidean', linkage='ward')
visualizer = KElbowVisualizer(ac, k=(2,7), metric='silhouette', timings=False)
visualizer.fit(data)
visualizer.show()

<AxesSubplot:title={'center':'Silhouette Score Elbow for AgglomerativeClustering Clustering'}, xlabel='k', ylabel='silhouette score'>
visualizer = KElbowVisualizer(ac, k=(2,7), metric='calinski_harabasz', timings=False)
visualizer.fit(data)
visualizer.show()

ac = AgglomerativeClustering(n_clusters=2, affinity='euclidean', linkage='ward')
ac.fit(data)
plt.scatter(data.iloc[:, 1], data.iloc[:, 2], c=ac.labels_, cmap='tab10')
plt.show()

728x90
Comments