063 클러스터링 실전 - 고객 세분화
키워드: 고객 세분화, segmentation
개요
고객 세분화는 클러스터링의 가장 대표적인 비즈니스 활용 사례입니다. 이 글에서는 PyCaret을 활용하여 고객을 세분화하고 마케팅 전략을 수립하는 실전 프로젝트를 진행합니다.
실습 환경
- Python 버전: 3.11 권장
- 필요 패키지:
pycaret[full]>=3.0
비즈니스 문제
목표: 고객을 유사한 특성을 가진 그룹으로 나누어 타겟 마케팅 수행 활용: 맞춤형 프로모션, 제품 추천, 고객 관리 전략
데이터 준비
import pandas as pd
import numpy as np
# 063 고객 데이터 시뮬레이션
np.random.seed(42)
n_customers = 2000
# 063 RFM 기반 데이터 생성
data = pd.DataFrame({
'customer_id': range(1, n_customers + 1),
# Recency: 마지막 구매 후 일수
'recency': np.random.exponential(30, n_customers).astype(int) + 1,
# Frequency: 구매 횟수
'frequency': np.random.poisson(5, n_customers) + 1,
# Monetary: 총 구매 금액
'monetary': np.random.exponential(500, n_customers) + 100,
# 추가 특성
'avg_order_value': np.random.uniform(20, 200, n_customers),
'days_since_first_purchase': np.random.randint(30, 730, n_customers),
'num_categories': np.random.randint(1, 8, n_customers),
'discount_sensitivity': np.random.uniform(0, 1, n_customers),
'return_rate': np.random.beta(2, 20, n_customers),
})
# 063 파생 변수
data['customer_lifetime_value'] = data['monetary'] * (1 + data['frequency'] * 0.1)
data['purchase_interval'] = data['days_since_first_purchase'] / data['frequency']
print(f"고객 수: {len(data)}")
print(f"특성 수: {len(data.columns) - 1}") # customer_id 제외
print(data.describe())
탐색적 데이터 분석
import matplotlib.pyplot as plt
import seaborn as sns
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 063 RFM 분포
axes[0, 0].hist(data['recency'], bins=30, edgecolor='black')
axes[0, 0].set_title('Recency Distribution')
axes[0, 0].set_xlabel('Days Since Last Purchase')
axes[0, 1].hist(data['frequency'], bins=20, edgecolor='black')
axes[0, 1].set_title('Frequency Distribution')
axes[0, 1].set_xlabel('Number of Purchases')
axes[0, 2].hist(data['monetary'], bins=30, edgecolor='black')
axes[0, 2].set_title('Monetary Distribution')
axes[0, 2].set_xlabel('Total Spending ($)')
# 063 관계 시각화
axes[1, 0].scatter(data['frequency'], data['monetary'], alpha=0.3)
axes[1, 0].set_xlabel('Frequency')
axes[1, 0].set_ylabel('Monetary')
axes[1, 0].set_title('Frequency vs Monetary')
axes[1, 1].scatter(data['recency'], data['frequency'], alpha=0.3)
axes[1, 1].set_xlabel('Recency')
axes[1, 1].set_ylabel('Frequency')
axes[1, 1].set_title('Recency vs Frequency')
# 063 상관관계
corr_cols = ['recency', 'frequency', 'monetary', 'avg_order_value', 'customer_lifetime_value']
corr = data[corr_cols].corr()
sns.heatmap(corr, annot=True, cmap='coolwarm', center=0, ax=axes[1, 2])
axes[1, 2].set_title('Correlation Matrix')
plt.tight_layout()
plt.savefig('customer_eda.png', dpi=150)
PyCaret 클러스터링 설정
from pycaret.clustering import *
# 063 분석용 특성 선택
features_for_clustering = [
'recency', 'frequency', 'monetary',
'avg_order_value', 'num_categories',
'discount_sensitivity', 'customer_lifetime_value'
]
clustering_data = data[features_for_clustering]
# 063 환경 설정
clust = setup(
data=clustering_data,
normalize=True,
session_id=42,
verbose=False
)
print("설정 완료!")
최적 클러스터 수 탐색
from sklearn.metrics import silhouette_score
import pandas as pd
X = get_config('X')
# 063 여러 K에 대해 평가
results = []
for k in range(2, 10):
kmeans = create_model('kmeans', num_clusters=k)
clustered = assign_model(kmeans)
labels = clustered['Cluster'].values
score = silhouette_score(X, labels)
results.append({'K': k, 'Silhouette': score})
df_results = pd.DataFrame(results)
print("K별 실루엣 점수:")
print(df_results)
optimal_k = df_results.loc[df_results['Silhouette'].idxmax(), 'K']
print(f"\n최적 K: {optimal_k}")
# 063 엘보우 플롯
kmeans_temp = create_model('kmeans', num_clusters=4)
plot_model(kmeans_temp, plot='elbow')
클러스터링 수행
from pycaret.clustering import *
# 063 K-Means 클러스터링
kmeans = create_model('kmeans', num_clusters=5)
# 063 클러스터 할당
clustered_data = assign_model(kmeans)
# 063 원본 데이터에 클러스터 추가
data['Cluster'] = clustered_data['Cluster']
print("클러스터별 고객 수:")
print(data['Cluster'].value_counts().sort_index())
클러스터 프로파일링
import pandas as pd
# 063 클러스터별 평균
cluster_profile = data.groupby('Cluster')[features_for_clustering].mean()
print("\n=== 클러스터 프로파일 ===\n")
print(cluster_profile.round(2))
# 063 각 클러스터 해석
print("\n=== 클러스터 해석 ===\n")
for cluster in sorted(data['Cluster'].unique()):
cluster_data = data[data['Cluster'] == cluster]
profile = cluster_data[features_for_clustering].mean()
print(f"클러스터 {cluster} (n={len(cluster_data)}, {len(cluster_data)/len(data)*100:.1f}%):")
# 특성 해석
if profile['recency'] < data['recency'].mean() and profile['frequency'] > data['frequency'].mean():
print(" → 활성 충성 고객")
elif profile['recency'] > data['recency'].mean() * 1.5:
print(" → 이탈 위험 고객")
elif profile['monetary'] > data['monetary'].mean() * 1.5:
print(" → 고가치 고객")
elif profile['discount_sensitivity'] > 0.7:
print(" → 가격 민감 고객")
else:
print(" → 일반 고객")
print(f" Recency: {profile['recency']:.0f}일, "
f"Frequency: {profile['frequency']:.1f}회, "
f"Monetary: ${profile['monetary']:.0f}")
print()
세그먼트 네이밍
# 063 비즈니스 의미 있는 세그먼트 이름 부여
def name_segment(row):
cluster = row['Cluster']
profile = data[data['Cluster'] == cluster][features_for_clustering].mean()
if profile['recency'] < 30 and profile['frequency'] > 8:
return 'Champions'
elif profile['monetary'] > 800:
return 'Big Spenders'
elif profile['recency'] > 60 and profile['frequency'] > 5:
return 'At Risk'
elif profile['recency'] > 90:
return 'Lost'
elif profile['discount_sensitivity'] > 0.6:
return 'Bargain Hunters'
else:
return 'Potential Loyalists'
# 063 실제로는 클러스터 프로파일 분석 후 수동으로 매핑
segment_mapping = {
'Cluster 0': 'Champions',
'Cluster 1': 'At Risk',
'Cluster 2': 'Big Spenders',
'Cluster 3': 'Bargain Hunters',
'Cluster 4': 'New Customers'
}
data['Segment'] = data['Cluster'].map(segment_mapping)
print("\n세그먼트별 고객 수:")
print(data['Segment'].value_counts())
시각화
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
# 063 PCA 2D 시각화
X = clustering_data.values
pca = PCA(n_components=2)
X_pca = pca.fit_transform(get_config('X'))
plt.figure(figsize=(12, 8))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1],
c=data['Cluster'], cmap='viridis',
alpha=0.6, s=30)
plt.colorbar(scatter, label='Cluster')
plt.xlabel(f'PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)')
plt.ylabel(f'PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)')
plt.title('Customer Segments (PCA)')
plt.savefig('customer_segments_pca.png', dpi=150)
레이더 차트로 프로파일 비교
import numpy as np
import matplotlib.pyplot as plt
# 063 정규화된 클러스터 평균
cluster_means = data.groupby('Cluster')[features_for_clustering].mean()
cluster_means_norm = (cluster_means - cluster_means.min()) / (cluster_means.max() - cluster_means.min())
# 063 레이더 차트
categories = features_for_clustering
N = len(categories)
angles = [n / float(N) * 2 * np.pi for n in range(N)]
angles += angles[:1]
fig, ax = plt.subplots(figsize=(10, 10), subplot_kw=dict(polar=True))
colors = plt.cm.tab10(np.linspace(0, 1, len(cluster_means)))
for idx, (cluster, row) in enumerate(cluster_means_norm.iterrows()):
values = row.values.tolist()
values += values[:1]
ax.plot(angles, values, 'o-', linewidth=2, label=f'Cluster {cluster}', color=colors[idx])
ax.fill(angles, values, alpha=0.1, color=colors[idx])
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories, size=10)
ax.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
plt.title('Customer Segment Profiles', size=14, y=1.08)
plt.tight_layout()
plt.savefig('customer_radar.png', dpi=150, bbox_inches='tight')
마케팅 전략 수립
print("\n=== 세그먼트별 마케팅 전략 ===\n")
strategies = {
'Champions': {
'strategy': '리워드 프로그램, VIP 혜택, 신제품 우선 접근',
'channel': '이메일, 앱 푸시',
'frequency': '주 1회',
'offer': '독점 할인, 멤버십 업그레이드'
},
'Big Spenders': {
'strategy': '프리미엄 상품 추천, 번들 할인',
'channel': '개인화 이메일, 전화',
'frequency': '월 2회',
'offer': '프리미엄 상품, 대량 구매 할인'
},
'At Risk': {
'strategy': '윈백 캠페인, 재구매 인센티브',
'channel': 'SMS, 이메일',
'frequency': '즉시 + 주 1회',
'offer': '특별 할인, 무료 배송'
},
'Bargain Hunters': {
'strategy': '세일 알림, 쿠폰 제공',
'channel': '앱 푸시, 이메일',
'frequency': '세일 시즌',
'offer': '할인 쿠폰, 번들 딜'
},
'New Customers': {
'strategy': '온보딩, 첫 구매 후 팔로업',
'channel': '이메일 시퀀스',
'frequency': '월 2회',
'offer': '환영 할인, 추천 상품'
}
}
for segment, strategy in strategies.items():
print(f"{segment}:")
print(f" 전략: {strategy['strategy']}")
print(f" 채널: {strategy['channel']}")
print(f" 빈도: {strategy['frequency']}")
print(f" 오퍼: {strategy['offer']}")
print()
비즈니스 영향 분석
# 063 세그먼트별 가치 분석
segment_value = data.groupby('Segment').agg({
'customer_id': 'count',
'monetary': 'sum',
'customer_lifetime_value': 'sum'
}).rename(columns={'customer_id': 'customer_count'})
segment_value['revenue_share'] = segment_value['monetary'] / segment_value['monetary'].sum() * 100
segment_value['avg_clv'] = segment_value['customer_lifetime_value'] / segment_value['customer_count']
print("\n=== 세그먼트별 비즈니스 가치 ===\n")
print(segment_value.round(2))
# 063 시각화
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 063 고객 수
segment_value['customer_count'].plot(kind='bar', ax=axes[0], color='steelblue')
axes[0].set_title('Customers by Segment')
axes[0].set_ylabel('Number of Customers')
axes[0].tick_params(axis='x', rotation=45)
# 063 매출 비중
segment_value['revenue_share'].plot(kind='pie', ax=axes[1], autopct='%1.1f%%')
axes[1].set_title('Revenue Share by Segment')
plt.tight_layout()
plt.savefig('segment_business_value.png', dpi=150)
모델 저장
# 063 클러스터링 모델 저장
save_model(kmeans, 'customer_segmentation_model')
print("\n모델 저장 완료: customer_segmentation_model.pkl")
# 063 세그먼트 결과 저장
data.to_csv('customer_segments.csv', index=False)
print("세그먼트 결과 저장: customer_segments.csv")
정리
- 고객 세분화는 클러스터링의 대표적 비즈니스 활용
- RFM (Recency, Frequency, Monetary)이 핵심 특성
- 클러스터 프로파일링으로 비즈니스 의미 해석
- 세그먼트별 맞춤 마케팅 전략 수립
- 정기적 재분석으로 세그먼트 업데이트
다음 글 예고
다음 글에서는 클러스터링 실전 - 문서 군집화를 다룹니다.
PyCaret 머신러닝 마스터 시리즈 #063