556 lines
20 KiB
Markdown
556 lines
20 KiB
Markdown
|
|
# Retention Analysis Script Documentation
|
|||
|
|
|
|||
|
|
## 개요
|
|||
|
|
|
|||
|
|
`retention_analysis.py`는 던전 스토커즈 게임의 신규 유저 리텐션 분석을 위한 고도화된 Python 분석 도구입니다. 이 스크립트는 통계적 방법론을 기반으로 신규 유저의 행동 패턴을 심층 분석하여 리텐션에 영향을 미치는 핵심 지표를 식별하고, 특히 D0(첫날) 이탈 문제에 대한 실행 가능한 인사이트를 제공합니다.
|
|||
|
|
|
|||
|
|
## 프로젝트 배경 및 필요성
|
|||
|
|
|
|||
|
|
현재 던전 스토커즈는 **D0 이탈률이 65.9%**로 매우 높아 서비스 지속성에 심각한 위협이 되고 있습니다. 모바일 게임 업계 평균 D0 이탈률이 25-35%인 점을 고려하면, 이는 즉각적인 개선이 필요한 critical 수준입니다.
|
|||
|
|
|
|||
|
|
이 스크립트는 데이터 기반의 과학적 접근을 통해:
|
|||
|
|
|
|||
|
|
- **리텐션 영향 요인 정량화**: 어떤 지표가 유저 리텐션에 긍정적/부정적 영향을 미치는지 통계적으로 검증
|
|||
|
|
- **행동 패턴 차이 분석**: D0 이탈 유저와 잔존 유저의 게임 내 행동 패턴의 핵심 차이점 파악
|
|||
|
|
- **개선 우선순위 제시**: 게임 디자인 개선을 위한 데이터 기반의 구체적이고 실행 가능한 인사이트 제공
|
|||
|
|
- **예측 모델 구축**: 머신러닝을 활용한 이탈 위험 예측 및 조기 경보 시스템
|
|||
|
|
|
|||
|
|
## 설치 및 환경 설정
|
|||
|
|
|
|||
|
|
### 필수 패키지 설치
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 기본 데이터 분석 패키지
|
|||
|
|
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 # 상세 실행 로그
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 사용법
|
|||
|
|
|
|||
|
|
### 기본 실행
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 명령행 인자로 CSV 파일 지정
|
|||
|
|
python retention_analysis.py path/to/your/analysis_data.csv
|
|||
|
|
|
|||
|
|
# 대화형 모드 (파일 경로 입력 프롬프트)
|
|||
|
|
python retention_analysis.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 실행 예시
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 절대 경로 사용 (권장)
|
|||
|
|
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) 기준으로 처리
|
|||
|
|
|
|||
|
|
## 스크립트 작동 원리 및 아키텍처
|
|||
|
|
|
|||
|
|
### 전체 워크플로우
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
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 순서형 인코딩:**
|
|||
|
|
```python
|
|||
|
|
# 문자열 리텐션 상태를 순서형 수치로 변환
|
|||
|
|
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 기반):**
|
|||
|
|
```python
|
|||
|
|
# 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: 샘플 크기
|
|||
|
|
|
|||
|
|
**코드 구현:**
|
|||
|
|
```python
|
|||
|
|
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₁(대립가설): μ₁ ≠ μ₂ (두 그룹의 평균이 다르다)
|
|||
|
|
|
|||
|
|
**코드 구현:**
|
|||
|
|
```python
|
|||
|
|
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에서의 불순도 감소량
|
|||
|
|
|
|||
|
|
**코드 구현:**
|
|||
|
|
```python
|
|||
|
|
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. 통계적 검증 강화
|
|||
|
|
|
|||
|
|
**다중 비교 문제 해결:**
|
|||
|
|
```python
|
|||
|
|
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')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**효과 크기 계산:**
|
|||
|
|
```python
|
|||
|
|
# 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. 심층 세그멘테이션 분석
|
|||
|
|
|
|||
|
|
**클러스터링 기반 유저 타입 분류:**
|
|||
|
|
```python
|
|||
|
|
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. 시계열 분석 및 트렌드
|
|||
|
|
|
|||
|
|
**일별/주별 리텐션 트렌드:**
|
|||
|
|
```python
|
|||
|
|
# 코호트 분석
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```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. 메모리 부족 오류**
|
|||
|
|
```python
|
|||
|
|
# 청크 단위 처리
|
|||
|
|
chunk_size = 50000
|
|||
|
|
for chunk in pd.read_csv(filepath, chunksize=chunk_size):
|
|||
|
|
process_chunk(chunk)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 인코딩 오류**
|
|||
|
|
```python
|
|||
|
|
# UTF-8 인코딩 명시적 지정
|
|||
|
|
df = pd.read_csv(filepath, encoding='utf-8-sig')
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. 성능 최적화**
|
|||
|
|
```python
|
|||
|
|
# 데이터 타입 최적화
|
|||
|
|
df['user_level'] = df['user_level'].astype('int16') # 메모리 절약
|
|||
|
|
df['is_premium'] = df['is_premium'].astype('bool') # 불린 최적화
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 대용량 데이터 처리
|
|||
|
|
|
|||
|
|
**샘플링 전략:**
|
|||
|
|
```python
|
|||
|
|
# 층화 샘플링 (각 리텐션 그룹에서 동일 비율 추출)
|
|||
|
|
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 테스트를 통해 검증:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 실험 설계 예시
|
|||
|
|
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` 스크립트는 단순한 데이터 분석을 넘어서, 게임 서비스 개선을 위한 **의사결정 지원 시스템**의 역할을 합니다.
|
|||
|
|
|
|||
|
|
**핵심 가치:**
|
|||
|
|
1. **과학적 접근**: 통계적으로 검증된 방법론으로 신뢰할 수 있는 인사이트 제공
|
|||
|
|
2. **실행 가능성**: 분석 결과가 구체적인 개선 액션으로 연결
|
|||
|
|
3. **확장성**: 새로운 분석 기법과 지표를 쉽게 추가 가능
|
|||
|
|
4. **자동화**: 정기적인 분석과 모니터링을 통한 지속적 개선
|
|||
|
|
5. **시간대 인사이트**: 가입 패턴 분석으로 마케팅/운영 최적화 지원
|
|||
|
|
|
|||
|
|
**2024년 8월 업데이트 내용:**
|
|||
|
|
- **HTML 리포트**: 마크다운에서 전문적인 HTML 형식으로 변경
|
|||
|
|
- **분석 기간 정보**: ISO 8601 형식 `create_time` 기반 자동 추출
|
|||
|
|
- **시간대 분석**: 한국 시간 기준 시간대별/요일별 가입 패턴 분석
|
|||
|
|
- **콘솔로그 분리**: HTML 리포트에서 콘솔로그 제거, 상세 분석 데이터 중심으로 개편
|
|||
|
|
- **데이터 품질 강화**: 표준 시간 형식 지원으로 분석 정확도 향상
|
|||
|
|
|
|||
|
|
**향후 발전 방향:**
|
|||
|
|
- 실시간 대시보드 연동 (가입 패턴 실시간 모니터링)
|
|||
|
|
- 예측 모델 고도화 (딥러닝 활용)
|
|||
|
|
- 개인화된 리텐션 전략 추천 (시간대별 타겟팅)
|
|||
|
|
- 자동화된 A/B 테스트 파이프라인
|
|||
|
|
- 가입 패턴 기반 마케팅 최적화 시스템
|
|||
|
|
|
|||
|
|
정기적인 분석을 통해 게임 개선 효과를 측정하고, 새로운 문제점을 조기에 발견하여 서비스의 지속 가능한 성장에 기여할 수 있습니다.
|