Add retention analysis configuration file - Introduced `retention_analysis_config.json` to define settings for retention analysis. - Configured analysis settings including correlation threshold, top features, and minimum sample size. - Specified retention groups for analysis. - Listed columns to be excluded from the analysis. - Defined key metrics for day 0 analysis. - Set output preferences for saving results in various formats.
20 KiB
Retention Analysis Script Documentation
개요
retention_analysis.py는 던전 스토커즈 게임의 신규 유저 리텐션 분석을 위한 고도화된 Python 분석 도구입니다. 이 스크립트는 통계적 방법론을 기반으로 신규 유저의 행동 패턴을 심층 분석하여 리텐션에 영향을 미치는 핵심 지표를 식별하고, 특히 D0(첫날) 이탈 문제에 대한 실행 가능한 인사이트를 제공합니다.
프로젝트 배경 및 필요성
현재 던전 스토커즈는 **D0 이탈률이 65.9%**로 매우 높아 서비스 지속성에 심각한 위협이 되고 있습니다. 모바일 게임 업계 평균 D0 이탈률이 25-35%인 점을 고려하면, 이는 즉각적인 개선이 필요한 critical 수준입니다.
이 스크립트는 데이터 기반의 과학적 접근을 통해:
- 리텐션 영향 요인 정량화: 어떤 지표가 유저 리텐션에 긍정적/부정적 영향을 미치는지 통계적으로 검증
- 행동 패턴 차이 분석: D0 이탈 유저와 잔존 유저의 게임 내 행동 패턴의 핵심 차이점 파악
- 개선 우선순위 제시: 게임 디자인 개선을 위한 데이터 기반의 구체적이고 실행 가능한 인사이트 제공
- 예측 모델 구축: 머신러닝을 활용한 이탈 위험 예측 및 조기 경보 시스템
설치 및 환경 설정
필수 패키지 설치
# 기본 데이터 분석 패키지
pip install pandas numpy scipy matplotlib seaborn
# 머신러닝 패키지
pip install scikit-learn
# 진행률 표시 및 유틸리티
pip install tqdm
# 선택적: 고급 분석을 위한 추가 패키지
pip install plotly jupyter ipywidgets # 인터랙티브 시각화
pip install statsmodels # 고급 통계 분석
프로젝트 구조
ds_new_user_analy/
├── retention_analysis.py # 메인 분석 스크립트 (562 lines)
├── retention_analysis_config.json # 설정 파일 (분석 파라미터 관리)
├── retention_analysis.md # 이 문서 (사용법 및 이론)
└── analysis_results/ # 분석 결과 저장 디렉토리
├── correlation_analysis_YYYYMMDD_HHMMSS.csv # 상관관계 분석 결과
├── d0_churn_analysis_YYYYMMDD_HHMMSS.csv # D0 이탈 분석 결과
├── feature_importance_YYYYMMDD_HHMMSS.csv # 특성 중요도 분석 결과
├── insights_report_YYYYMMDD_HHMMSS.html # HTML 인사이트 리포트
└── retention_analysis_YYYYMMDD_HHMMSS.log # 상세 실행 로그
사용법
기본 실행
# 명령행 인자로 CSV 파일 지정
python retention_analysis.py path/to/your/analysis_data.csv
# 대화형 모드 (파일 경로 입력 프롬프트)
python retention_analysis.py
실행 예시
# 절대 경로 사용 (권장)
python retention_analysis.py "E:\DS_Git\ds_new_user_analy\analysis_results\ds-new_user_analy-20250830_174158.csv"
# 상대 경로 사용
python retention_analysis.py analysis_results/ds-new_user_analy-20250830_174158.csv
입력 데이터 요구사항
스크립트가 정상 작동하기 위해 CSV 파일에 다음 컬럼들이 필수로 포함되어야 합니다:
필수 컬럼:
uid: 사용자 고유 식별자retention_status: 리텐션 상태 (Retained_d0, Retained_d1, ..., Retained_d7+)create_time: 사용자 생성 시간 (ISO 8601 형식:2025-08-13T20:14:43+09:00)
권장 컬럼 (분석 품질 향상):
tutorial_complete: 튜토리얼 완료 여부level_up_count: 레벨업 횟수play_time_total: 총 플레이 시간COOP_entry_count,Solo_entry_count: 게임 모드별 진입 횟수death_Monster,death_Trap,death_PK: 사망 원인별 통계item_gain_Equipment,gold_gain_total: 아이템/골드 획득량
데이터 형식 표준:
create_time은 ISO 8601 표준 형식을 사용하며 빈 값이 없음을 보장- 한국 시간대(+09:00) 정보를 포함하여 정확한 시간대 분석 지원
- 모든 날짜/시간 계산은 한국 표준시(KST) 기준으로 처리
스크립트 작동 원리 및 아키텍처
전체 워크플로우
flowchart TD
A[CSV 파일 로드] --> B[데이터 유효성 검사]
B --> C[분석 기간 정보 추출 ISO8601]
C --> D[기본 전처리 및 인코딩]
D --> E[상관관계 분석 Spearman]
E --> F[D0 이탈자 특성 분석 t-test]
F --> G[Feature Importance Random Forest]
G --> H[HTML 인사이트 리포트 생성]
H --> I[결과 파일 저장 CSV/HTML/LOG]
C --> J[시간대별 가입 패턴 분석]
C --> K[요일별 가입 패턴 분석]
J --> H
K --> H
핵심 클래스: RetentionAnalyzer
class RetentionAnalyzer:
"""
리텐션 분석의 메인 엔진
- 통계적 방법론 적용
- 결과 저장 및 시각화
- 확장 가능한 분석 프레임워크
"
# 핵심 메서드들
def __init__(csv_path, config_path='retention_analysis_config.json') # 초기화
def validate_csv() # 입력 데이터 검증
def load_data() # 데이터 로드 및 기본 통계
def extract_analysis_period() # 분석 기간 및 가입 패턴 추출 (ISO 8601)
def analyze_correlations() # Spearman 상관관계 분석
def analyze_d0_churners() # D0 이탈자 vs 잔존자 비교 (t-test)
def feature_importance_analysis() # Random Forest 중요도 분석
def generate_insights_report() # HTML 리포트 생성 (콘솔로그 제외)
def run_full_analysis() # 전체 분석 파이프라인 실행
데이터 구조 및 전처리
1. Retention Status 순서형 인코딩:
# 문자열 리텐션 상태를 순서형 수치로 변환
retention_groups = ['Retained_d0', 'Retained_d1', ..., 'Retained_d7+']
df['retention_encoded'] = df['retention_status'].map(
{status: i for i, status in enumerate(retention_groups)}
)
# 결과: Retained_d0=0, Retained_d1=1, ..., Retained_d7+=7
이 인코딩을 통해 리텐션 단계 간의 순서 관계를 보존하면서 수치 분석이 가능해집니다.
2. 분석 기간 정보 추출 (create_time 기반):
# ISO 8601 형식 시간 데이터 처리
create_times = pd.to_datetime(df['create_time'], format='ISO8601')
# 한국 시간대 기준 분석
korea_dates = create_times.dt.tz_convert('Asia/Seoul').dt.date
korea_hours = create_times.dt.tz_convert('Asia/Seoul').dt.hour
korea_weekdays = create_times.dt.tz_convert('Asia/Seoul').dt.dayofweek
# 가입 패턴 분석
daily_signups = korea_dates.value_counts().sort_index() # 일별 가입자
hourly_signups = korea_hours.value_counts().sort_index() # 시간대별 가입자
weekday_signups = korea_weekdays.value_counts().sort_index() # 요일별 가입자
3. 결측값 처리 전략:
- 상관관계 분석: 각 지표별로 pairwise deletion (해당 지표만 결측값 제거)
- Feature Importance: Zero imputation (게임 내 활동 없음으로 해석)
- 그룹 비교: Complete case analysis (양쪽 그룹 모두 유효한 값만 사용)
- 시간 데이터: ISO 8601 표준으로 완전한 데이터 보장
통계적 분석 방법론
1. 상관관계 분석 (Spearman Rank Correlation)
목적: 각 게임 내 지표와 리텐션 단계 간의 단조 관계를 정량화
왜 Spearman을 선택했는가?
retention_status가 순서형 변수 (d0 < d1 < d2 < ... < d7+)- 비모수적 방법으로 분포 가정이 불필요
- 이상치에 덜 민감
- 비선형 단조 관계도 감지 가능
수학적 배경: Spearman 상관계수 ρ는 다음과 같이 계산됩니다:
ρ = 1 - (6 × Σd²) / (n × (n² - 1))
여기서:
- d: 각 관측값의 두 변수 순위 차이
- n: 샘플 크기
코드 구현:
from scipy.stats import spearmanr
for col in numeric_columns:
# 결측값 제거
valid_data = df[[col, 'retention_encoded']].dropna()
# 최소 샘플 크기 확인 (기본값: 10)
if len(valid_data) >= min_sample_size:
corr, p_value = spearmanr(
valid_data[col],
valid_data['retention_encoded']
)
# 통계적 유의성 검정 (α = 0.05)
is_significant = p_value < 0.05
결과 해석:
- ρ > 0: 해당 지표 값이 높을수록 더 오래 게임 플레이 (긍정적)
- ρ < 0: 해당 지표 값이 높을수록 빨리 이탈 (부정적)
- |ρ| > 0.3: 중간 정도의 관계
- |ρ| > 0.5: 강한 관계
2. 그룹 비교 분석 (Welch's t-test)
목적: D0 이탈 그룹과 잔존 그룹 간의 평균 차이를 통계적으로 검정
Welch's t-test를 선택한 이유:
- 등분산성 가정이 불필요 (두 그룹의 분산이 달라도 됨)
- 샘플 크기가 달라도 안정적
- 게임 데이터에서 흔히 발생하는 이분산성 문제 해결
통계적 가설:
- H₀(귀무가설): μ₁ = μ₂ (두 그룹의 평균이 같다)
- H₁(대립가설): μ₁ ≠ μ₂ (두 그룹의 평균이 다르다)
코드 구현:
from scipy import stats
# D0 이탈자와 잔존자 데이터 분리
d0_users = df[df['retention_status'] == 'Retained_d0']
retained_users = df[df['retention_status'] != 'Retained_d0']
for metric in key_metrics:
d0_data = d0_users[metric].dropna()
retained_data = retained_users[metric].dropna()
# Welch's t-test 수행
t_stat, p_value = stats.ttest_ind(
d0_data,
retained_data,
equal_var=False # 등분산 가정 안 함
)
# 실용적 차이 계산 (효과 크기)
d0_mean = d0_data.mean()
retained_mean = retained_data.mean()
diff_percentage = ((retained_mean - d0_mean) / (d0_mean + 0.0001)) * 100
결과 해석:
- p-value < 0.05: 통계적으로 유의한 차이 존재
- 차이율 100% 이상: 잔존자가 이탈자보다 2배 이상 높은 지표
- Cohen's d 효과 크기: |d| > 0.8 (큰 효과), 0.5-0.8 (중간), 0.2-0.5 (작은 효과)
3. 특성 중요도 분석 (Random Forest)
목적: 다변량 환경에서 리텐션 예측에 가장 중요한 변수들을 식별
Random Forest의 장점:
- 비선형 관계 포착: 복잡한 게임 내 상호작용 모델링
- 변수 간 상호작용: A와 B의 조합 효과 자동 탐지
- 과적합 방지: 배깅과 랜덤 서브스페이스 기법 사용
- 안정성: 아웃라이어에 견고함
- 해석 가능성: 각 변수의 기여도 정량화
중요도 계산 원리: 각 변수의 중요도는 해당 변수가 모든 트리에서 불순도 감소에 기여한 평균값입니다:
Importance(v) = (1/T) × Σ[t=1 to T] Σ[s∈Splits(v,t)] p(s) × ΔI(s)
여기서:
- T: 트리 개수
- p(s): 분할 s에 도달하는 샘플 비율
- ΔI(s): 분할 s에서의 불순도 감소량
코드 구현:
from sklearn.ensemble import RandomForestClassifier
# 데이터 준비
X = df[feature_columns].fillna(0) # Zero imputation
y = df['retention_encoded'] # 타겟 변수
# Random Forest 모델 설정
rf = RandomForestClassifier(
n_estimators=100, # 트리 개수 (안정성과 성능 균형)
random_state=42, # 재현 가능성
n_jobs=-1, # 병렬 처리
max_depth=None, # 트리 깊이 제한 없음 (자연스러운 중단)
min_samples_split=2, # 최소 분할 샘플 수
min_samples_leaf=1 # 최소 리프 샘플 수
)
# 모델 학습
rf.fit(X, y)
# 중요도 추출
importance_scores = rf.feature_importances_
결과 해석:
- 0.1 이상: 매우 중요한 변수 (핵심 개선 타겟)
- 0.05-0.1: 중요한 변수 (보조 개선 요소)
- 0.01-0.05: 보통 중요 (모니터링 지표)
- 0.01 미만: 낮은 중요도 (우선순위 후순위)
고급 분석 기법 및 확장
1. 통계적 검증 강화
다중 비교 문제 해결:
from statsmodels.stats.multitest import multipletests
# Bonferroni 또는 FDR 보정 적용
p_values = [result['p_value'] for result in analysis_results]
rejected, p_corrected, _, _ = multipletests(p_values, method='fdr_bh')
효과 크기 계산:
# Cohen's d 계산
def cohens_d(group1, group2):
pooled_std = np.sqrt(((len(group1) - 1) * group1.var() +
(len(group2) - 1) * group2.var()) /
(len(group1) + len(group2) - 2))
return (group1.mean() - group2.mean()) / pooled_std
2. 심층 세그멘테이션 분석
클러스터링 기반 유저 타입 분류:
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# 특성 정규화
scaler = StandardScaler()
scaled_features = scaler.fit_transform(df[numeric_columns])
# K-means 클러스터링
kmeans = KMeans(n_clusters=5, random_state=42)
df['user_segment'] = kmeans.fit_predict(scaled_features)
# 세그먼트별 리텐션 패턴 분석
segment_retention = df.groupby(['user_segment', 'retention_status']).size()
3. 시계열 분석 및 트렌드
일별/주별 리텐션 트렌드:
# 코호트 분석
df['signup_date'] = pd.to_datetime(df['signup_date'])
df['cohort'] = df['signup_date'].dt.to_period('W') # 주간 코호트
cohort_retention = df.groupby('cohort')['retention_encoded'].describe()
설정 파일 상세 설명
retention_analysis_config.json
{
"analysis_settings": {
"correlation_threshold": 0.05, // 통계적 유의수준 (Type I 오류율)
"top_n_features": 20, // 결과 표시할 상위 중요 변수 개수
"min_sample_size": 10, // 분석 최소 샘플 크기 (신뢰성 확보)
"effect_size_threshold": 0.1 // 실용적 유의성 최소 효과 크기
},
"retention_groups": [ // 리텐션 단계 정의 및 순서
"Retained_d0", "Retained_d1", "Retained_d2", "Retained_d3",
"Retained_d4", "Retained_d5", "Retained_d6", "Retained_d7+"
],
"exclude_columns": [ // 분석에서 제외할 메타데이터 컬럼
"uid", "retention_status", "retention_encoded",
"country", "nickname", "auth_id", "create_time"
],
"key_metrics_for_d0_analysis": [ // D0 분석 핵심 지표 (도메인 지식 기반)
"tutorial_complete", // 온보딩 완료도
"level_up_count", // 성장 경험
"play_time_total", // 참여도
"COOP_entry_count", "Solo_entry_count", // 게임 모드 선호
"death_Monster", "death_Trap", "death_PK", // 실패 경험
"item_gain_Equipment", "gold_gain_total" // 보상 경험
],
"output_settings": {
"save_csv": true, // 원시 데이터 CSV 저장
"save_html": true, // HTML 리포트 저장 (기본 출력)
"save_console_log": true, // 디버깅용 로그 저장
"timestamp_format": "%Y%m%d_%H%M%S" // 파일명 타임스탬프 형식
}
}
출력 결과 해석 가이드
상관관계 분석 결과
긍정적 상관관계 (양의 값) 해석:
shop_sell_count: 0.428 (p < 0.001)
→ 아이템 판매 활동이 활발한 유저일수록 더 오래 게임을 플레이하는 경향 → 액션 아이템: 거래 시스템 개선, 마켓플레이스 UX 향상
부정적 상관관계 (음의 값) 해석:
dungeon_first_result: -0.173 (p < 0.01)
→ 첫 던전에서 실패한 유저일수록 빨리 이탈하는 경향
→ 액션 아이템: 초심자 던전 난이도 조정, 실패 시 보상 시스템
Feature Importance 결과
중요도 점수별 액션:
- 0.1 이상 (매우 중요): 즉시 개선 프로젝트 착수
- 0.05-0.1 (중요): 다음 분기 개선 계획에 포함
- 0.01-0.05 (보통): 정기 모니터링 대상
- 0.01 미만 (낮음): 우선순위 후순위
D0 분석 결과
차이율별 해석 및 액션:
tutorial_complete:
- D0 평균: 0.23, 잔존 평균: 0.89, 차이: 287%
→ 튜토리얼 완료가 리텐션에 결정적 영향
→ 즉시 액션: 튜토리얼 완료율 개선 프로젝트
가입 패턴 분석 결과
시간대별 분석:
가입 집중 시간: 20시 (1,250명)
→ 저녁 시간대에 가입이 집중됨을 의미 → 마케팅 액션: 해당 시간대 타겟팅 광고 집중
요일별 분석:
가입 집중 요일: 토요일 (3,200명)
→ 주말에 신규 가입이 활발함 → 운영 액션: 주말 이벤트 및 서버 안정성 강화
문제 해결 및 최적화
일반적인 오류와 해결책
1. 메모리 부족 오류
# 청크 단위 처리
chunk_size = 50000
for chunk in pd.read_csv(filepath, chunksize=chunk_size):
process_chunk(chunk)
2. 인코딩 오류
# UTF-8 인코딩 명시적 지정
df = pd.read_csv(filepath, encoding='utf-8-sig')
3. 성능 최적화
# 데이터 타입 최적화
df['user_level'] = df['user_level'].astype('int16') # 메모리 절약
df['is_premium'] = df['is_premium'].astype('bool') # 불린 최적화
대용량 데이터 처리
샘플링 전략:
# 층화 샘플링 (각 리텐션 그룹에서 동일 비율 추출)
from sklearn.model_selection import train_test_split
sampled_data, _ = train_test_split(
df,
test_size=0.7, # 30%만 사용
stratify=df['retention_status'], # 층화
random_state=42
)
비즈니스 임팩트 및 ROI
개선 우선순위 매트릭스
높은 임팩트 낮은 임팩트
높은 구현비용 [계획 검토] [보류]
낮은 구현비용 [즉시 실행] [빠른 승리]
A/B 테스트 설계
분석 결과를 바탕으로 한 개선안은 반드시 A/B 테스트를 통해 검증:
# 실험 설계 예시
control_group = new_users.sample(frac=0.5, random_state=42)
treatment_group = new_users.drop(control_group.index)
# 개선 효과 측정
improvement = (treatment_retention - control_retention) / control_retention
statistical_power = calculate_power(effect_size, alpha=0.05, sample_size=len(control_group))
결론 및 향후 개선
이 retention_analysis.py 스크립트는 단순한 데이터 분석을 넘어서, 게임 서비스 개선을 위한 의사결정 지원 시스템의 역할을 합니다.
핵심 가치:
- 과학적 접근: 통계적으로 검증된 방법론으로 신뢰할 수 있는 인사이트 제공
- 실행 가능성: 분석 결과가 구체적인 개선 액션으로 연결
- 확장성: 새로운 분석 기법과 지표를 쉽게 추가 가능
- 자동화: 정기적인 분석과 모니터링을 통한 지속적 개선
- 시간대 인사이트: 가입 패턴 분석으로 마케팅/운영 최적화 지원
2024년 8월 업데이트 내용:
- HTML 리포트: 마크다운에서 전문적인 HTML 형식으로 변경
- 분석 기간 정보: ISO 8601 형식
create_time기반 자동 추출 - 시간대 분석: 한국 시간 기준 시간대별/요일별 가입 패턴 분석
- 콘솔로그 분리: HTML 리포트에서 콘솔로그 제거, 상세 분석 데이터 중심으로 개편
- 데이터 품질 강화: 표준 시간 형식 지원으로 분석 정확도 향상
향후 발전 방향:
- 실시간 대시보드 연동 (가입 패턴 실시간 모니터링)
- 예측 모델 고도화 (딥러닝 활용)
- 개인화된 리텐션 전략 추천 (시간대별 타겟팅)
- 자동화된 A/B 테스트 파이프라인
- 가입 패턴 기반 마케팅 최적화 시스템
정기적인 분석을 통해 게임 개선 효과를 측정하고, 새로운 문제점을 조기에 발견하여 서비스의 지속 가능한 성장에 기여할 수 있습니다.