[Visualization] Interact Clustering with Kmeans (with any data)(continuous)

2020. 10. 9. 21:42분석 Python/Visualization

728x90

 

 

이번에 k-means를 이용해서 interact plot을 그려보는 것을 해볼 일이 있어서 서칭을 하다 보니,

iris로 k-means를 활용해서 시각화한 것을 찾았다.

그래서 좀 더 확장하여 클래스로 만들어서 다양한 데이터가 들어왔을 때 적용할 수 있게 바꿔봤다.

일부 코드 중에 수정할 부분이 있어서 수정함 (argmax 보다는 dictionary로 관리를 해야 정확한 값을 뽑을 수 있음)

 

해당 글을 들어가면 잘 설명을 해줄 테니, 기술적인 내용은 들어가서 확인해주시기 바란다!

아주 대충 설명한다고 하면 cluster 예측값 각각에서 실제 값의 빈도를 세었을 때, 가장 많은 빈도를 가진 클래스로 매핑을 시켜줘서 라벨을 붙인다.

 

그리고 참고로 알다시피 k-means 같은 경우 거리 기반이 주로 유클리디안이기 때문에 만약 변수에 범주형이 있다면, 많이 적절하지는 못할 수 있으니, 참고하길 바란다. (꼭 그렇다고 못쓰는 것은 아닌 것 같기도 하고... 그냥 잘 안된다 정도?)

 

여기서 사용한 sklearn 버전은 0.23.2다

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from ipywidgets import interact

from sklearn import preprocessing
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn import datasets 

 

class kmeans_clustring(object) :
    def __init__(self, x_columns:list , y_column:str , data:pd.DataFrame) :
        self.x_columns = x_columns
        self.y_column = y_column
        self.data = data
        self.y_true_label_encoder = preprocessing.LabelEncoder()
        self.y_true_encoded       = self.y_true_label_encoder.fit_transform(data[y_column])
        self.kMeansModels              = dict() # k값별 모델 저장할 딕셔너리
        self.kMeansModelPreds          = dict() # k값별 모델 예측 결과 저장할 딕셔너리
        self.kMeansModelLabelEncoder   = dict() # k값별 라벨인코더 저장할 딕셔너리
        uni_key = np.unique(y_true_encoded) 
        self.frequent_y_dict = dict(zip(uni_key, [0]*len(uni_key)))
        
    def define_k(self, ks : list) :
        self.ks = ks
    
    def excute(self) :
        """
        """
        for k in self.ks: # k값 순회
            model = KMeans(n_clusters=k, random_state=0, n_init=100)  # k개 클러스터로 모델 생성
            cluster_labels = model.fit_predict(self.data[self.x_columns].values) # X컬럼으로 지정된 필드값으로 피팅
            y_pred_label_encoder = preprocessing.LabelEncoder() # 예측한 클러스터에서 사용할 라벨인코더
            # 초기 임의 값 (unknown 1, unknown 2...) 으로 인코딩한다
            y_pred_label_encoder.fit(np.array(['unknown ' + str(i+1) for i in range(0, k, 1)], dtype=object))
            for pred_label_num in range(0,k,1): # 각 클러스터 순회        
                self.frequent_y_dict = {k:0 for k in self.frequent_y_dict.keys()}
                # 해당 클러스터에서 가장 많이 출력한 실제 값의 인덱스를 구한다
                frequent_dict = pd.Series(self.y_true_encoded[cluster_labels==pred_label_num]).value_counts().to_dict()
                self.frequent_y_dict.update(frequent_dict)
                most_frequent_index = max(self.frequent_y_dict.keys(), key=self.find_key)
                y_pred_label_encoder.classes_[pred_label_num] = self.y_true_label_encoder.classes_[most_frequent_index]
            self.kMeansModels[k]     = model                       # 모델 저장
            self.kMeansModelPreds[k] = cluster_labels              # 모델 예측결과 저장     
            self.kMeansModelLabelEncoder[k] = y_pred_label_encoder # 라벨인코더 저장

    def render_plot(self, x_col='petal_length', y_col='petal_width', k=3):
        y_pred        = self.kMeansModelPreds[k]        # 모델 예측값
        label_encoder = self.kMeansModelLabelEncoder[k] # 라벨인코더
        # 원본과 예측값을 합쳐 데이터셋을 준비
        mdf = pd.concat([self.data, pd.DataFrame(label_encoder.inverse_transform(y_pred), columns=[self.y_column+'_pred'])], axis=1)

        plt.figure(figsize=(15,8)) # 출력 크기를 지정

        ddf = mdf[mdf[self.y_column] != mdf[self.y_column+'_pred']] # 실제라벨과 예측라벨이 틀린 경우 추출
        # X기호로 error임을 표시한다
        plt.scatter(x=ddf[x_col], y=ddf[y_col], c='black', marker='x', s=300, label='error') 

        colors = ['blue', 'red', 'green', 'cyan', 'magenta', 'yellow', 'black'] # 컬러값 리스트
        for (idx,cls) in enumerate(list(self.y_true_label_encoder.classes_)):
            ddf = mdf[mdf[self.y_column] == cls] # 실제라벨 데이터는 작은 점으로 출력
            plt.scatter(x=ddf[x_col], y=ddf[y_col], c=colors[idx], marker='.', linewidths=3, s=50, label=cls)
            ddf = mdf[mdf[y_column+'_pred'] == cls] # 예측라벨 데이터는 큰 원으로 출력
            plt.scatter(x=ddf[x_col], y=ddf[y_col], c=colors[idx], marker='o', linewidths=13, s=2, label=cls+' pred')

        plt.legend();plt.xlabel(x_col);plt.ylabel(y_col)
        print('accuracy {:.2f}'.format(np.mean(mdf[self.y_column] == mdf[self.y_column+'_pred']))) # 정확도 출력
        plt.show()
        
    def interact(self,) :
        interact(self.render_plot, x_col=self.x_columns, y_col=self.x_columns, k=self.ks)
        
    def find_key(self,x) :
        return self.frequent_y_dict[x]

 

필요한 것은 다음과 같다.

  • data : pandas format
  • x_columns : 연속형 변수들 (list)
  • y_column : class (int가 아닌 string 형태)

 

iris 데이터의 경우

iris_df = sns.load_dataset('iris').sample(frac=1).reset_index(drop=True)
x_columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']
y_column  = 'species'
cluster = kmeans_clustring(x_columns= x_columns,y_column = y_column,data = iris_df)
cluster.define_k(ks=[2,3,4,5,6,7,8,9])
cluster.excute()
cluster.interact()

 

SCRIPT

https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js

SCRIPT

(adsbygoogle = window.adsbygoogle || []).push({});

wine 데이터의 경우

sklearn 버전에 따라서 다르게 dataset을 load하니 주의!!

## sklearn.__version__ == 0.23.2
wine_data = datasets.load_wine(return_X_y=False, as_frame=True)
x_columns = wine_data["feature_names"]
y_column = "target"
wine_df = pd.concat([wine_data["data"] , wine_data["target"]],axis=1)
replace__ = dict(zip(wine_df["target"].unique() ,wine_data["target_names"]))
wine_df["target"] = wine_df["target"].replace(replace__)
## sklearn.__version__ == 0.22
wine_data = datasets.load_wine(return_X_y=False)
x_columns = wine_data["feature_names"]
y_column = "target"
wine_df = pd.DataFrame(
    np.concatenate([wine_data["data"] , wine_data["target"].reshape(-1,1)],axis=1),
    columns = x_columns + [y_column])
replace__ = dict(zip(wine_df["target"].unique() ,wine_data["target_names"]))
wine_df["target"] = wine_df["target"].replace(replace__)

 

cluster = kmeans_clustring(x_columns= x_columns,y_column = y_column,data = wine_df)
cluster.define_k(ks=[2,3,4,5,6,7,8,9])
cluster.excute()
cluster.interact()

 

 

 

참고 :

blog.naver.com/PostView.nhn?blogId=wideeyed&logNo=221534602937

 

[ML] IRIS 꽃 데이터 클러스터링 with KMeans

IRIS 꽃 데이터를 클러스터링하는 방법에 대해서 알아봅니다.​KMeans란? 각 클러스터와 거리 차이의...

blog.naver.com

 

728x90