본문 바로가기
AI/머신러닝

12. K-평균 군집화 (KMeans) | Marketing

by 사라리24 2024. 6. 17.
SMALL

1. Clusters

* 유사한 특성을 가진 개체들의 집합
* 고객 분류, 유전자 분석, 이미지 분할

 

  • import

       
        import matplotlib.pyplot as plt
        import numpy as np
        import pandas as pd
        import seaborn as sns
        from sklearn.datasets import make_blobs

 

 

  • make_blobs 함수 : 가상 데이터를 생성

       

        X, y = make_blobs(n_samples=100, centers=3, random_state=2024)
 


  • n_samples=100은 100개의 샘플 데이터를 생성
  • centers=3은 3개의 중심(클러스터)을 생성
  • random_state=2024은 랜덤 시드(seed)를 설정
  • X는 생성된 샘플 데이터의 좌표를 담고 있는 배열
    y는 각 샘플이 속하는 클러스터의 레이블을 담고 있는 배열

 

 

  • x : 샘플 데이터의 좌표

       

       X = pd.DataFrame(X)
       X


 

 

  • y: 각 데이터 포인트가 속하는 클러스터 레이블을 나타내는 1차원 배열

       

        y



array([0, 2, 1, 0, 2, 0, 0, 1, 0, 1, 0, 0, 2, 0, 2, 1, 2, 2, 1, 1, 0, 2,
       2, 1, 2, 2, 1, 2, 2, 0, 2, 2, 1, 2, 1, 2, 1, 1, 1, 0, 0, 2, 1, 1,
       0, 1, 1, 0, 0, 0, 2, 1, 2, 1, 0, 2, 0, 2, 1, 0, 2, 2, 2, 1, 0, 2,
       0, 1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 2, 1, 2, 1, 0, 1, 2, 1, 0, 0, 2,
       2, 1, 1, 2, 1, 2, 1, 0, 1, 0, 1, 0])

 

 

KMeans 클러스터링을 하지 않은 경우 ( hue = y )  _ 실제값

  •  2차원 데이터셋의 산점도를 그리기 ( random_state=2024 )


        sns.scatterplot(x=X[0], y=X[1], hue=y)



hue = y:
  • y는 실제 레이블로, 데이터를 실제 클래스(또는 카테고리)로 구분하여 시각화
  • 실제 레이블에 따라 색상이 달라집니다.
  • 실제 분포와 카테고리를 시각적으로 표현, 감독 학습(Supervised Learning)에서 데이터의 분포를 이해하는 데 유용

 

  • random_state=2024  -> random_state=2023 으로 변경하고 다시 생성
    이 섞임이 적절하다고 판단해 2023으로 진행

       

        sns.scatterplot(x=X[0], y=X[1], hue=y)




 

 

KMeans 클러스터링을 한 경우 ( hue = pred ) _ 예측값

  • n_clusters = 3 인 경우

       
        from sklearn.cluster import KMeans

        km = KMeans(n_clusters=3)
        km.fit(X)
        pred=km.predict(X)

        sns.scatterplot(x=X[0], y=X[1], hue=pred)


  • from sklearn.cluster import KMeans:
    • KMeans 클러스터링 알고리즘을 사용할 수 있도록 sklearn 라이브러리에서 KMeans 클래스를 불러옵니다.
  • km = KMeans(n_clusters=3):
    • KMeans 객체를 생성, 클러스터의 개수를 3개로 설정
      데이터 X를 3개의 클러스터로 나누기
  • km.fit(X):
    • X에 대해 KMeans 클러스터링을 수행
      이는 알고리즘이 데이터 포인트들을 분석하여 3개의 클러스터로 나누는 최적의 방법을 찾는 과정
  • pred = km.predict(X):
    • 클러스터링 결과를 바탕으로 각 데이터 포인트가 속한 클러스터 레이블을 예측
      pred는 각 데이터 포인트가 어느 클러스터에 속하는지를 나타내는 레이블(0, 1, 2)을 갖는 배열
  • sns.scatterplot(x=X[0], y=X[1], hue=pred):
    • Seaborn 라이브러리를 사용하여 클러스터링 결과를 2차원 평면에 시각화
      X의 첫 번째와 두 번째 특성을 x축과 y축에 사용
      , pred 값을 기준으로 색상을 다르게 하여 각 클러스터를 구분

      hue=pred :
      • pred는 KMeans 클러스터링 알고리즘이 예측한 클러스터 레이블로, 각 데이터 포인트가 속한 클러스터를 나타냅니다.
      • 각 데이터 포인트는 클러스터링 결과에 따라 색상이 달라집니다.
      • 알고리즘이 데이터를 어떻게 그룹화했는지 시각적으로 표현, 비지도 학습(Unsupervised Learning)에서 데이터의 군집 구조를 이해하는 데 유용



 

 

  • n_clusters = 5 인 경우

       
        from sklearn.cluster import KMeans


        km = KMeans(n_clusters=5)
        km.fit(X)
        pred=km.predict(X)

        sns.scatterplot(x=X[0], y=X[1], hue=pred)


 

  • Inertia (관성): KMeans 알고리즘에서 관성은 각 데이터 포인트와 그것이 속한 클러스터 중심점 간의 거리 제곱합, 
                               클러스터의 응집도를 측정하는 지표

       

        # 평가값: 하나의 클러스터안에 중심점으로부터 각각의 데이터 거리를 합한 값의 평균
        km.inertia_


161.15478072681793
값의 의미:
  • 161.15478072681793이라는 값은 주어진 데이터 세트에 대해 KMeans 알고리즘이 3개의 클러스터로 나눴을 때,
    각 데이터 포인트와 그 소속 클러스터 중심점 간의 거리 제곱합이 161.15478072681793임을 의미합니다.
  • 낮은 관성값: 관성값이 낮을수록 클러스터링이 더 좋다고 볼 수 있습니다.
                         이는 클러스터 내의 데이터 포인트들이 중심점에 더 가깝게 모여 있음을 나타냅니다.
  • 높은 관성값: 관성값이 높을수록 클러스터링 품질이 낮아질 수 있습니다.
                         이는 클러스터 내의 데이터 포인트들이 중심점으로부터 더 멀리 퍼져 있음을 나타냅니다.

 

 

  • K에 대한 inertia 값을 계산하고 리스트에 저장 ( 클러스터 개수(K)
    inertia 값의 변화, 적절한 클러스터 개수를 결정하는 데 도움을 줍니다.

     
          inertia_list = []

          for i in range(2, 11):
              km = KMeans(n_clusters=i)
              km.fit(X)
              inertia_list.append(km.inertia_)

          inertia_list

 
  • inertia_list = []: 관성값을 저장할 빈 리스트

  • for i in range(2, 11): 클러스터 수를 2~ 10까지 변경하면서 루프를 실행 (i는 클러스터 수)

  • km = KMeans(n_clusters=i): 클러스터 수가 i인 KMeans 객체를 생성

  • km.fit(X): 데이터 X에 대해 KMeans 클러스터링을 수행
  • inertia_list.append(km.inertia_):
    클러스터 수가 i인 KMeans 클러스터링의 관성값이 리스트에 추가
    km.inertia_는 각 데이터 포인트가 속한 클러스터의 중심점과의 거리 제곱합(Sum of Squared Distances, SSD)

  • inertia_list: 다양한 클러스터 수에 대한 관성값을 포함한 리스트를 반환
[1578.856698952461,
 222.7636237762,
 192.9939401435695,
 161.0432695929017,
 132.08269283797623,
 110.36480652885867,
 91.26636887566491,
 81.14561909030127,
 72.47155026760123]

 



  • 엘보우 메서드(Elbow Method)
    K-Means 클러스터링에서 적절한 클러스터 개수(K)를 결정하는 시각적인 방법을 구현
    클러스터 개수(K)에 따른 inertia 값의 변화를 시각적으로 확인

       
          sns.lineplot(x=range(2, 11), y=inertia_list) # 엘보우 메서드


 

 

2. Marketing 데이터셋 살펴보기

 

 

  • 데이터 가져오기

       
        mkt_df = pd.read_csv('/content/drive/MyDrive/1. KDT/6. 머신러닝 딥러닝/데이터/marketing.csv')
        mkt_df

 

 

 

 

  • 정보보기

      
      mkt_df.info()





* ID: 고객 아이디

* Year_Birth: 출생 연도
* Education: 학력
* Marital_Status: 결혼 여부
* Income: 소득
* Kidhome: 어린이 수
* Teenhome: 청소년 수
* Dt_Customer: 고객 등록일
* Recency: 마지막 구매일로부터 경과일
* MntWines: 와인 구매액
* MntFruits: 과일 구매액
* MntMeatProducts: 육류 구매액
* MntFishProducts: 어류 구매액
* MntSweetProducts: 단맛 제품 구매액
* MntGoldProds: 골드 제품 구매액
* NumDealsPurchases: 할인 행사 구매 수
* NumWebPurchases: 웹에서 구매 수
* NumCatalogPurchases: 카탈로그에서 구매 수
* NumStorePurchases: 매장에서의 구매 수
* NumWebVisitsMonth: 월별 웹 방문 수
* Complain: 불만 여부

 

 

  • 필요없는 행 삭제

       
        mkt_df.drop('ID', axis=1, inplace=True)

        mkt_df.describe()





 

 

  • [ Year_Birth ] 열을 기준으로 오름차순으로 정렬

       

        mkt_df.sort_values('Year_Birth')


 

  • [ Income ] 열을 기준으로 내림차순으로 정렬

       
        mkt_df.sort_values('Income', ascending=False)


 

 

 

  • [ Income ] 열의 값이 666666인 행을 제거한 후 결과를 mkt_df에 다시 저장

       
          # mkt_df = mkt_df[mkt_df['income'] < 200000]  NaN이 저장되지 않음

          mkt_df = mkt_df[mkt_df['Income'] != 666666]

          mkt_df.sort_values('Income', ascending=False)


 

 

  • NaN 값 확인

       
        mkt_df.isna().mean()


 

 

  • 결측값(NA, NaN)을 제거

       
        mkt_df = mkt_df.dropna()

        mkt_df.isna().mean()

 

 

  • [ DT_Customer ] 고객 등록일이 object 타입

       
        mkt_df.info()


 

  • [ Dt_Customer ] 고객등록일을 datetime 타입으로 변환

       
          mkt_df['Dt_Customer'] = pd.to_datetime(mkt_df['Dt_Customer'], format='%d-%m-%Y')

          mkt_df.info()

 

 

 

  • 마지막으로 가입한 사람을 기준으로 가입 날짜(달)를 구하는 작업

       
      # 마지막으로 가입된 사람을 기준으로 가입 날짜(달) 구하기
      mkt_df['pass_month'] = (mkt_df['Dt_Customer'].max().year * 12 + mkt_df['Dt_Customer'].max().month) - (mkt_df['Dt_Customer'].dt.year * 12 + mkt_df['Dt_Customer'].dt.month)

      mkt_df.head()

 
  • mkt_df['Dt_Customer'].max().year * 12 + mkt_df['Dt_Customer'].max().month:
    • Dt_Customer 열에서 가장 최근에 등록한 사람의 가입 연월을 구합니다.
    • mkt_df['Dt_Customer'].max()는 Dt_Customer 열에서 가장 큰(최근) 날짜를 반환합니다.
    • year * 12 + month는 해당 연월을 연 단위로 변환하여 연도를 개월로 변환합니다.
  • (mkt_df['Dt_Customer'].dt.year * 12 + mkt_df['Dt_Customer'].dt.month):
    • Dt_Customer 열의 모든 값을 연월 형식으로 변환합니다.
    • dt.year 및 dt.month는 각각 연도와 월을 나타내는 pandas의 datetime 속성입니다.
  • mkt_df['pass_month']:
    • 새로운 열을 만들어서 계산 결과를 할당합니다.



  • [ Dt_Customer ]  고객등록일 삭제

     
      mkt_df.drop('Dt_Customer', axis=1, inplace=True)

      mkt_df.head()





 

 

  • 충성고객 알아보기
    여러 열의 값을 합산하여 [ Total_mnt ] 를 생성

       
          # mkt_df['Total_mnt'] = 와인, 과일, 육류, 어류, 단맛, 골드 합하기

          mkt_df['Total_mnt'] = mkt_df[['MntWines', 'MntFruits', 'MntMeatProducts', 'MntFishProducts', 'MntSweetProducts', 'MntGoldProds']].sum(axis=1)
 
           mkt_df.head()


 




  • [ Children ] 파생변수 생성 =  [ Kidhome ] + [ Teenhome ] 
  • [ Kidhome ] + [ Teenhome ] 행 삭제

       
      mkt_df.info()

      mkt_df['Children'] = mkt_df[['Kidhome', 'Teenhome']].sum(axis=1)

      mkt_df['Children'] = mkt_df[['Kidhome', 'Teenhome']].sum(axis=1)
      mkt_df.head()

      mkt_df.drop(['Kidhome', 'Teenhome'],axis=1,inplace=True)







 

  • [ Education ] 고유값 확인


            mkt_df['Education'].value_counts()


 

 

  • [ Marital_Status ] 결혼여부  값들의 빈도

       
        mkt_df['Marital_Status'].value_counts()


 

 

  • [ Marital_Status ] 결혼여부의 특정 값들을 다른 값으로 치환, 각 값들의 빈도 확인


          mkt_df
['Marital_Status'] = mkt_df['Marital_Status'].replace({
                                          'Married':'Partner',
                                          'Together':'Partner',
                                          'Single':'Single',
                                          'Divorced':'Single',
                                          'Widow':'Single',
                                          'Alone':'Single',
                                          'Absurd':'Single',
                                          'YOLO':'Single'})

          mkt_df['Marital_Status'].value_counts()



 

 

  • [ Education ] , [ Marital_Status ] 문자형 데이터 (원핫인코딩 가능)
       

      mkt_df.info()


 

  • [ Education ] , [ Marital_Status ] 원핫인코딩

       

          mkt_df = pd.get_dummies(mkt_df,columns=['Education','Marital_Status'])

          mkt_df.head()





 

 

  • 데이터를 표준화(Standardization)
    StandardScaler는 데이터를 평균이 0이고 표준편차가 1인 데이터로 변환해주는 기능을 제공

       
        from sklearn.preprocessing import StandardScaler

        ss = StandardScaler()

        ss.fit_transform(mkt_df)


ss = StandardScaler()

StandardScaler 클래스의 인스턴스를 생성하여 ss라는 변수에 할당
이 인스턴스를 사용하여 데이터를 표준화할 준비

ss.fit_transform(mkt_df)

StandardScaler 객체(ss)의 fit_transform 메서드를 호출하여 데이터를 표준화
  • .fit_transform(mkt_df):
    • mkt_df 데이터프레임에 대해 표준화를 수행합니다.
    • fit_transform 메서드는 두 단계를 한 번에 수행합니다:
      • Fit: 데이터에서 평균과 표준편차를 추정하여 저장합니다.
      • Transform: 추정된 평균과 표준편차를 사용하여 데이터를 변환합니다.
    • 반환값은 표준화된 데이터로, 일반적으로 넘파이 배열 또는 희소 행렬로 반환됩니다.


 

 

  • StandardScaler 객체(ss)를 사용하여 mkt_df 데이터프레임의 모든 열을 표준화

       
      pd.DataFrame(ss.fit_transform(mkt_df))  


  • fit_transform 메서드는 두 단계를 수행합니다:
    • Fit: 데이터에서 평균과 표준편차를 추정하여 저장합니다.
    • Transform: 추정된 평균과 표준편차를 사용하여 데이터를 변환합니다.
  • 반환값은 표준화된 데이터로, 일반적으로 넘파이 배열 또는 희소 행렬 형태로 반환

 

 

  • mkt_df 데이터프레임의 모든 열을 표준화, 그 결과를 새로운 데이터프레임으로 생성하여 ss_df에 저장

       
        ss_df = pd.DataFrame(ss.fit_transform(mkt_df),columns=mkt_df.columns)
        ss_df



 

 

3. KMeans

* k개의 중심점을 찍은 후에 중심점에서 각 접간의 거리의 합이 가장 최소가 되는 중심점 k의 위치를 찾고, 
  이 중심점에서 가까운 점들을 중심점으로 묶는 알고리즘
* k개의 클라스터의 수는 정해줘야 함

 

 

  • 각 경우의 클러스터(2~10) 내 제곱합(inertia)을 계산
    (데이터 포인트가 해당 클러스터의 중심에 얼마나 가까이 있는지)

       

      inertia_list =[]

      for i in range(2,11):
        km = KMeans(n_clusters=i,random_state=10)
        km.fit(ss_df)
        inertia_list.append(km.inertia_)



 

 

  • 각 클러스터 개수에 따른 제곱합(inertia)

       

        inertia_list


[42895.856234544146,
 39808.76012368429,
 37612.35981391132,
 36117.28280437582,
 34082.90546728393,
 32068.210471119026,
 30933.637059149678,
 29408.708100372387,
 28483.55451230912]

 

 

  • 클러스터 개수 (2~10) 변화에 따른 클러스터 내 제곱합(inertia)의 변화

       

        sns.lineplot(x=range(2, 11), y=inertia_list)


 

 

4. 실루엣 스코어

* 군집화의 품질을 평가하는 지표로, 
각 데이터 포인트가 자신이 속한 군집과 얼마나 잘 맞는지, 그리고 다른 군집과 얼마나 잘 구분되는지를 측정

* -1 에서 +1  사이의 값을 가지며, 값이 크수록 군집화의 품질이 높음을 나타냄

 

  • 각 클러스터 개수에 대해 K-means 클러스터링을 수행
    클러스터링 결과의 실루엣 점수(silhouette score)를 계산

       
        from sklearn.metrics import silhouette_score

        score =[]

        for i in range(2, 11):
            km = KMeans(n_clusters=i, random_state=2024)
            km.fit(ss_df)
            pred = km.predict(ss_df)
            score.append(silhouette_score(ss_df, pred))

 

from sklearn.metrics import silhouette_score

  • silhouette_score는 클러스터링 결과의 품질을 측정하는 지표 중 하나
    각 데이터 포인트의 실루엣 계수를 평균하여 계산
    실루엣 계수는 데이터 포인트가 속한 클러스터와 다른 클러스터와의 거리를 기반으로 계산 ( 값은 -1에서 1 사이의 범위 )

score = []

  • 빈 리스트 score를 초기화합니다. 이 리스트는 각 클러스터 개수에 따른 실루엣 점수를 저장할 용도로 사용됩니다.

for i in range(2, 11):

  • range(2, 11)은 2부터 10까지의 숫자 범위를 나타냅니다. 이 반복문은 클러스터 개수를 2부터 시작하여 10까지 변화시키면서 코드 블록을 실행합니다.

km = KMeans(n_clusters=i, random_state=2024)

  • KMeans 객체를 생성합니다.
  • n_clusters=i는 클러스터 개수를 i로 설정합니다. i는 반복문을 통해 2부터 10까지 변화하는 값입니다.
  • random_state=2024는 재현 가능성을 위해 설정한 난수 시드입니다. 같은 random_state를 사용하면 결과가 동일하게 재현됩니다.

km.fit(ss_df)

  • KMeans 객체 km을 사용하여 데이터프레임 ss_df를 클러스터링합니다.
  • fit 메서드는 데이터를 기반으로 클러스터링을 수행합니다.

pred = km.predict(ss_df)

  • predict 메서드는 각 데이터 포인트의 클러스터 레이블을 예측합니다. 예측된 클러스터 레이블은 pred 변수에 저장됩니다.

score.append(silhouette_score(ss_df, pred))

  • silhouette_score 함수를 사용하여 클러스터링 결과의 실루엣 점수를 계산합니다.
  • ss_df는 표준화된 데이터프레임을 의미하며, pred는 각 데이터 포인트의 클러스터 레이블을 나타냅니다.
  • 계산된 실루엣 점수는 score 리스트에 추가됩니다.

 

  • seaborn 라이브러리를 사용하여 클러스터링 성능 점수의 변화를 시각화

       

        sns.lineplot(x=range(2,11),y=score)


  • x축: 클러스터 개수(2부터 10까지의 정수).
  • y축: 클러스터링 성능 점수(예: silhouette score).


* 처음으로 올라온 지점이 나누기 좋은 지점


 

 

  • 4개의 클러스터로 나누기 위해 K-means 클러스터링 모델을 학습

       
      km =KMeans(n_clusters=4,random_state=2024)

      km.fit(ss_df)


 

 

 

  • K-means 클러스터링 모델을 사용하여 표준화된 데이터 프레임 ss_df의 각 데이터 포인트가
    어떤 클러스터에 속하는지 예측하고, 예측된 클러스터 레이블을 출력

       
      pred=km.predict(ss_df)
      pred


array([2, 1, 2, ..., 2, 3, 1], dtype=int32)

-------------------------------------------------
해당 데이터 포인트가 속하는 클러스터의 번호(0, 1, 2, 3 중 하나)를 의미

 

 

 

  • [ label ] 열 생성한 후  pred 배열의 값을 할당
    pred 배열: 각 데이터 포인트가 속하는 클러스터를 나타내는 레이블 배열

       
        mkt_df['label']=pred

        mkt_df


 

 

 

  • 나눠진 고객정보 보기
    mkt_df 데이터프레임의 [ label ] 열의 각 클러스터 레이블의 개수를 세어서 출력

       

      mkt_df['label'].value_counts()