[k-means]타깃마케팅을 위한 소비자군집 분석하기
<개념>
1. 비지도 학습
-훈련 데이터에 타깃값이 주어지지 않은 상태에서 학습 수행
-훈련 데이터를 학습하여 모델을 생성하면서 유사한 특성을 가지는 데이터를 클러스터로 구성
-새로운 데이터의 특성을 분석하여 해당하는 클러스터를 예측
2.군집화
데이터를 클러스터(군집)로 구성하는 작업
3.군집화의 목표
서로 유사한 데이터들은 같은 그룹으로, 서로 유사하지 않은 데이터는 다른 그룹으로 분리한 것
-k개의 클러스터 수 결정
-데이터의 유사도? means 각 데이터와 클러스터 중심점 과의 평균거리
4.K-means
1)k개의 임의의 중심점 배치
2) 각 데이터들을 가장 가까운 중심점으로 할당(군집으로 형성
3)군집 내 데이터들을 기반으로 중심점 이동
4)중심점의 이동이 없을 때 까지 반복
5. K-평균 알고리즘
-k개의 중심점을 임의 위치로 잡고 중심점을 기준으로 가까이 있는 데이터를 확인한 뒤 그들과의 거리(유클리디안 거리의 제곱을 사용하여 계산)의 평균 지점으로 중심점을 이동하는 방식
-가장 많이 활용하는 군집화 알고리즘
- 클러스터의 수를 나타내는 K를 직접 지정해야함
6. 엘보 방법
-왜곡: 클러스터의 중심점과 클러스터 내의 데이터 거리 차이의 제곱값의 합
- 클러스터의 개수 K의 변화에 따른 왜곡의 변화를 그래프로 그려보면 그래프가 꺽이는 지점인 엘보 -> 그 지점의 K를 최적의 K로 선택
7. 실루엣 분석
-클러스터 내에 있는 데이터가 얼마나 조밀하게 모여있는지를 측정하는 그래프 도구
-데이터 i가 해당 클러스터 내의 데이터와 얼마나 가까운가를 나타내는 클러스터 응집력 a(i)
-가장 가까운 다른 클러스터 내의 데이터와 얼마나 떨어져있는가를 나타내는 클러스터 분리도 b(i)를 이용
-실루엣 계수 S(i) = b(i)-a(i) / max(a(i),b(i))
- -1~1 사이의 값을 가지며 1에 가까울수록 좋은 군집화 : 1에 가까우면 다른 군집과 떨어져있음, -1에 가까우면 다른 군집에 샘플이 할당되어 있음
-군집이 잘 되었는지 성능 평가 기준이 모호함
-각 군집 간의 거리가 얼마나 효율적으로 분리되었는가? 실루엣 계수로 판단
-다른 군집과의 거리는 떨어져 있고, 동일 군집끼리의 데이터는 서로 가깝게 뭉쳐 있는 척도/지표
<프로젝트>
-목표: k-평균으로 온라인 판매 데이터를 분석한 수 타깃 마케팅을 위한 소비자 군집을 만드는 프로젝트
-타깃 마케팅: 구매 행동을 가진 그룹을 세분화하여 각 특성에 맞는 마케팅을 하는 전략
-타깃 마케팅에서 고객 정보를 분석하고 그룹을 세분화하는 작업에 군집 분석 사용
-사전 데이터 : Online Retail Data Set -UCI Machine Learning Repository 검색 -> Data Folder 클릭 -> Online Retail.xlsx
■ 데이터 준비 및 탐색
import pandas as pd
retail_df= pd.read_excel('./Online_Retail.xlsx')
retail_df.head()
#오류데이터정제
retail_df= retail_df[retail_df['Quantity']>0]
retail_df= retail_df[retail_df['UnitPrice']>0]
retail_df= retail_df[retail_df['CustomerID'].notnull()]
#'CustomerID' 자료형을정수형으로변환
retail_df['CustomerID'] = retail_df['CustomerID'].astype(int)
#중복레코드제거
retail_df.drop_duplicates(inplace= True)
print(retail_df.shape) #작업확인용출력
pd.DataFrame([{'Product':len(retail_df['StockCode'].value_counts()),
'Transaction':len(retail_df['InvoiceNo'].value_counts()), 'Customer':len(retail_df['CustomerID'].
value_counts())}], columns = ['Product', 'Transaction',
'Customer'], index = ['counts'])
retail_df['Country'].value_counts()
#주문금액컬럼추가
retail_df['SaleAmount'] = retail_df['UnitPrice']*retail_df['Quantity']
retail_df.head() #작업확인용출력
aggregations = {
'InvoiceNo':'count',
'SaleAmount':'sum',
'InvoiceDate':'max'
}
customer_df= retail_df.groupby('CustomerID').agg(aggregations)
#customer_df = retail_df.sort_values('SaleAmount').groupby('CustomerID').agg(aggregations)
customer_df= customer_df.reset_index() #groupby에자동으로지정한컬럼으로인덱스가변함에따른인덱스초기화
customer_df.head() #작업확인용출력
customer_df= customer_df.rename(columns = {'InvoiceNo':'Freq', 'InvoiceDate':'ElapsedDays'})
customer_df.head() #작업확인용출력
import datetime
customer_df['ElapsedDays'] = datetime.datetime(2011,12,10) -customer_df['ElapsedDays']
customer_df.head() #작업확인용출력
customer_df['ElapsedDays'] = customer_df['ElapsedDays'].apply(lambda x: x.days+1)
customer_df.head() #작업확인용출력
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots()
ax.boxplot([customer_df['Freq'], customer_df['SaleAmount'], customer_df['ElapsedDays']], sym= 'bo')
plt.xticks([1, 2, 3], ['Freq', 'SaleAmount','ElapsedDays'])
plt.show()
파란색 점sym='bo' 아웃레이어 값이 많은 것은 데이터 값이 고르게 분포 x 치우쳐 있다.
로그 함수를 적용하여 값의 분포를 고르게 조정
import numpy as np
customer_df['Freq_log'] = np.log1p(customer_df['Freq'])
customer_df['SaleAmount_log'] = np.log1p(customer_df['SaleAmount'])
customer_df['ElapsedDays_log'] = np.log1p(customer_df['ElapsedDays'])
customer_df.head() #작업확인용출력
fig, ax = plt.subplots()
ax.boxplot([customer_df['Freq_log'], customer_df['SaleAmount_log'], customer_df['ElapsedDays_log']], sym= 'bo')
plt.xticks([1, 2, 3], ['Freq_log', 'SaleAmount_log', 'ElapsedDays_log'])
plt.show()
아웃레이어가 줄어들고 모양도 균형을 잡힘
■ 분석 모델 구축
X_features 를 정규 분포로 스케일링
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score, silhouette_samples
X_features= customer_df[['Freq_log', 'SaleAmount_log', 'ElapsedDays_log']].values
from sklearn.preprocessing import StandardScaler
X_features_scaled= StandardScaler().fit_transform(X_features) #정규분포로 스케일링 한다.
엘보 방법으로 클러스터 개수 k선택하기
distortions = []
for i in range(1, 11):
kmeans_i= KMeans(n_clusters= i, random_state= 0) #모델생성
kmeans_i.fit(X_features_scaled) #모델훈련
distortions.append(kmeans_i.inertia_)
#cluster에속한samples가얼마나가깝게모여있는지나타내는값(거리의제곱)
plt.plot(range(1,11), distortions, marker = 'o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.show()
엘보는 3, 클러스터 개수를 3을하고 모델 구축
kmeans= KMeans(n_clusters=3, random_state=0) #모델생성
#모델학습과결과예측(클러스터레이블생성)
Y_labels= kmeans.fit_predict(X_features_scaled)
customer_df['ClusterLabel'] = Y_labels
customer_df.head()
■ 결과 분석 및 시각화
1)각 클러스터의 비중을 가로 바 차트로 시각화
from matplotlib import cm
def silhouetteViz(n_cluster, X_features):
kmeans= KMeans(n_clusters= n_cluster, random_state= 0)
Y_labels= kmeans.fit_predict(X_features)
silhouette_values= silhouette_samples(X_features, Y_labels, metric = 'euclidean')
y_ax_lower, y_ax_upper= 0, 0
y_ticks= []
for c in range(n_cluster):
c_silhouettes= silhouette_values[Y_labels== c]
c_silhouettes.sort()
y_ax_upper+= len(c_silhouettes)
color = cm.jet(float(c) / n_cluster)
plt.barh(range(y_ax_lower, y_ax_upper), c_silhouettes, height = 1.0, edgecolor= 'none', color = color)
y_ticks.append((y_ax_lower+ y_ax_upper) / 2.)
y_ax_lower+= len(c_silhouettes)
silhouette_avg= np.mean(silhouette_values)
plt.axvline(silhouette_avg, color = 'red', linestyle= '--')
plt.title('Number of Cluster : '+ str(n_cluster) + '\n' + 'Silhouette Score : '+ str(round(silhouette_avg,3)))
plt.yticks(y_ticks, range(n_cluster))
plt.xticks([0, 0.2, 0.4, 0.6, 0.8, 1])
plt.ylabel('Cluster')
plt.xlabel('Silhouette coefficient')
plt.tight_layout()
plt.show()
#코드분석하지 않았음
2)클러스터의 데이터 분포를 확인하기 위해 스캐터 차트로 시각화
def clusterScatter(n_cluster, X_features):
c_colors= []
kmeans= KMeans(n_clusters= n_cluster, random_state= 0)
Y_labels= kmeans.fit_predict(X_features)
for i in range(n_cluster):
c_color= cm.jet(float(i) / n_cluster) #클러스터의색상설정
c_colors.append(c_color)
#클러스터의데이터분포를동그라미로시각화
plt.scatter(X_features[Y_labels== i,0], X_features[Y_labels== i,1], marker = 'o', color = c_color, edgecolor= 'black', s = 50, label = 'cluster '+ str(i))
#각클러스터의중심점을삼각형으로표시
for i in range(n_cluster):
plt.scatter(kmeans.cluster_centers_[i,0], kmeans.cluster_centers_[i,1], marker = '^', color = c_colors[i],edgecolor= 'w', s = 200)
plt.legend()
plt.grid()
plt.tight_layout()
plt.show()
3) 3d scatter plot
%matplotlib notebook
# 주피터노트북사용시matplotlib backend 활성화
from mpl_toolkits.mplot3d import Axes3D
def clusterScatter_3D(n_cluster, X_features):
c_colors= []
kmeans= KMeans(n_clusters= n_cluster, random_state= 0)
Y_labels= kmeans.fit_predict(X_features)
plt.figure(figsize=(3, 3))
ax = plt.axes(projection='3d')
for i in range(n_cluster):
c_color= cm.jet(float(i) / n_cluster) #클러스터의색상설정
c_colors.append(c_color)
#클러스터의데이터분포를동그라미로시각화
ax.scatter(X_features[Y_labels== i,0], X_features[Y_labels== i,1], X_features[Y_labels== i,2], label = str(i))
#각클러스터의중심점을삼각형으로표시
for i in range(n_cluster):
ax.scatter(kmeans.cluster_centers_[i,0], kmeans.cluster_centers_[i,1], kmeans.cluster_centers_[i,2])
ax.legend()
ax.grid()
#ax.tight_layout()
ax.set_xlabel('Freq_log')
ax.set_ylabel('SaleAmount_log')
ax.set_zlabel('ElapsedDays_log')
plt.show()
클러스터의 데이터 분포(원으로 표시)와 클러스터의 중심점 위치(삼각형으로 표시)
군집화가 잘된 클러스터 4개를 이용하여 모델생성
best_cluster= 4
kmeans= KMeans(n_clusters= best_cluster, random_state= 0)
Y_labels= kmeans.fit_predict(X_features_scaled)
customer_df['ClusterLabel'] = Y_labels
customer_df.head()
clusterLabel을 기준으로 그룹을 만든다
customer_df.groupby('ClusterLabel')['CustomerID'].count()
customer_cluster_df= customer_df.drop(['Freq_log', 'SaleAmount_log', 'ElapsedDays_log'],axis = 1, inplace= False)
#주문1회당평균구매금액: SaleAmountAvg
customer_cluster_df['SaleAmountAvg'] = customer_cluster_df['SaleAmount']/customer_cluster_df['Freq']
customer_cluster_df.head()
customer_cluster_df.drop(['CustomerID'],axis = 1, inplace= False).groupby('ClusterLabel').mean()
출처: 데이터 과학 기반의 파이썬 빅데이터 분석(이지은 지음)책을 공부하며 작성한 내용입니다.