본문 바로가기
Data Analysis/Python

[Python] K-means Clustering

by 불탄오징어 2020. 1. 23.
반응형

 

 

 Kmeans Clustering은 Unsupervised learning에 속하는 분류 기법으로 데이터와 그룹의 갯수(K)를 지정하면 알아서 데이터를 분류해줍니다. 간단하고 아주 빠르고 (주어진 데이터 기준으로) 잘 분류해주기 때문에 많이 사용하며 사랑 받는 기법 중 하나입니다. 다만 k를 결정하는데 있어서 어떻게 나눠야할지는 수치적으로 명확하게 정의되지 않기 때문에 분석자의 주관이 다소 들어가는 부분이 있습니다.

 

그럼 간단하게 kmeans Clustering을 수행해보겠습니다. 사용할 예제는 유명한 iris 데이터입니다.

 

iris.txt
0.00MB

 

# package를 가져옵니다.
from sklearn.cluster import KMeans
import numpy as np
from sklearn.datasets import load_iris
import pandas as pd

# iris 데이터를 가져옵니다.
iris_ds = load_iris()

 

sklearn에는 다양한 샘플 데이터를 가지고 있는데 포함된 iris 데이터는 다음과 같은 구조를 가지고 있습니다.

 

data

 

target과 target_name

 

보통은 data frame으로 많이 다루기 때문에 이 데이터를 data frame으로 변환합니다.

 

X = iris_ds.data
Y = iris_ds.target

# dataframe으로 전환하고 concat()함수를 활용하여 하나의 dataframe으로 합칩니다.
iris = pd.concat([pd.DataFrame(X), pd.DataFrame(Y)], axis=1)
iris.columns = ['SepalLength','SepalWidth','PetalLength','PetalWidth','Species']
iris.head()

 

pandas의 concat() 함수는 R에서 rbind()와 cbind()의 기능을 수행합니다.  Species의 값들이 0,1,2로 종이 분류되어있습니다. pandas.replace() 함수를 사용하여 각각 종의 명칭을 부여합니다.

 

# 각각의 그룹값을 종명칭으로 대체합니다.
iris = iris.replace({'Species':0}, {'Species':'setosa'})
iris = iris.replace({'Species':1}, {'Species':'versicolor'})
iris = iris.replace({'Species':2}, {'Species':'virginica'})

 

먼저 간단하게 데이터 현황을 산점도로 출력하여 봅시다.

 

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
for name, group in iris.groupby('Species'):
    ax.scatter(group.SepalLength, group.SepalWidth, marker='o', label=name)
ax.legend(fontsize=10, loc='lower right')


fig, ax = plt.subplots()
for name, group in iris.groupby('Species'):
    ax.scatter(group.PetalLength, group.PetalWidth, marker='o', label=name)
ax.legend(fontsize=10, loc='lower right')

 

 

각 축의 기준에 된 SepalLength, SepalWidth 기준에서는 setosa는 눈으로 봐도 잘 분류됩니다만 나머지 두 종 versicolor와 virginica는 혼합된 것을 볼 수 있습니다. PetalLength, PetalWidth 기준에서는 3종이 모두 잘 분류되어 보이는 것을 확인 할 수 있습니다. 일단은 4개의 지표를 활용하여 Kmeans를 실행합니다.

 

kmeans = KMeans(n_clusters=3, random_state=0).fit(iris[['SepalLength','SepalWidth','PetalLength','PetalWidth']])

iris_fit = pd.concat([iris, pd.DataFrame(kmeans.labels_)], axis=1)

iris_fit.columns = ['SepalLength','SepalWidth','PetalLength','PetalWidth','Species', 'KmeansFit']

 

kmeans Clustering 결과는 각 그룹에 대한 번호가 나옵니다만 해당 번호가 어떤 종을 지칭하는지는 알 수가 없습니다. 위에서 했던 방법과 동일하게 Kmeans Clustering 결과도 하나의 테이블로 합칩니다. 종 분류 결과도 종명칭을 부여합니다. 정말 잘 분류 되었는지 교차테이블을 만들어서 확인해봅시다.

 

pd.crosstab(iris_fit.Species, iris_fit.KmeansFit)

 

결과를 보면 setosa는 모두 50개 하나의 그룹으로 분류가 되었지만 versicolo는 2개가, virginica는 14개가 오분류된 것을 확인할 수 있습니다. 앞서 scatter plot으로 확인한 바와 같이 PetalLength, PetalWidth만 선택하여 KmeansClustering을 수행합니다.

 

kmeans2 = KMeans(n_clusters=3, random_state=0).fit(iris[['PetalLength','PetalWidth']])

iris_fit2 = pd.concat([iris, pd.DataFrame(kmeans2.labels_)], axis=1)

iris_fit2.columns = ['SepalLength','SepalWidth','PetalLength','PetalWidth','Species', 'KmeansFit']

pd.crosstab(iris_fit2.Species, iris_fit2.KmeansFit)

 

setosa는 50개 모두 분류되었으며 versicolor는 2개, virginica는 4개가 오분류 되었습니다. 앞서 진행했던 결과보다 더 좋은 결과를 확인 할 수 있습니다. KmeansClustering은 간단하고 사용하기가 쉬워 많이 사용들 하지만 투입되는 데이터(지표)를 어떻게 만들고 어떤 것을 선택하느냐에 따라 분류율의 변화가 생깁니다. 실무에서도 이를 적용하기 위해 데이터를 만드는 과정을 꽤 오랫동안 거치게 되는데 이 과정에서 데이터를 얼마나 잘 다듬고 정교화하느냐에 따라 결과가 판이하게 달라집니다. 

댓글