콘텐츠로 이동
Data Prep
상세

분석 기법 개요

비즈니스 의사결정을 지원하는 핵심 데이터 분석 기법들을 정리함. 각 기법의 목적, 방법론, 실무 적용 방법을 다룬다.


분석 프레임워크

문제 해결 접근법

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()

분석 포인트:

  1. 수평 분석: 특정 코호트의 시간별 리텐션 추이
  2. 수직 분석: 같은 기간(Month 1 등)에서 코호트 간 비교
  3. 대각선 분석: 같은 시점에서의 모든 코호트 상태

참고 논문: - Ascarza, E. et al. (2018). "In Pursuit of Enhanced Customer Retention Management". Customer Needs and Solutions.

상세: cohort.md


LTV 분석 (Lifetime Value)

고객이 전체 관계 기간 동안 창출하는 총 가치를 추정하는 기법.

목적: CAC 대비 효율성 평가, 마케팅 예산 책정, 고객 세그먼트 가치 비교

기본 공식 (구독 모델):

\[LTV = \frac{ARPU}{Churn Rate} = ARPU \times \frac{1}{1 - Retention Rate}\]

할인율 적용:

\[LTV = \sum_{t=1}^{T} \frac{m \cdot r^{t-1}}{(1+d)^{t-1}}\]

여기서: - \(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 개선 우선순위 결정, 마케팅 최적화

일반적인 퍼널 단계:

방문 (100%)
상품 조회 (60%)
장바구니 추가 (15%)
결제 시작 (10%)
결제 완료 (7%)
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

분석 포인트:

  1. Drop-off 분석: 가장 큰 이탈이 발생하는 단계
  2. 세그먼트별 비교: 신규/기존, 채널별, 디바이스별
  3. 시간 추이: 퍼널 전환율의 변화
  4. 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 탐지하고자 하는 최소 효과 크기 비즈니스 기준

샘플 크기 계산:

\[n = \frac{2(Z_{\alpha/2} + Z_{\beta})^2 \sigma^2}{\delta^2}\]
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)

매출 변동의 원인을 분해하는 기법.

요소 분해:

\[\Delta Revenue = \Delta Traffic \times CVR \times AOV + Traffic \times \Delta CVR \times AOV + ...\]
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)