- 비지도학습 개념 : 정답을 알려주지 않고 데이터간 유사성으로 정답을 지정하는 것.
- Kmeans Clustering 군집화
- RFM으로 고객 세그멘테이션
- 고객 군집화의 방법
- Recency 구하기
- 불균형 군집화에 대한 로그스케일 적용
- 군집화모델 평가 : 실루엣 스코어



sklearn.cluster.KMeansn_cluster: 군집화 갯수max_iter: 최대 반복 횟수labels_: 각 데이터 포인트가 속한 군집 중심점 레이블cluster_centers: 각 군집 중심점의 좌표비지도 학습 특성상 답이 없기 때문에 평가를 하기 쉽지 않다.
다만, 군집화가 잘 되어있다는 것은 다른 군집간의 거리가 떨어져 있고 동일한 구집끼리는 가까이 있다는 것을 의미한다.
실루엣 분석(Silhouette Analaysis) : 군집간의 거리가 얼마나 효율적으로 분리되어잇는지 측정
sklearn.metrics.sihouette_score: 전제 데이터의 실루엣 계수 평균 값 반환X: 데이터 세트labels: 레이블metrics: 측정 기준 기본은 euclideanimport pandas as pd
import seaborn as sns
import sklearn
import matplotlib.pyplot as plt
iris_df = sns.load_dataset('iris')
iris_df.head(3)
sns.scatterplot(data = iris_df, x = 'sepal_length', y = 'sepal_width')

iris_df_2 = iris_df.copy().drop(columns = 'species')
iris_df_2.head(3)
from sklearn.cluster import KMeans
X = iris_df_2[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]
kmeans = KMeans(n_clusters = 3, random_state=42, init = 'k-means++', max_iter= 300)
kmeans.fit(X) #비지도학습이기 때문에 y값을 넣어주지 않음
iris_df_2['cluster'] = kmeans.predict(X)
iris_df_2.head(3)

plt.figure(figsize = (12, 6))
plt.subplot(1, 2, 1)
sns.scatterplot(data = iris_df, x = 'sepal_length', y = 'sepal_width', hue = 'species')
plt.title('Original')
plt.subplot(1, 2, 2)
sns.scatterplot(data = iris_df_2, x = 'sepal_length', y = 'sepal_width', hue = 'cluster', palette = 'winter')
plt.title('Clustering')
plt.show()

왼쪽 그래프는 군집화 그래프와 비교해보기 위해 species를 그려본 것.
1번 군집은 잘 군집화 된 것으로 보임
from sklearn.metrics import silhouette_score
silhouette_score(X, iris_df_2['cluster'])
0.5528190123564097
이 수준이면 클러스터링이 잘 되었다고 판단할 수 있다고 gpt가 말해주었다.
비지도 학습이 가장 많이 사용되는 곳이 고객관리(CRM)
타겟마케팅을 위해 고객 특성에 맞게 세분화할 수 있다.
import pandas as pd
import matplotlib.pyplot
import seaborn as sns
import sklearn
retail_df = pd.read_excel('/Users/sookyeong/Desktop/Online Retail.xlsx')
retail_df.isnull().sum()
retail_df.describe(include = 'all')
con_customer = retail_df['CustomerID'].notnull()
con_quan = retail_df['Quantity'] > 0
con_price = retail_df['UnitPrice'] > 0
con_county = retail_df['Country'] == 'United Kingdom'
retail_df2 = retail_df[con_customer & con_quan & con_price & con_county]
#매출총합
retail_df2['amount_monetary'] = retail_df2['Quantity'] * retail_df2['UnitPrice']
retail_df2['amount_monetary'] = retail_df2['amount_monetary'].astype('int')
#최근방문경과일
import datetime as dt
#2011.12.10일 기준으로 각 날짜를 빼고 + 1
# 추후 customerid 기준으로 period의 최소의 period를 구하면 그것이 recency
retail_df2['Period'] = (dt.datetime(2011, 12, 10) - retail_df2['InvoiceDate']).apply(lambda x : x.days + 1)
retail_df2
#고객별 RFM
rfm_df = retail_df2.groupby('CustomerID').agg({'Period' : 'min',
'InvoiceNo' : 'count',
'amount_monetary' : 'sum'})
rfm_df.columns = ['Recency', 'Frequency', 'Monetary']
rfm_df

sns.pairplot(rfm_df)
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = rfm_df[['Recency', 'Frequency', 'Monetary']]
X_features = sc.fit_transform(X)
X_features
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
kmeans = KMeans(n_clusters = 3, random_state = 42)
labels = kmeans.fit_predict(X_features)
rfm_df['label'] = labels
silhouette_score(X_features, labels) -- 0.592575402996014
### 여러개의 클러스터링 갯수를 List로 입력 받아 각각의 실루엣 계수를 면적으로 시각화한 함수 작성
def visualize_silhouette(cluster_lists, X_features):
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
# 입력값으로 클러스터링 갯수들을 리스트로 받아서, 각 갯수별로 클러스터링을 적용하고 실루엣 개수를 구함
n_cols = len(cluster_lists)
# plt.subplots()으로 리스트에 기재된 클러스터링 수만큼의 sub figures를 가지는 axs 생성
fig, axs = plt.subplots(figsize=(4*n_cols, 4), nrows=1, ncols=n_cols)
# 리스트에 기재된 클러스터링 갯수들을 차례로 iteration 수행하면서 실루엣 개수 시각화
for ind, n_cluster in enumerate(cluster_lists):
# KMeans 클러스터링 수행하고, 실루엣 스코어와 개별 데이터의 실루엣 값 계산.
clusterer = KMeans(n_clusters = n_cluster, max_iter=500, random_state=0)
cluster_labels = clusterer.fit_predict(X_features)
sil_avg = silhouette_score(X_features, cluster_labels)
sil_values = silhouette_samples(X_features, cluster_labels)
y_lower = 10
axs[ind].set_title('Number of Cluster : '+ str(n_cluster)+'\n' \
'Silhouette Score :' + str(round(sil_avg,3)) )
axs[ind].set_xlabel("The silhouette coefficient values")
axs[ind].set_ylabel("Cluster label")
axs[ind].set_xlim([-0.1, 1])
axs[ind].set_ylim([0, len(X_features) + (n_cluster + 1) * 10])
axs[ind].set_yticks([]) # Clear the yaxis labels / ticks
axs[ind].set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])
# 클러스터링 갯수별로 fill_betweenx( )형태의 막대 그래프 표현.
for i in range(n_cluster):
ith_cluster_sil_values = sil_values[cluster_labels==i]
ith_cluster_sil_values.sort()
size_cluster_i = ith_cluster_sil_values.shape[0]
y_upper = y_lower + size_cluster_i
color = cm.nipy_spectral(float(i) / n_cluster)
axs[ind].fill_betweenx(np.arange(y_lower, y_upper), 0, ith_cluster_sil_values, \
facecolor=color, edgecolor=color, alpha=0.7)
axs[ind].text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
y_lower = y_upper + 10
axs[ind].axvline(x=sil_avg, color="red", linestyle="--")
visualize_silhouette([2,3,4,5,6], X_features)

실루엣 스코어는 높은데 군집의 불균형이 너무 심함
-> 로그 스케일 적용
#log 스케일을 통한 추가전처리
import numpy as np
rfm_df['Recency_log'] = np.log1p(rfm_df['Recency'])
rfm_df['Frequency_log'] = np.log1p(rfm_df['Frequency'])
rfm_df['Monetary_log'] = np.log1p(rfm_df['Monetary'])
X_features2 = rfm_df[['Recency_log','Frequency_log','Monetary_log']]
sc2 = StandardScaler()
X_features2_sc = sc2.fit_transform(X_features2)
visualize_silhouette([2,3,4,5,6], X_features2_sc)

실루엣 스코어는 줄었지만 군집화가 균일하게 잘 적용됨을 확인 할 수 있음.