콘텐츠로 이동

코호트 분석


왜 이 분석을?

문제: 전체 리텐션율만 보면 어떤 고객이 언제 이탈하는지 알 수 없다.

코호트 분석은 동일 시점에 유입된 고객 그룹의 행동 변화를 추적한다:

"1월 가입자 1,000명 중 3개월 후 몇 명이 남아있는가?"
"2월 가입자와 비교하면 어떤가?"
"프로모션 기간 가입자는 더 오래 남는가?"

핵심: 시간에 따른 행동 변화를 보면 "언제" 문제가 생기는지 알 수 있다.


어떤 가설?

가설 예시

가설 검증 방법 예상 액션
첫 주에 이탈이 집중된다 Week 0→1 리텐션 급락 확인 온보딩 강화, 첫 주 인센티브
프로모션 유입 고객은 리텐션이 낮다 프로모션 코호트 vs 일반 코호트 비교 프로모션 구조 변경, CAC 재계산
특정 기능 사용자는 이탈이 적다 기능 사용 여부로 코호트 분리 해당 기능 온보딩 시 노출
시즌 유입자는 시즌 종료 후 이탈 시즌별 코호트 추적 시즌 종료 전 리텐션 캠페인

분석 방법

1. 리텐션 코호트

import pandas as pd
import numpy as np

# 데이터: user_id, event_date
df['cohort'] = df.groupby('user_id')['event_date'].transform('min').dt.to_period('M')
df['event_month'] = df['event_date'].dt.to_period('M')
df['cohort_index'] = (df['event_month'] - df['cohort']).apply(lambda x: x.n)

# 코호트 테이블
cohort_table = df.groupby(['cohort', 'cohort_index'])['user_id'].nunique().unstack()

# 리텐션율 (%)
cohort_size = cohort_table[0]
retention = cohort_table.divide(cohort_size, axis=0) * 100

2. 코호트 히트맵

import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 8))
sns.heatmap(retention, 
            annot=True, 
            fmt='.1f',
            cmap='YlGnBu',
            vmin=0, vmax=100)
plt.title('Monthly Retention Cohort')
plt.xlabel('Months Since First Purchase')
plt.ylabel('Cohort (First Purchase Month)')

결과 예시:

           M+0    M+1    M+2    M+3    M+4    M+5
2024-01   100%   45%    32%    28%    25%    23%
2024-02   100%   42%    30%    26%    24%    -
2024-03   100%   48%    35%    30%    -      -
2024-04   100%   38%    25%    -      -      -     ← 문제
2024-05   100%   44%    -      -      -      -

3. 세그먼트별 코호트

# 유입 채널별 코호트
channel_cohort = df.groupby(['channel', 'cohort', 'cohort_index'])['user_id'].nunique().unstack()

# 결과 비교
organic = channel_cohort.loc['organic']
paid = channel_cohort.loc['paid']

# 차이 분석
diff = (organic / organic[0] * 100) - (paid / paid[0] * 100)

코호트 유형

획득 기반 코호트 (Acquisition Cohort)

  • 가입/첫 구매 시점 기준
  • 용도: 마케팅 채널 평가, 시즌 효과 분석

행동 기반 코호트 (Behavioral Cohort)

  • 특정 행동 수행 여부 기준
  • 예: "앱 설치 후 7일 내 구매한 유저" vs "구매 안 한 유저"
# 7일 내 구매 여부로 코호트 분리
first_purchase = df[df['event'] == 'purchase'].groupby('user_id')['event_date'].min()
signup_date = df[df['event'] == 'signup'].groupby('user_id')['event_date'].min()

days_to_purchase = (first_purchase - signup_date).dt.days
df['purchase_cohort'] = np.where(days_to_purchase <= 7, 'early_buyer', 'late_buyer')

가치 기반 코호트 (Value Cohort)

  • 첫 구매 금액, LTV 예측치 기준
  • 용도: 고가치 고객 조기 식별

비즈니스 액션

코호트 패턴별 대응

패턴 해석 액션
M+1 급락 온보딩 실패 첫 주 경험 개선, 튜토리얼, 인센티브
M+3~4 급락 습관화 실패 핵심 가치 재인식 캠페인, 새 기능 소개
특정 코호트만 낮음 해당 시점 이슈 그 시점 변경사항 확인 (가격, UI, 정책)
Paid 코호트 낮음 타겟팅 문제 광고 타겟 재설정, 메시지 변경

실제 적용 예시

문제: 전체 M+3 리텐션이 30% → 25%로 하락

코호트 분석:

               M+0    M+1    M+2    M+3
2024-01       100%    45%    32%    30%
2024-02       100%    44%    31%    28%
2024-03       100%    43%    30%    25%  ← 하락 시작
2024-04       100%    42%    28%    22%

가설: 3월에 무언가 바뀌었다

확인: - 3월 앱 업데이트: 메인 화면 UI 변경 - 3월 마케팅: 신규 광고 채널 추가

심층 분석:

# 신규 채널 코호트 분리
march_cohort = df[df['cohort'] == '2024-03']
new_channel = march_cohort[march_cohort['channel'] == 'new_channel']
existing_channel = march_cohort[march_cohort['channel'] != 'new_channel']

# 비교 결과
# new_channel M+3: 18%
# existing_channel M+3: 29%

발견: 신규 광고 채널 유입 고객의 리텐션이 현저히 낮음

액션: 1. 신규 채널 광고 타겟팅/메시지 검토 2. 신규 채널 유입자 전용 온보딩 실험 3. 채널별 CAC vs LTV 재계산 → ROI 음수면 채널 중단


주의사항

함정

  1. 코호트 크기 무시
  2. 1월 코호트 10,000명, 4월 코호트 500명 → 변동성 차이
  3. 해결: 신뢰구간 함께 표시, 작은 코호트 해석 주의

  4. 계절성 미반영

  5. 1월(신년) vs 8월(휴가철) 리텐션은 자연스럽게 다름
  6. 해결: 전년 동기 대비(YoY) 비교

  7. 단일 지표 함정

  8. 리텐션만 보면 매출/활동량 변화를 놓침
  9. 해결: 리텐션 + 구매액 + 빈도 코호트 함께 분석

보완 분석

한계 보완 방법
"왜" 이탈하는지 모름 VoC 분석, 이탈 설문
미래 예측 어려움 LTV 예측 모델
행동 상세 파악 퍼널 분석, 세션 분석

고급: 코호트 기반 예측

리텐션 커브 피팅

from scipy.optimize import curve_fit

# 멱함수 피팅 (Power Law)
def retention_curve(t, a, b):
    return a * (t + 1) ** (-b)

# 피팅
t = np.arange(len(retention_row))
popt, _ = curve_fit(retention_curve, t, retention_row.values)

# 12개월 후 예측
predicted_m12 = retention_curve(12, *popt)

LTV 추정

# 코호트별 누적 매출
cumulative_revenue = df.groupby(['cohort', 'cohort_index'])['revenue'].sum().unstack().cumsum(axis=1)

# M+12 LTV 예측 (리텐션 커브 기반)
# 현재까지 누적 + 예측 잔여 기간 매출