본문 바로가기
데이터분석/데이터분석

군집 분석(Clustering)

by 이규승 2022. 5. 16.
728x90
반응형

Cluster의 개수나 구조에 관한 특별한 사전 가정 없이 개체들 사이의 유사성 / 거리에 근거해 cluster를  찾고 다음 단계의 분석을 하게 하는 기법

유사한 개체들을 cluster로 그룹화하여 각 집단의 성격을 파악

Clustering 장점
– 데이터에 대해 탐색적 기법으로, 데이터 내부구조 등이 주어지지 않아도 자료구조를 탐색
– 추가적인 분석을 위해 사용할 수 있음.
– 유사성, 비유사성 만 계산할 수 있다면 여러 형태 데이터 적용이 가능.
Clustering 단점
– 자료 유형이 혼합된 경우, 거리정의 등이 어려울 수 있음
– 초기 군집 수 설정이 중요
– 결과 해석에 주의
1) 계층적 군집분석 (단일결합법, 완전결합법, 평균결합법, 중심결합기준법, Ward법

# 군집분석(Clustering): 비지도학습
# 계층적 군집분석 : 특정 알고리즘에 의해 데이터들을 연결하여 계층적으로 군집(클러스터)을 형성
# 웅집형 : 자료 하나를 군집으로 간주하고 가까운 군집끼리 연결해가는 방법(상향식)
# 분리형 : 전체 자료를 하나의 큰 군집으로 간주하고, 유의한 부분을 분리해가는 방법(하향식)

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rc('font', family = 'malgun gothic')

np.random.seed(123)
var = ['X', 'Y']
labels = ['점0', '점1', '점2', '점3', '점4']
x = np.random.random_sample([5, 2]) * 10
df = pd.DataFrame(x, columns = var, index = labels)
print(df)

# plt.scatter(x[:,0],x[:,1], s=50, c='blue', marker='o')
# plt.grid(True)
# plt.show()

from scipy.spatial.distance import pdist, squareform
dist_vec = pdist(df, metric = 'euclidean') #유클라디안 거리계산법
print(dist_vec)

# 거리벡터를 사각형 형식으로 출력
row_dist = pd.DataFrame(squareform(dist_vec), columns = labels, index = labels)
print(row_dist)

# 계층적 군집분석
from scipy.cluster.hierarchy import linkage
row_cluster = linkage(dist_vec, method='ward') # single, average, ...

df = pd.DataFrame(row_cluster, columns = ['클러스터id_1','클러스터id_2','거리','클러스터멤버수'])
print(df)

from scipy.cluster.hierarchy import dendrogram
row_dend = dendrogram(row_cluster, labels = labels)
plt.tight_layout()
plt.ylabel('유클리드 거리')
plt.show()

군집분석은 scipy를 이용한다

유클라디안 거리계산 후 와드법으로 계층적 클러스터링 시작하고 덴드로그램으로 시각화

2) 비계층적 군집분석 (k-means clustering)

# 비계층적 군집분석 : k-means - 평균을 새로운 기준으로 갱신해가며 군집화

import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
x, y = make_blobs(n_samples= 150, n_features = 2, centers = 3, cluster_std = 0.5,
                  shuffle = True, random_state = 0) # center는 초기개수같은것

print(x[:3], x.shape)
# print(y)

plt.scatter(x[:,0], x[:,1], s=50, c='gray', marker='o')
plt.grid()
plt.show()

from sklearn.cluster import KMeans
#init_centroid = 'random' # 초기 클러스터 중심점 임의로 선택
init_centroid = 'k-means++' # 초기 중심점을 할당하고, 나머지 후속 중심점은 멀리 위치해 중심을 선택

kmodel = KMeans(n_clusters = 3, init = init_centroid, random_state = 0).fit(x)# 비지도학습
pred = kmodel.predict(x)
print('pred', pred)

# print(x[pred==0])
# print()
# print(x[pred==1])
# print()
# print(x[pred==2])

print('중심점 : ', kmodel.cluster_centers_)
plt.scatter(x[pred==0, 0 ], x[pred==0,1], s=50, c='red', marker='o', label='cluster1')
plt.scatter(x[pred==1, 0 ], x[pred==1,1], s=50, c='green', marker='s', label='cluster2')
plt.scatter(x[pred==2, 0 ], x[pred==2,1], s=50, c='blue', marker='v', label='cluster3')
plt.scatter(kmodel.cluster_centers_[:,0], kmodel.cluster_centers_[:,1],
            s=100, c='black', marker='+', label='center')
plt.legend()
plt.grid()
plt.show()

# k값? 방법1) 엘보우(elbow) 기법 - 클러스터 간 SSE(sum of square error)의 차이를 이용해 최적의 클러스터 수를 제시

def elbow_func(x):
    sse = []
    for i in range(1, 11):
        km = KMeans(n_clusters = i, init = init_centroid, random_state = 15).fit(x)
        sse.append(km.inertia_)
    plt.plot(range(1, 11), sse, marker = 'o')
    plt.xlabel('cluster')
    plt.ylabel('sse')
    plt.show()  
elbow_func(x)

# k값? 방법2) 실루엣(silhoutte) 기법 
'''
실루엣(silhouette) 기법
  클러스터링의 품질을 정량적으로 계산해 주는 방법이다.
  클러스터의 개수가 최적화되어 있으면 실루엣 계수의 값은 1에 가까운 값이 된다.
  실루엣 기법은 k-means 클러스터링 기법 이외에 다른 클러스터링에도 적용이 가능하다
'''
import numpy as np
from sklearn.metrics import silhouette_samples
from matplotlib import cm

# 데이터 X와 X를 임의의 클러스터 개수로 계산한 k-means 결과인 y_km을 인자로 받아 각 클러스터에 속하는 데이터의 실루엣 계수값을 수평 막대 그래프로 그려주는 함수를 작성함.
# y_km의 고유값을 멤버로 하는 numpy 배열을 cluster_labels에 저장. y_km의 고유값 개수는 클러스터의 개수와 동일함.

def plotSilhouette(x, pred):
    cluster_labels = np.unique(pred)
    n_clusters = cluster_labels.shape[0]   # 클러스터 개수를 n_clusters에 저장
    sil_val = silhouette_samples(x, pred, metric='euclidean')  # 실루엣 계수를 계산
    y_ax_lower, y_ax_upper = 0, 0
    yticks = []
    for i, c in enumerate(cluster_labels):
        # 각 클러스터에 속하는 데이터들에 대한 실루엣 값을 수평 막대 그래프로 그려주기
        c_sil_value = sil_val[pred == c]
        c_sil_value.sort()
        y_ax_upper += len(c_sil_value)

        plt.barh(range(y_ax_lower, y_ax_upper), c_sil_value, height=1.0, edgecolor='none')
        yticks.append((y_ax_lower + y_ax_upper) / 2)
        y_ax_lower += len(c_sil_value)   

    sil_avg = np.mean(sil_val)         # 평균 저장
    plt.axvline(sil_avg, color='red', linestyle='--')  # 계산된 실루엣 계수의 평균값을 빨간 점선으로 표시
    plt.yticks(yticks, cluster_labels + 1)
    plt.ylabel('클러스터')
    plt.xlabel('실루엣 개수')
    plt.show() 
'''
그래프를 보면 클러스터 1~3 에 속하는 데이터들의 실루엣 계수가 0으로 된 값이 아무것도 없으며, 실루엣 계수의 평균이 0.7 보다 크므로 잘 분류된 결과라 볼 수 있다.
'''
X, y = make_blobs(n_samples=150, n_features=2, centers=3, cluster_std=0.5, shuffle=True, random_state=0)
km = KMeans(n_clusters=3, random_state=0) 
y_km = km.fit_predict(X)

plotSilhouette(X, y_km)

엘보우
실루엣

엘보우 : 클러스터의 개수를 늘려가면서 계산한 SSE를 그래프로 그려보면 SSE의 값이 점점 줄어들다가 어느 순간 줄어드는 비율이 급격하게 작아지는 부분이 생기는데, 그래프의 모양을 보면 팔꿈치 끄트머리 처럼 보이는 부분이 있는데 이 부분이 우리가 구하려는 최적의 클러스터 개수가 된다.

 

실루엣 : 클러스터링의 품질을 정량적으로 계산해 주는 방법이다. 클러스터의 개수가 최적화되어 있으면 실루엣 계수의 값은 1에 가까운 값이 된다. 실루엣 기법은 kmeans 클러스터링 기법 이외에 다른 클러스터링에도 적용이 가능하다

728x90

'데이터분석 > 데이터분석' 카테고리의 다른 글

밀도기반 클러스터링(DBSCAN)  (0) 2022.05.19
지도학습(K-NN) 과 비지도학습(K-Means) 비교  (0) 2022.05.16
인공신경망 (ANN)  (0) 2022.05.16
K-NN (K -Nearest Neighbor)  (0) 2022.05.16
나이브 베이즈  (0) 2022.05.16