비지도 학습으로 고객 그룹 나누기
슈퍼스토어 마케팅 캠페인 데이터를 전처리하고, 클러스터링으로 비슷한 특성을 가진 고객끼리 묶어보는 실습이다. 레이블(정답) 없이 데이터만으로 그룹을 만드는 비지도 학습의 대표 예시인 K-Means를 사용하고, 엘보우 메서드·실루엣 스코어로 적절한 클러스터 개수를 정하는 방법까지 한 번에 다룬다.
(슈퍼스토어 마케팅 캠페인 데이터셋 정리를 기반으로 재구성)
1. 슈퍼스토어 마케팅 캠페인 데이터셋
1-1. 데이터셋 소개
슈퍼스토어 마케팅 캠페인 데이터셋은 고객의 인구통계학적 정보(출생 연도, 학력, 결혼 상태, 소득), 가구 구성(어린이·십대 자녀 수), 구매 기록(와인·육류·생선 등 제품군별 지출), 구매 채널(매장·웹·카탈로그), 마케팅 캠페인 참여·불만 여부 등을 담고 있다. 고객 행동 패턴과 마케팅 전략 효과 분석에 활용할 수 있다.
1-2. 컬럼 설명
| 컬럼 | 설명 |
|---|---|
| ID | 고객 고유 식별 번호 |
| Year_Birth | 출생 연도 (나이 계산 가능) |
| Complain | 지난 2년간 불만 제기 여부 (1: 있음, 0: 없음) |
| Dt_Customer | 거래 시작일 (충성도·거래 기간 분석용) |
| Education | 학력 (고졸, 대졸, 대학원 등) |
| Marital_Status | 결혼 상태 (Single, Married, Divorced 등) |
| Kidhome | 어린 자녀 수 |
| Teenhome | 십대 자녀 수 |
| Income | 연간 가계 소득 |
| MntWines, MntFruits, MntMeatProducts 등 | 지난 2년간 해당 제품군 지출액 |
| NumDealsPurchases | 할인으로 구매한 횟수 |
| NumCatalogPurchases | 카탈로그 구매 횟수 |
| NumStorePurchases | 매장 구매 횟수 |
| NumWebPurchases | 웹 구매 횟수 |
| NumWebVisitsMonth | 지난달 웹사이트 방문 횟수 |
1-3. 데이터 로드 및 기본 확인
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
mkt_df = pd.read_csv('/본인의 구글드라이브 경로/superstore_data.csv')
mkt_df.info()
mkt_df.head()
분석에 불필요한 고객 ID는 제거한다.
mkt_df.drop('Id', axis=1, inplace=True)
mkt_df.describe()
1-4. 이상치·결측 처리
소득이 비정상적으로 큰 값(예: 666666)은 이상치로 보고 제거하거나, 필요 시 상한을 두어 잘라준다.
mkt_df = mkt_df[mkt_df['Income'] != 666666]
mkt_df.sort_values('Income', ascending=False)
결측 비율 확인 후 결측이 있는 행을 제거한다.
mkt_df.isna().mean()
mkt_df = mkt_df.dropna()
mkt_df.isna().mean()
mkt_df.info()
1-5. 날짜·파생 변수
거래 시작일을 datetime으로 바꾼 뒤, 가입 후 경과 월 수를 만들어 활용한다.
mkt_df['Dt_Customer'] = pd.to_datetime(mkt_df['Dt_Customer'])
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.drop('Dt_Customer', axis=1, inplace=True)
mkt_df.head()
총 구매액·자녀 수 파생 변수:
mkt_df['Total_mnt'] = mkt_df[
['MntWines', 'MntFruits', 'MntMeatProducts', 'MntFishProducts',
'MntSweetProducts', 'MntGoldProds']
].sum(axis=1)
mkt_df['Children'] = mkt_df[['Kidhome', 'Teenhome']].sum(axis=1)
mkt_df.drop(['Kidhome', 'Teenhome'], axis=1, inplace=True)
mkt_df.head()
1-6. 범주형 정리 및 인코딩
결혼 상태에서 의미 없는 라벨(예: Absurd, YOLO)을 제거한 뒤, 유사한 상태를 묶어준다.
mkt_df['Marital_Status'].value_counts()
mkt_df = mkt_df[~mkt_df['Marital_Status'].isin(['Absurd', 'YOLO'])]
mkt_df['Marital_Status'] = mkt_df['Marital_Status'].replace({
'Married': 'Partner', 'Together': 'Partner',
'Single': 'Single', 'Divorced': 'Single', 'Widow': 'Single', 'Alone': 'Single'
})
mkt_df['Marital_Status'].value_counts()
범주형은 원-핫 인코딩으로 변환한다.
mkt_df = pd.get_dummies(mkt_df, columns=['Education', 'Marital_Status'])
mkt_df.head()
1-7. 스케일링
K-Means는 거리 기반이므로, 숫자형 변수는 표준화해 두는 것이 좋다.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
num_cols = [
'Income', 'Recency', 'MntWines', 'MntFruits', 'MntMeatProducts',
'MntFishProducts', 'MntSweetProducts', 'MntGoldProds',
'NumDealsPurchases', 'NumWebPurchases', 'NumCatalogPurchases',
'NumStorePurchases', 'NumWebVisitsMonth', 'Response',
'pass_month', 'Total_mnt', 'Children'
]
mkt_df[num_cols] = ss.fit_transform(mkt_df[num_cols])
mkt_df.head()
이제 이 mkt_df를 클러스터링에 사용할 수 있다.
2. 클러스터(Clusters)란?
클러스터는 비슷한 특성을 가진 데이터 포인트들을 묶은 그룹을 말한다. 클러스터링은 비지도 학습의 한 형태로, 정답 레이블 없이 데이터만으로 숨겨진 패턴이나 구조를 찾는 데 쓴다. 각 클러스터는 안쪽은 비슷하고, 다른 클러스터와는 구분되도록 나뉜다.
예를 들어 고객 데이터를 클러스터링하면, 구매 패턴·소득·관심사에 따라 고객 그룹을 나눌 수 있고, 타겟 마케팅·고객 세그먼테이션에 활용할 수 있다. 대표 알고리즘으로는 K-Means, 계층적 클러스터링, DBSCAN 등이 있다.
2-1. 간단한 2차원 예제 (make_blobs)
클러스터링 개념을 보기 위해, 사이킷런으로 인위적으로 그룹이 있는 2차원 데이터를 만든다.
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=100, centers=3, random_state=2026)
X
y
import pandas as pd
X = pd.DataFrame(X)
X
sns.scatterplot(x=X[0], y=X[1], hue=y)
여기서 y는 “정답” 레이블이다. 실제 클러스터링에서는 이 레이블 없이 X만 가지고 그룹을 찾는다.
3. K-Means
K-Means는 데이터를 미리 정한 개수(K)의 클러스터로 나누는 비지도 학습 알고리즘이다. 동작은 대략 다음과 같다.
- 데이터 안에서 K개의 초기 중심(centroid) 을 랜덤하게 선택한다.
- 각 데이터 포인트를 가장 가까운 중심에 할당해 클러스터를 만든다.
- 각 클러스터의 평균을 구해 중심을 새로 갱신한다.
- 중심이 거의 변하지 않을 때까지 2–3을 반복한다.
장점은 구현이 단순하고 대규모 데이터에서도 빠르게 돌아간다는 것이고, 단점은 K를 미리 정해야 한다는 것, 그리고 데이터가 구형에 가깝지 않거나 잡음이 많으면 성능이 떨어질 수 있다는 것이다. 고객 세그먼트화, 이미지 압축, 패턴 인식 등에 널리 쓰인다.
3-1. 간단한 예제 (make_blobs)
위에서 만든 X에 K-Means를 적용해 본다.
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)
클러스터 수를 5로 바꾸면 그에 맞게 나뉜다.
km = KMeans(n_clusters=5)
km.fit(X)
pred = km.predict(X)
sns.scatterplot(x=X[0], y=X[1], hue=pred)
※ Inertia (관성)
inertia_는 K-Means가 계산하는 관성 값이다. 각 클러스터의 중심(centroid)과 그 클러스터에 속한 점들 사이의 거리 제곱합(SSD) 이다.
- 클러스터 안의 점들이 중심에 얼마나 모여 있는지를 나타낸다.
- 값이 작을수록 클러스터가 더 잘 뭉쳐 있다고 해석할 수 있다.
- 다만 K가 커질수록 관성은 계속 줄어들기 때문에, K를 정할 때만 쓰기보다는 실루엣 스코어 등과 함께 보는 것이 좋다.
km = KMeans(n_clusters=3)
km.fit(X)
km.inertia_
3-2. 엘보우 메서드 (Elbow Method)
엘보우 메서드는 K를 2, 3, 4, … 처럼 바꿔 가며 관성 값을 구하고, 감소율이 꺾이는 지점(엘보우 포인트) 을 최적의 클러스터 개수 후보로 보는 방법이다.
- K가 커질수록 관성은 줄어든다.
- 어느 K부터는 감소폭이 작아진다. 이 꺾이는 부분이 엘보우 포인트다.
make_blobs 예제에서 K=2~10으로 관성을 그려 본다.
inertia_list = []
for i in range(2, 11):
km = KMeans(n_clusters=i)
km.fit(X)
inertia_list.append(km.inertia_)
inertia_list
sns.lineplot(x=range(2, 11), y=inertia_list)
3-3. 실루엣 스코어 (Silhouette Score)
실루엣 스코어는 클러스터링 품질을 -1~1 사이로 나타내는 지표다. 같은 클러스터 안에서는 가깝고, 다른 클러스터와는 멀수록 점수가 높다. 1에 가까우면 그룹 구분이 잘 된 것이고, 0에 가깝거나 음수면 그룹이 애매하거나 잘못 묶인 경우에 가깝다.
K=2~10으로 실루엣 스코어를 계산해 보면, 관성과 함께 K 선택에 참고할 수 있다.
from sklearn.metrics import silhouette_score
score = []
for i in range(2, 11):
km = KMeans(n_clusters=i)
km.fit(X)
pred = km.predict(X)
score.append(silhouette_score(X, pred))
score
sns.lineplot(x=range(2, 11), y=score)
4. 슈퍼스토어 데이터에 K-Means 적용
전처리된 마케팅 데이터 mkt_df에 K-Means를 적용해 고객 세그먼트를 만든다.
4-1. 엘보우·실루엣으로 K 선택
관성과 실루엣을 모두 그려 보고, 엘보우와 실루엣이 높은 K를 고른다.
inertia_list = []
for i in range(2, 11):
km = KMeans(n_clusters=i, random_state=2025)
km.fit(mkt_df)
inertia_list.append(km.inertia_)
sns.lineplot(x=range(2, 11), y=inertia_list)
score = []
for i in range(2, 11):
km = KMeans(n_clusters=i, random_state=2025)
km.fit(mkt_df)
pred = km.predict(mkt_df)
score.append(silhouette_score(mkt_df, pred))
sns.lineplot(x=range(2, 11), y=score)
4-2. 클러스터 레이블 부여
엘보우와 실루엣을 참고해 K를 정한 뒤(예: 3), 모델을 학습하고 각 고객에게 클러스터 번호를 붙인다.
km = KMeans(n_clusters=3, random_state=2025)
km.fit(mkt_df)
pred = km.predict(mkt_df)
pred
mkt_df['label'] = pred
mkt_df.head()
mkt_df['label'].value_counts()
이제 label별로 평균 소득, 구매액, 채널 이용 패턴 등을 비교하면, “고소득·와인 구매 많음”, “할인 민감·웹 방문 많음” 같은 세그먼트 해석이 가능하다. 이 그룹별로 다른 마케팅 메시지나 프로모션을 기획할 수 있다.
마치며
- 슈퍼스토어 마케팅 데이터를 정제·파생 변수·스케일링까지 해 두면, K-Means로 고객 세그먼트를 만들 수 있다.
- 클러스터 개수 K는 엘보우 메서드(관성) 와 실루엣 스코어를 함께 보면서 정하는 것이 좋다.
- K-Means는 비지도 학습의 기본이므로, 계층적 클러스터링·DBSCAN 등과 비교해 보면 도움이 된다.
(슈퍼스토어 마케팅 캠페인 데이터셋 · K-Means, 엘보우, 실루엣 정리 참고)
'AI·머신러닝 > 딥러닝·비전' 카테고리의 다른 글
| Multi-class Weather 이미지 분류 실습 - ImageFolder, DataLoader, 완전연결 신경망 (0) | 2025.12.22 |
|---|---|
| 논리 회귀부터 손글씨 숫자·퍼셉트론까지 - 분류, DataLoader, 데이터 증강, MLP 실습 (0) | 2025.12.20 |
| 파이토치로 시작하는 딥러닝 - 텐서, 선형 회귀, 논리 회귀 실습 (0) | 2025.12.20 |
| 서울 자전거 · 호텔 예약 수요 예측 실습 - 결정 트리, 랜덤 포레스트, 로지스틱 회귀, 교차 검증 (0) | 2025.12.16 |
| 머신러닝 완전 정복 - 기초부터 실전까지, 사이킷런으로 시작하기 (0) | 2025.12.12 |