이커머스 분석
이커머스 데이터 분석은 비즈니스 의사결정에 직접 연결되는 실무형 프로젝트입니다. 고객 행동 패턴을 분석하고, 매출 트렌드를 파악하며, RFM(Recency, Frequency, Monetary) 분석으로 고객을 세그먼트합니다.학습 목표
- 이커머스 거래 데이터의 품질을 점검하고 정제할 수 있다
- 매출 트렌드와 시간 패턴을 분석할 수 있다
- RFM 분석으로 고객을 세그먼트할 수 있다
- 비즈니스 인사이트를 도출하고 보고할 수 있다
왜 중요한가
이커머스 분석은 데이터 분석가의 가장 흔한 업무 중 하나입니다. 매출 트렌드, 고객 세그먼트, 구매 패턴 분석은 마케팅 전략, 재고 관리, 고객 유지 정책의 근거가 됩니다.Step 1: 데이터 생성 및 로드
Copy
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
np.random.seed(42)
# 이커머스 거래 데이터 생성
n_customers = 500
n_orders = 3000
customers = np.random.randint(1000, 1000 + n_customers, n_orders)
dates = pd.date_range('2024-01-01', '2024-12-31', periods=n_orders)
products = np.random.choice(
['전자제품', '의류', '식품', '도서', '화장품', '가전', '스포츠'], n_orders,
p=[0.15, 0.20, 0.25, 0.10, 0.12, 0.08, 0.10]
)
quantities = np.random.randint(1, 10, n_orders)
unit_prices = np.random.choice(
[5000, 10000, 15000, 25000, 50000, 80000, 120000], n_orders,
p=[0.15, 0.20, 0.25, 0.15, 0.12, 0.08, 0.05]
)
df = pd.DataFrame({
'order_id': range(1, n_orders + 1),
'customer_id': customers,
'order_date': dates,
'product_category': products,
'quantity': quantities,
'unit_price': unit_prices
})
df['total_amount'] = df['quantity'] * df['unit_price']
# 일부 결측치와 이상 데이터 삽입
df.loc[np.random.choice(n_orders, 50), 'quantity'] = 0
df.loc[np.random.choice(n_orders, 20), 'total_amount'] = -df.loc[
np.random.choice(n_orders, 20), 'total_amount'].abs() # 반품
print(f"데이터 크기: {df.shape}")
print(df.head())
print(f"\n기간: {df['order_date'].min()} ~ {df['order_date'].max()}")
Step 2: 데이터 정제
Copy
# 품질 점검
print("결측치:", df.isnull().sum().sum())
print(f"수량 0 이하: {(df['quantity'] <= 0).sum()}건")
print(f"금액 음수 (반품): {(df['total_amount'] < 0).sum()}건")
# 반품 분리
returns = df[df['total_amount'] < 0].copy()
df_clean = df[df['total_amount'] > 0].copy()
df_clean = df_clean[df_clean['quantity'] > 0]
print(f"\n정제 후: {len(df_clean)}건 (반품 {len(returns)}건 제외)")
print(f"총 매출: {df_clean['total_amount'].sum():,.0f}원")
print(f"고유 고객: {df_clean['customer_id'].nunique()}명")
Step 3: 매출 트렌드 분석
Copy
# 월별 매출 트렌드
df_clean['month'] = df_clean['order_date'].dt.to_period('M')
monthly = df_clean.groupby('month').agg(
매출=('total_amount', 'sum'),
주문수=('order_id', 'count'),
고객수=('customer_id', 'nunique')
).reset_index()
monthly['month'] = monthly['month'].astype(str)
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].plot(range(len(monthly)), monthly['매출'] / 1e6, 'o-', color='#4a9eca')
axes[0].set_title('월별 매출 추이')
axes[0].set_ylabel('매출 (백만원)')
axes[0].set_xticks(range(0, len(monthly), 2))
axes[0].set_xticklabels(monthly['month'].iloc[::2], rotation=45)
axes[1].bar(range(len(monthly)), monthly['주문수'], color='#4a9e4a', alpha=0.7)
axes[1].set_title('월별 주문 수')
axes[1].set_xticks(range(0, len(monthly), 2))
axes[1].set_xticklabels(monthly['month'].iloc[::2], rotation=45)
axes[2].plot(range(len(monthly)), monthly['고객수'], 's-', color='#e6a23c')
axes[2].set_title('월별 고유 고객 수')
axes[2].set_xticks(range(0, len(monthly), 2))
axes[2].set_xticklabels(monthly['month'].iloc[::2], rotation=45)
plt.tight_layout()
plt.show()
요일/시간 패턴
Copy
# 요일별 주문 패턴
df_clean['day_of_week'] = df_clean['order_date'].dt.day_name()
day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
day_stats = df_clean.groupby('day_of_week')['total_amount'].agg(['sum', 'count'])
day_stats = day_stats.reindex(day_order)
fig, ax = plt.subplots(figsize=(8, 5))
ax.bar(range(7), day_stats['sum'] / 1e6, color='#4a9eca', alpha=0.7)
ax.set_xticks(range(7))
ax.set_xticklabels(['월', '화', '수', '목', '금', '토', '일'])
ax.set_title('요일별 매출')
ax.set_ylabel('매출 (백만원)')
plt.tight_layout()
plt.show()
Step 4: 상품 카테고리 분석
Copy
# 카테고리별 매출과 주문
category_stats = df_clean.groupby('product_category').agg(
매출=('total_amount', 'sum'),
주문수=('order_id', 'count'),
평균단가=('unit_price', 'mean'),
평균수량=('quantity', 'mean')
).sort_values('매출', ascending=False)
print("카테고리별 실적:")
print(category_stats.round(0))
# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 매출 비중 파이차트
axes[0].pie(category_stats['매출'], labels=category_stats.index,
autopct='%1.1f%%', colors=sns.color_palette('Set2'))
axes[0].set_title('카테고리별 매출 비중')
# 카테고리별 평균 주문 금액
avg_order = df_clean.groupby('product_category')['total_amount'].mean().sort_values()
avg_order.plot(kind='barh', ax=axes[1], color='#4a9eca')
axes[1].set_title('카테고리별 평균 주문 금액')
axes[1].set_xlabel('평균 금액 (원)')
plt.tight_layout()
plt.show()
Step 5: RFM 분석
RFM 분석은 Recency(최근성), Frequency(빈도), Monetary(금액)로 고객을 세그먼트합니다.Copy
# 기준일 설정
reference_date = df_clean['order_date'].max() + pd.Timedelta(days=1)
# RFM 계산
rfm = df_clean.groupby('customer_id').agg(
Recency=('order_date', lambda x: (reference_date - x.max()).days),
Frequency=('order_id', 'count'),
Monetary=('total_amount', 'sum')
).reset_index()
print("RFM 요약:")
print(rfm.describe().round(1))
# RFM 점수 (4분위)
rfm['R_score'] = pd.qcut(rfm['Recency'], 4, labels=[4, 3, 2, 1]) # 최근일수록 높은 점수
rfm['F_score'] = pd.qcut(rfm['Frequency'].rank(method='first'), 4, labels=[1, 2, 3, 4])
rfm['M_score'] = pd.qcut(rfm['Monetary'].rank(method='first'), 4, labels=[1, 2, 3, 4])
rfm['RFM_score'] = rfm['R_score'].astype(str) + rfm['F_score'].astype(str) + rfm['M_score'].astype(str)
rfm['RFM_total'] = rfm[['R_score', 'F_score', 'M_score']].astype(int).sum(axis=1)
print("\nRFM 점수 분포:")
print(rfm[['R_score', 'F_score', 'M_score', 'RFM_total']].describe())
고객 세그먼트 정의
Copy
def segment_customer(row):
"""RFM 점수로 고객 세그먼트를 분류합니다."""
r, f, m = int(row['R_score']), int(row['F_score']), int(row['M_score'])
if r >= 3 and f >= 3 and m >= 3:
return 'VIP'
elif r >= 3 and f >= 2:
return '충성 고객'
elif r >= 3 and f == 1:
return '신규 고객'
elif r <= 2 and f >= 3:
return '이탈 위험'
elif r <= 2 and f <= 2 and m >= 3:
return '고가 이탈 위험'
else:
return '일반'
rfm['segment'] = rfm.apply(segment_customer, axis=1)
# 세그먼트별 통계
segment_stats = rfm.groupby('segment').agg(
고객수=('customer_id', 'count'),
평균_Recency=('Recency', 'mean'),
평균_Frequency=('Frequency', 'mean'),
평균_Monetary=('Monetary', 'mean')
).round(0)
segment_stats['비율(%)'] = (segment_stats['고객수'] / len(rfm) * 100).round(1)
print("고객 세그먼트:")
print(segment_stats.sort_values('평균_Monetary', ascending=False))
# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 세그먼트별 고객 수
segment_stats['고객수'].sort_values().plot(kind='barh', ax=axes[0], color='#4a9eca')
axes[0].set_title('세그먼트별 고객 수')
# 세그먼트별 평균 매출
segment_stats['평균_Monetary'].sort_values().plot(kind='barh', ax=axes[1], color='#4a9e4a')
axes[1].set_title('세그먼트별 평균 매출')
axes[1].set_xlabel('평균 매출 (원)')
plt.tight_layout()
plt.show()
Step 6: 인사이트와 제안
Copy
insights = """
## 이커머스 분석 인사이트
### 매출 트렌드
- 월별 매출이 꾸준한 성장세
- 주말 매출이 평일 대비 약 20% 높음
### 상품 카테고리
- 의류와 식품이 전체 매출의 45% 차지
- 전자제품: 주문 수는 적으나 평균 주문 금액이 가장 높음
### 고객 세그먼트 (RFM)
- VIP 고객(상위 15%)이 전체 매출의 40% 이상 기여
- 이탈 위험 고객에 대한 리텐션 마케팅 필요
- 신규 고객의 재구매 전환율 개선 여지 있음
### 비즈니스 제안
1. VIP 고객 대상 로열티 프로그램 운영
2. 이탈 위험 고객에게 재구매 쿠폰 발송
3. 주말 타겟 프로모션 강화
4. 전자제품 카테고리 마진 분석 필요
"""
print(insights)
ML로 확장하기
- 고객 이탈 예측: RFM 피처를 사용한 이진 분류 모델
- 고객 세그먼트: K-Means 클러스터링으로 자동 세그먼트
- 매출 예측: 시계열 모델로 월별 매출 예측
- 상품 추천: 구매 패턴 기반 협업 필터링
RFM 분석의 한계는?
RFM 분석의 한계는?
RFM은 과거 행동만 반영하므로, 신규 고객이나 장기 이탈 고객의 잠재 가치를 평가하기 어렵습니다. 또한 세그먼트 기준(4분위)이 임의적입니다. ML 기반 CLV(Customer Lifetime Value) 예측이 더 정교한 대안입니다.
체크리스트
- 이커머스 데이터를 정제하고 반품을 분리할 수 있다
- 매출 트렌드와 시간 패턴을 분석할 수 있다
- RFM 분석으로 고객을 세그먼트할 수 있다
- 분석 결과를 비즈니스 제안으로 연결할 수 있다

