분석 기법 개요¶
비즈니스 의사결정을 지원하는 핵심 데이터 분석 기법들을 정리함. 각 기법의 목적, 방법론, 실무 적용 방법을 다룬다.
분석 프레임워크¶
문제 해결 접근법¶
1. 문제 정의 (Problem Definition)
└─ 비즈니스 질문을 데이터 질문으로 변환
└─ 성공 지표 정의
2. 가설 수립 (Hypothesis)
└─ 검증 가능한 형태로 구체화
└─ 예: "30일 이내 재구매 고객의 LTV가 2배 높다"
3. 데이터 수집 및 탐색 (Data Collection & EDA)
└─ 필요한 데이터 소스 식별
└─ 데이터 품질 검증
4. 분석 실행 (Analysis)
└─ 적절한 기법 선택 및 적용
└─ 결과 검증
5. 인사이트 도출 (Insight)
└─ "So What?" - 비즈니스 의미 해석
└─ 액션 가능한 결론
6. 의사결정 및 실행 (Action)
└─ 구체적인 비즈니스 액션 제안
└─ 효과 측정 계획
고객 분석 (Customer Analytics)¶
RFM 분석¶
고객을 최근성(Recency), 구매빈도(Frequency), 구매금액(Monetary)으로 세분화하는 기법.
목적: 고객 세그먼테이션, 마케팅 타겟팅, 이탈 예측
RFM 정의:
| 지표 | 정의 | 높은 값의 의미 |
|---|---|---|
| Recency | 마지막 구매로부터 경과 일수 | 최근 활동 (좋음) |
| Frequency | 일정 기간 구매 횟수 | 충성 고객 |
| Monetary | 일정 기간 총 구매 금액 | 고가치 고객 |
점수화 방법:
import pandas as pd
import numpy as np
from datetime import datetime
def rfm_analysis(df, customer_id, order_date, amount, analysis_date=None):
"""
RFM 분석 수행
Parameters:
-----------
df : DataFrame with transaction data
customer_id : 고객 ID 컬럼명
order_date : 주문 날짜 컬럼명
amount : 구매 금액 컬럼명
analysis_date : 분석 기준일 (None이면 max date 사용)
"""
if analysis_date is None:
analysis_date = df[order_date].max() + pd.Timedelta(days=1)
# RFM 계산
rfm = df.groupby(customer_id).agg({
order_date: lambda x: (analysis_date - x.max()).days, # Recency
customer_id: 'count', # Frequency (using customer_id as proxy)
amount: 'sum' # Monetary
}).rename(columns={
order_date: 'Recency',
customer_id: 'Frequency',
amount: 'Monetary'
})
# Frequency 재계산 (더 정확한 방법)
rfm['Frequency'] = df.groupby(customer_id)[order_date].nunique().values
# Quintile 점수화 (1-5)
rfm['R_Score'] = pd.qcut(rfm['Recency'], 5, labels=[5,4,3,2,1]) # 낮을수록 좋음
rfm['F_Score'] = pd.qcut(rfm['Frequency'].rank(method='first'), 5, labels=[1,2,3,4,5])
rfm['M_Score'] = pd.qcut(rfm['Monetary'].rank(method='first'), 5, labels=[1,2,3,4,5])
rfm['RFM_Score'] = rfm['R_Score'].astype(str) + rfm['F_Score'].astype(str) + rfm['M_Score'].astype(str)
return rfm
def rfm_segment(rfm_df):
"""RFM 점수 기반 고객 세그먼트 분류"""
def segment_customer(row):
r, f, m = int(row['R_Score']), int(row['F_Score']), int(row['M_Score'])
if r >= 4 and f >= 4:
return 'Champions'
elif r >= 4 and f >= 2:
return 'Loyal Customers'
elif r >= 3 and f >= 3:
return 'Potential Loyalists'
elif r >= 4 and f == 1:
return 'Recent Customers'
elif r >= 3 and f == 1:
return 'Promising'
elif r == 2 and f >= 2:
return 'Customers Needing Attention'
elif r == 2 and f == 1:
return 'About to Sleep'
elif r == 1 and f >= 2:
return 'At Risk'
elif r == 1 and f == 1 and m >= 3:
return 'Cant Lose Them'
elif r == 1 and f == 1:
return 'Hibernating'
else:
return 'Others'
rfm_df['Segment'] = rfm_df.apply(segment_customer, axis=1)
return rfm_df
세그먼트별 전략:
| 세그먼트 | 특성 | 마케팅 전략 |
|---|---|---|
| Champions | 최근, 자주, 많이 구매 | 로열티 프로그램, VIP 혜택 |
| Loyal Customers | 자주 구매 | 업셀링, 리뷰 요청 |
| Potential Loyalists | 최근 구매, 중간 빈도 | 멤버십 가입 유도 |
| Recent Customers | 최근 첫 구매 | 온보딩, 두 번째 구매 유도 |
| At Risk | 과거 우수 고객, 최근 비활성 | 재활성화 캠페인, 윈백 |
| Hibernating | 오래 전 구매, 저빈도 | 할인 쿠폰, 재인게이지 |
참고 논문: - Hughes, A.M. (1994). "Strategic Database Marketing". Probus Publishing. - Fader, P.S. et al. (2005). "RFM and CLV: Using Iso-Value Curves for Customer Base Analysis". JMR.
상세: rfm.md
코호트 분석 (Cohort Analysis)¶
공통 특성(주로 가입/첫 구매 시점)을 가진 고객 그룹의 행동을 시간에 따라 추적하는 기법.
목적: 리텐션 패턴 파악, 제품/서비스 변화 영향 평가, 고객 생애주기 이해
리텐션 코호트 테이블:
Month 0 Month 1 Month 2 Month 3 Month 4
Jan-24 100% 45% 32% 28% 25%
Feb-24 100% 48% 35% 30%
Mar-24 100% 50% 38%
Apr-24 100% 52%
May-24 100%
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
def cohort_analysis(df, user_id, event_date, event_type=None):
"""
코호트 리텐션 분석
Parameters:
-----------
df : DataFrame with event data
user_id : 사용자 ID 컬럼명
event_date : 이벤트 날짜 컬럼명
event_type : 특정 이벤트 필터 (선택)
"""
df = df.copy()
df[event_date] = pd.to_datetime(df[event_date])
# 코호트 정의 (첫 활동 월)
df['CohortMonth'] = df.groupby(user_id)[event_date].transform('min').dt.to_period('M')
df['EventMonth'] = df[event_date].dt.to_period('M')
# 코호트 인덱스 (가입 후 경과 월)
df['CohortIndex'] = (df['EventMonth'] - df['CohortMonth']).apply(lambda x: x.n if pd.notna(x) else 0)
# 코호트별, 월별 고유 사용자 수
cohort_data = df.groupby(['CohortMonth', 'CohortIndex'])[user_id].nunique().reset_index()
cohort_pivot = cohort_data.pivot(index='CohortMonth', columns='CohortIndex', values=user_id)
# 리텐션 비율 계산
cohort_size = cohort_pivot.iloc[:, 0]
retention_matrix = cohort_pivot.divide(cohort_size, axis=0) * 100
return retention_matrix, cohort_size
def plot_cohort_heatmap(retention_matrix, title='Retention by Cohort'):
"""코호트 히트맵 시각화"""
plt.figure(figsize=(12, 8))
sns.heatmap(
retention_matrix,
annot=True,
fmt='.0f',
cmap='YlGnBu',
vmin=0,
vmax=100
)
plt.title(title)
plt.xlabel('Months Since First Activity')
plt.ylabel('Cohort Month')
plt.tight_layout()
return plt.gcf()
분석 포인트:
- 수평 분석: 특정 코호트의 시간별 리텐션 추이
- 수직 분석: 같은 기간(Month 1 등)에서 코호트 간 비교
- 대각선 분석: 같은 시점에서의 모든 코호트 상태
참고 논문: - Ascarza, E. et al. (2018). "In Pursuit of Enhanced Customer Retention Management". Customer Needs and Solutions.
상세: cohort.md
LTV 분석 (Lifetime Value)¶
고객이 전체 관계 기간 동안 창출하는 총 가치를 추정하는 기법.
목적: CAC 대비 효율성 평가, 마케팅 예산 책정, 고객 세그먼트 가치 비교
기본 공식 (구독 모델):
할인율 적용:
여기서: - \(m\): 기간당 마진 - \(r\): 리텐션율 - \(d\): 할인율 - \(T\): 예측 기간
확률적 모델 (BG/NBD + Gamma-Gamma):
from lifetimes import BetaGeoFitter, GammaGammaFitter
from lifetimes.utils import summary_data_from_transaction_data
def calculate_clv(transactions, customer_id, order_date, amount,
observation_period_end=None, discount_rate=0.01):
"""
BG/NBD + Gamma-Gamma 모델 기반 CLV 계산
"""
# Summary 데이터 생성
summary = summary_data_from_transaction_data(
transactions,
customer_id,
order_date,
monetary_value_col=amount,
observation_period_end=observation_period_end
)
# 구매 횟수 > 0인 고객만 (모델 요구사항)
summary = summary[summary['frequency'] > 0]
# BG/NBD 모델 (구매 빈도 예측)
bgf = BetaGeoFitter(penalizer_coef=0.01)
bgf.fit(summary['frequency'], summary['recency'], summary['T'])
# Gamma-Gamma 모델 (평균 주문 금액 예측)
ggf = GammaGammaFitter(penalizer_coef=0.01)
ggf.fit(summary['frequency'], summary['monetary_value'])
# 향후 12개월 CLV 예측
clv = ggf.customer_lifetime_value(
bgf,
summary['frequency'],
summary['recency'],
summary['T'],
summary['monetary_value'],
time=12, # months
discount_rate=discount_rate
)
summary['predicted_clv'] = clv
return summary, bgf, ggf
def clv_segments(clv_data, n_segments=4):
"""CLV 기반 고객 세그먼트"""
clv_data['clv_segment'] = pd.qcut(
clv_data['predicted_clv'],
q=n_segments,
labels=['Low', 'Medium', 'High', 'Premium']
)
segment_summary = clv_data.groupby('clv_segment').agg({
'predicted_clv': ['mean', 'median', 'count'],
'frequency': 'mean',
'monetary_value': 'mean'
})
return clv_data, segment_summary
LTV:CAC 비율:
| 비율 | 해석 |
|---|---|
| < 1:1 | 손실 - 즉시 개선 필요 |
| 1:1 - 3:1 | 비효율적 - 개선 필요 |
| 3:1 - 5:1 | 건강한 상태 |
| > 5:1 | 우수 - 성장 투자 여력 |
참고 논문: - Fader, P.S. et al. (2005). "Counting Your Customers the Easy Way: An Alternative to the Pareto/NBD Model". Marketing Science. - Fader, P.S. & Hardie, B.G.S. (2007). "How to Project Customer Retention". Journal of Interactive Marketing.
상세: ltv.md
행동 분석 (Behavioral Analytics)¶
퍼널 분석 (Funnel Analysis)¶
사용자 여정의 단계별 전환율을 측정하여 이탈 구간을 식별하는 기법.
목적: 전환 병목 지점 파악, UX 개선 우선순위 결정, 마케팅 최적화
일반적인 퍼널 단계:
import pandas as pd
import plotly.graph_objects as go
def funnel_analysis(events_df, user_id, event_name, event_timestamp,
funnel_steps, window_days=30):
"""
퍼널 분석 수행
Parameters:
-----------
events_df : 이벤트 데이터
funnel_steps : 퍼널 단계 리스트 (순서대로)
window_days : 퍼널 완료 제한 시간 (일)
"""
results = []
# 첫 단계 사용자
step1_users = set(events_df[events_df[event_name] == funnel_steps[0]][user_id])
for i, step in enumerate(funnel_steps):
if i == 0:
step_users = step1_users
else:
# 이전 단계 통과자 중 현재 단계 도달자
prev_step = funnel_steps[i-1]
# 각 사용자별로 순차적 이벤트 확인
step_users = set()
for user in results[i-1]['users']:
user_events = events_df[
(events_df[user_id] == user)
].sort_values(event_timestamp)
# 이전 단계 시점 찾기
prev_time = user_events[user_events[event_name] == prev_step][event_timestamp].min()
# 현재 단계 도달 여부 (이전 단계 이후)
next_events = user_events[
(user_events[event_timestamp] > prev_time) &
(user_events[event_name] == step) &
(user_events[event_timestamp] <= prev_time + pd.Timedelta(days=window_days))
]
if len(next_events) > 0:
step_users.add(user)
results.append({
'step': step,
'users': step_users,
'count': len(step_users),
'conversion_from_prev': len(step_users) / results[i-1]['count'] if i > 0 else 1.0,
'conversion_from_top': len(step_users) / len(step1_users)
})
return pd.DataFrame(results)
def plot_funnel(funnel_df):
"""퍼널 시각화"""
fig = go.Figure(go.Funnel(
y=funnel_df['step'],
x=funnel_df['count'],
textinfo="value+percent initial",
marker=dict(color=["#3366CC", "#DC3912", "#FF9900", "#109618", "#990099"][:len(funnel_df)])
))
fig.update_layout(title="Conversion Funnel", funnelmode="stack")
return fig
분석 포인트:
- Drop-off 분석: 가장 큰 이탈이 발생하는 단계
- 세그먼트별 비교: 신규/기존, 채널별, 디바이스별
- 시간 추이: 퍼널 전환율의 변화
- Time to Convert: 각 단계 간 소요 시간
상세: funnel.md
A/B 테스트 (Experimentation)¶
두 개 이상의 변형을 비교하여 인과적 효과를 검증하는 기법.
목적: 기능/UI 변경의 효과 검증, 가설 테스트, 데이터 기반 의사결정
통계적 프레임워크:
| 개념 | 정의 | 일반적 기준 |
|---|---|---|
| 유의수준 (\(\alpha\)) | Type I Error (False Positive) | 0.05 (5%) |
| 검정력 (\(1-\beta\)) | Type II Error를 피할 확률 | 0.80 (80%) |
| MDE | 탐지하고자 하는 최소 효과 크기 | 비즈니스 기준 |
샘플 크기 계산:
from scipy import stats
import numpy as np
def calculate_sample_size(baseline_rate, mde, alpha=0.05, power=0.80):
"""
이항 지표(전환율 등)에 대한 필요 샘플 크기 계산
Parameters:
-----------
baseline_rate : 기존 전환율
mde : 최소 탐지 효과 (절대값)
"""
p1 = baseline_rate
p2 = baseline_rate + mde
pooled_p = (p1 + p2) / 2
pooled_se = np.sqrt(2 * pooled_p * (1 - pooled_p))
z_alpha = stats.norm.ppf(1 - alpha/2)
z_beta = stats.norm.ppf(power)
n = ((z_alpha * pooled_se + z_beta * np.sqrt(p1*(1-p1) + p2*(1-p2))) / mde) ** 2
return int(np.ceil(n))
def ab_test_analysis(control, treatment, metric_type='binary', alpha=0.05):
"""
A/B 테스트 결과 분석
Parameters:
-----------
control : 대조군 데이터 (binary: 0/1 배열, continuous: 값 배열)
treatment : 처리군 데이터
metric_type : 'binary' (전환율) 또는 'continuous' (평균)
"""
n_c, n_t = len(control), len(treatment)
if metric_type == 'binary':
# 비율 검정
p_c = control.mean()
p_t = treatment.mean()
pooled_p = (control.sum() + treatment.sum()) / (n_c + n_t)
se = np.sqrt(pooled_p * (1 - pooled_p) * (1/n_c + 1/n_t))
z_stat = (p_t - p_c) / se
p_value = 2 * (1 - stats.norm.cdf(abs(z_stat)))
# 신뢰구간
se_diff = np.sqrt(p_c*(1-p_c)/n_c + p_t*(1-p_t)/n_t)
ci = (p_t - p_c - 1.96*se_diff, p_t - p_c + 1.96*se_diff)
lift = (p_t - p_c) / p_c * 100
else:
# t-검정 (Welch's)
mean_c, mean_t = control.mean(), treatment.mean()
var_c, var_t = control.var(ddof=1), treatment.var(ddof=1)
se = np.sqrt(var_c/n_c + var_t/n_t)
t_stat = (mean_t - mean_c) / se
df = (var_c/n_c + var_t/n_t)**2 / ((var_c/n_c)**2/(n_c-1) + (var_t/n_t)**2/(n_t-1))
p_value = 2 * (1 - stats.t.cdf(abs(t_stat), df))
ci = (mean_t - mean_c - stats.t.ppf(0.975, df)*se,
mean_t - mean_c + stats.t.ppf(0.975, df)*se)
lift = (mean_t - mean_c) / mean_c * 100
p_c, p_t = mean_c, mean_t
return {
'control_metric': p_c,
'treatment_metric': p_t,
'absolute_effect': p_t - p_c,
'relative_lift': lift,
'p_value': p_value,
'confidence_interval': ci,
'significant': p_value < alpha,
'n_control': n_c,
'n_treatment': n_t
}
고급 기법:
| 기법 | 목적 | 상황 |
|---|---|---|
| CUPED | 분산 감소 | 사전 데이터 활용 가능 시 |
| Sequential Testing | 조기 종료 | 빠른 의사결정 필요 |
| Multi-armed Bandit | 탐색/활용 균형 | 지속적 최적화 |
| Diff-in-Diff | 시간 효과 통제 | 무작위화 불완전 |
참고 논문: - Kohavi, R. et al. (2009). "Controlled Experiments on the Web: Survey and Practical Guide". Data Mining and Knowledge Discovery. - Deng, A. et al. (2013). "Improving the Sensitivity of Online Controlled Experiments by Utilizing Pre-Experiment Data". WSDM.
상세: ab-test.md
정성 분석 (Qualitative Analysis)¶
VoC 분석 (Voice of Customer)¶
고객 피드백에서 인사이트를 추출하는 기법.
데이터 소스: - 고객 리뷰 - 설문조사 응답 - CS 문의 내역 - 소셜 미디어 멘션
분석 방법:
| 방법 | 설명 | 도구 |
|---|---|---|
| 텍스트 분류 | 주제/카테고리 분류 | ML 모델, LLM |
| 감성 분석 | 긍정/부정/중립 판단 | VADER, BERT |
| 토픽 모델링 | 잠재 주제 발견 | LDA, BERTopic |
| 키워드 추출 | 핵심 단어/구문 | TF-IDF, RAKE |
| NER | 제품/기능명 추출 | spaCy, Flair |
from bertopic import BERTopic
from transformers import pipeline
import pandas as pd
def voc_analysis(texts, n_topics=10):
"""
VoC 텍스트 분석 (토픽 모델링 + 감성 분석)
"""
# 토픽 모델링
topic_model = BERTopic(language='korean', nr_topics=n_topics)
topics, probs = topic_model.fit_transform(texts)
# 토픽별 대표 키워드
topic_info = topic_model.get_topic_info()
# 감성 분석
sentiment_analyzer = pipeline(
"sentiment-analysis",
model="nlptown/bert-base-multilingual-uncased-sentiment"
)
sentiments = []
for text in texts[:1000]: # 샘플링
try:
result = sentiment_analyzer(text[:512])[0]
sentiments.append({
'label': result['label'],
'score': result['score']
})
except:
sentiments.append({'label': 'unknown', 'score': 0})
return {
'topics': topics,
'topic_info': topic_info,
'topic_model': topic_model,
'sentiments': sentiments
}
상세: voc.md
수요/매출 분석¶
시계열 예측 (Time Series Forecasting)¶
과거 패턴을 기반으로 미래 값을 예측하는 기법.
방법론 분류:
| 분류 | 방법 | 특징 |
|---|---|---|
| 통계적 | ARIMA, ETS, Prophet | 해석 용이, 단일 시계열 |
| 기계학습 | LightGBM, XGBoost | 다변량, 특성 엔지니어링 |
| 딥러닝 | LSTM, Transformer, N-BEATS | 복잡한 패턴, 대용량 |
| Ensemble | 여러 모델 결합 | 안정성 |
from prophet import Prophet
import pandas as pd
def demand_forecast(df, date_col, value_col, periods=30,
seasonality='auto', holidays=None):
"""
Prophet 기반 수요 예측
"""
# Prophet 형식으로 변환
prophet_df = df[[date_col, value_col]].rename(
columns={date_col: 'ds', value_col: 'y'}
)
model = Prophet(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
holidays=holidays,
interval_width=0.95
)
model.fit(prophet_df)
# 예측
future = model.make_future_dataframe(periods=periods)
forecast = model.predict(future)
# 정확도 (MAPE)
train_predictions = forecast[forecast['ds'].isin(prophet_df['ds'])]
actuals = prophet_df['y'].values
predictions = train_predictions['yhat'].values
mape = np.mean(np.abs((actuals - predictions) / actuals)) * 100
return {
'model': model,
'forecast': forecast,
'mape': mape
}
기여도 분석 (Attribution Analysis)¶
매출 변동의 원인을 분해하는 기법.
요소 분해:
def revenue_decomposition(current, previous):
"""
매출 변동 요인 분해
Parameters:
-----------
current, previous: dict with keys ['traffic', 'cvr', 'aov', 'revenue']
"""
# 3-way decomposition
traffic_effect = (current['traffic'] - previous['traffic']) * previous['cvr'] * previous['aov']
cvr_effect = previous['traffic'] * (current['cvr'] - previous['cvr']) * previous['aov']
aov_effect = previous['traffic'] * previous['cvr'] * (current['aov'] - previous['aov'])
# Interaction effects
interaction = current['revenue'] - previous['revenue'] - traffic_effect - cvr_effect - aov_effect
return {
'traffic_effect': traffic_effect,
'cvr_effect': cvr_effect,
'aov_effect': aov_effect,
'interaction': interaction,
'total_change': current['revenue'] - previous['revenue']
}
기법 선택 가이드¶
비즈니스 질문별 매핑¶
| 질문 | 분석 기법 | 주요 지표 |
|---|---|---|
| "왜 매출이 떨어졌나?" | 퍼널, 코호트, 기여도 분석 | 전환율, 리텐션, AOV |
| "어떤 고객에게 투자해야 하나?" | RFM, LTV | CLV, LTV:CAC |
| "이 기능이 효과가 있나?" | A/B 테스트 | 전환율, 참여도 |
| "고객이 왜 이탈하나?" | 코호트, VoC | 이탈률, 감성 |
| "다음 달 수요는?" | 시계열 예측 | 예측값, MAPE |
상세 문서¶
| 기법 | 설명 | 링크 |
|---|---|---|
| RFM 분석 | 고객 세분화 | rfm.md |
| 코호트 분석 | 리텐션 추적 | cohort.md |
| LTV 분석 | 고객 가치 추정 | ltv.md |
| 퍼널 분석 | 전환 최적화 | funnel.md |
| A/B 테스트 | 실험 설계 | ab-test.md |
| VoC 분석 | 정성 데이터 | voc.md |
참고 문헌¶
서적¶
- Kohavi, R. et al. (2020). "Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing". Cambridge.
- Provost, F. & Fawcett, T. (2013). "Data Science for Business". O'Reilly.
- Fader, P.S. (2020). "Customer Centricity". Wharton Digital Press.
논문¶
- Fader, P.S. et al. (2005). "RFM and CLV". JMR.
- Kohavi, R. et al. (2009). "Controlled Experiments on the Web". DMKD.
- Deng, A. et al. (2013). "CUPED". WSDM.
도구¶
- Amplitude, Mixpanel (Product Analytics)
- Google Analytics 4
- Statsig, Optimizely (Experimentation)
- dbt (Data Transformation)