수민 '-'

플오그래밍

제가 작성하는 모든 글은 절대 상업적인 이용이 아니며, 그저 개인적인 공부 용도로만 사용하는 것임을 밝힙니다.

슈퍼스토어 마케팅 캠페인과 K-Means 클러스터링 - 고객 세그먼트 실습

비지도 학습으로 고객 그룹 나누기

슈퍼스토어 마케팅 캠페인 데이터를 전처리하고, 클러스터링으로 비슷한 특성을 가진 고객끼리 묶어보는 실습이다. 레이블(정답) 없이 데이터만으로 그룹을 만드는 비지도 학습의 대표 예시인 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)의 클러스터로 나누는 비지도 학습 알고리즘이다. 동작은 대략 다음과 같다.

  1. 데이터 안에서 K개의 초기 중심(centroid) 을 랜덤하게 선택한다.
  2. 각 데이터 포인트를 가장 가까운 중심에 할당해 클러스터를 만든다.
  3. 각 클러스터의 평균을 구해 중심을 새로 갱신한다.
  4. 중심이 거의 변하지 않을 때까지 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, 엘보우, 실루엣 정리 참고)