Compare commits

...

4 Commits

Author SHA1 Message Date
174e41c5b7 리텐션 분석 스크립트 최초 커밋
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.
2025-08-30 21:21:35 +09:00
6f35be1564 Update README.md: refine user retention classification, enhance OpenSearch data collection details, and improve output file naming conventions 2025-08-30 18:46:53 +09:00
98f3e739eb Add .gitignore to exclude analysis_results directory 2025-08-30 18:33:42 +09:00
79d9fba330 Add README.md from personal repository 2025-08-30 18:11:31 +09:00
6 changed files with 2351 additions and 0 deletions

View File

@ -0,0 +1,11 @@
{
"permissions": {
"allow": [
"Bash(python:*)",
"Bash(pip install:*)",
"Read(C:\\Users/**)"
],
"deny": [],
"ask": []
}
}

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/analysis_results

528
README.md Normal file
View File

@ -0,0 +1,528 @@
# 던전 스토커즈 신규 유저 리텐션 비교 분석 기획서
## 1. 분석 개요
### 1.1 분석 목표
- 스팀 얼리액세스 기간 중 유입된 신규 유저의 첫 24시간(D+0) 플레이 패턴을 분석
- D+1 리텐션(24-48시간 재접속)에 영향을 미치는 핵심 요소 파악
- 이탈 유저와 리텐션 유저 간의 행동 패턴 차이 분석
### 1.2 분석 대상
- 스크립트 실행 시 `--start-time``--end-time`으로 지정된 기간에 처음 접속한 모든 유저
- 예시: `python ds_new_user_analy.py --start-time "2025-08-22T12:00:00+09:00" --end-time "2025-08-25T12:00:00+09:00"`
### 1.3 그룹 분류
- Retained_d0: 첫 접속 후 24시간 이내에만 접속한 유저 (D+0 이탈)
- Retained_d1: 마지막 접속이 D+1인 유저
- Retained_d2: 마지막 접속이 D+2인 유저
- Retained_d3: 마지막 접속이 D+3인 유저
- Retained_d4: 마지막 접속이 D+4인 유저
- Retained_d5: 마지막 접속이 D+5인 유저
- Retained_d6: 마지막 접속이 D+6인 유저
- Retained_d7+: D+7 이후에도 접속한 유저
### 1.4 OpenSearch 연결 정보
- opensearch:
- host: "ds-opensearch.oneunivrs.com"
- port: 9200
- auth:
- username: "admin"
- password: "DHp5#r#GYQ9d"
- use_ssl: true
- verify_certs: false
- timeout: 60
- max_retries: 3
- headers={"Connection": "close"}
## 2. OpenSearch 인덱스별 수집 데이터
### 2.1 유저 식별 및 세션 관리
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-create_uid` | `uid`, `auth.id`, `@timestamp` | 신규 유저 식별 |
| `ds-logs-live-login_comp` | `uid`, `auth.id`, `@timestamp`, `body.nickname`, `body.language`, `country` | 첫 로그인 시간, 리텐션 판정, 닉네임, 사용 언어, 국가 |
| `ds-logs-live-logout` | `uid`, `@timestamp` | 세션 종료 시간, 비정상 종료 여부 |
| `ds-logs-live-heartbeat` | `uid`, `@timestamp` | 실제 활동 시간 추적 |
### 2.2 게임 진행 및 성과
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-survival_sta` | `uid`, `body.dungeon_mode`, `body.stalker_name`, `@timestamp` | 플레이한 모드, 던전 진입 횟수, 선택한 스토커 이름, 가장 먼저 시작한 모드 |
| `ds-logs-live-survival_end` | `uid`, `body.dungeon_mode`, `body.result`, `body.play_stats.playtime`, `body.play_stats.armor_break_cnt`, `body.play_stats.monster_kill_cnt`, `body.play_stats.player_kill_cnt`, `body.play_stats.raid_play`, `body.play_stats.damage_dealt_monster`, `body.play_stats.damage_dealt_player` | 던전 탈출 성공률, 생존 시간, 갑옷 파괴 횟수, 몬스터 킬 수, 플레이어 킬 수, 레이드 횟수, 몬스터에게 입힌 데미지, 플레이어에게 입힌 데미지 |
| `ds-logs-live-level_up` | `uid`, `body.level`, `body.stalker`| 각 스토커별 최고 레벨 |
| `ds-logs-live-dead` | `uid`, `body.inter_type` | 사망 횟수, 사망 원인 (PK 제외) |
| `ds-logs-live-player_kill` | `body.target_uid` | PK로 인한 사망 |
| `ds-logs-live-obj_inter` | `uid`, `inter_type` | 오브젝트 상호작용 |
| `ds-logs-live-item_ingame_equip` | `uid`, `body.base_type`, `body.item_grade`, `body.item_type`, `body.part_type` | 인게임 장비 장착 |
| `ds-logs-live-skill_point_get` | `uid` | 인게임 스킬 포인트 획득 경험 |
### 2.3 튜토리얼 및 퀘스트
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-tutorial_entry` | `uid`, `@timestamp`, `body.action` | 튜토리얼 진입 여부 (시작 또는 건너뛰기) |
| `ds-logs-live-log_tutorial` | `uid`, `body.action_type`, `body.stage_type` | 튜토리얼 완료 여부 (body.action_type: Complete 그리고 body.stage_type: result 인 경우 완료한 것임) |
| `ds-logs-live-guide_quest_stage` | `uid`, `body.guide_step` | 가이드 퀘스트 진행도 |
### 2.4 매칭 시스템
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-matching_start` | `uid`, `body.game_mode` | 매칭 시도 횟수 및 모드 |
| `ds-logs-live-matching_complete` | `uid`, `body.game_mode`, `body.matchingtime` | 모드 별 매칭 성공률, 평균 대기 시간 |
| `ds-logs-live-matching_failed` | `uid`, `body.fail_type` | 매칭 실패 원인 |
### 2.5 아이템 및 경제
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-item_get` | `uid`, `body.itemid`, `body.base_type`, `body.item_grade`, `body.item_type` | 아이템 획득 패턴 |
| `ds-logs-live-shop_buy` | `uid`, `body.cost_id`, `body.amt` | 구매 사용 금화량 (body.cost_id: i108000 일 경우만 합산) |
| `ds-logs-live-shop_sell` | `uid`, `body.cost_id`, `body.amt` | 판매 획득 금화량 (body.cost_id: i108000 일 경우만 합산) |
| `ds-logs-live-storage_use` | `uid`, `body.oper_type` | 창고 사용 여부 (입/출고) |
| `ds-logs-live-enchant` | `uid`, `body.amt` | 강화 소비 금화량 |
### 2.6 소셜 기능
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-friend` | `uid`, `body.oper_type`, `body.friend_type` | 친구 추가/삭제 |
| `ds-logs-live-player_invite` | `uid`, `body.target_uid` | 파티 초대 한 경험, 파티 초대 받은 경험 |
| `ds-logs-live-mail_read` | `uid`, `@timestamp` | 메일 확인 경험 |
### 2.7 거래소 및 경제 활동
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-exchange_reg` | `uid`, `@timestamp` | 거래소 등록 경험 |
| `ds-logs-live-exchange_use` | `uid`, `@timestamp` | 거래소 구입 경험 |
| `ds-logs-live-coupon` | `uid`, `body.coupon_code`, `@timestamp` | 리딤 코드 사용 |
### 2.8 기타 활동
| 인덱스 | 수집 항목 | 분석 용도 |
|--------|----------|----------|
| `ds-logs-live-button_click` | `uid`, `body.button_id` | 버튼 클릭 이력 |
| `ds-logs-live-log_hideout_upgrade` | `uid`, `body.hideout_level`, `body.hideout_type`, `body.hideout_upgrade_event` | 은신처 업그레이드 |
| `ds-logs-live-season_pass` | `uid`, `bbody.cause`, `body.season_pass_step`, `body.season_pass_type` | 시즌패스 진행 기록 |
## 3. 분석 지표 설계
### 3.1 기본 정보
- `uid`: 유저 ID
- `auth_id`: 스팀 ID (auth.id)
- `nickname`: 유저 닉네임 (body.nickname from login_comp)
- `create_time`: 계정 생성 시간 (KST) - create_uid 기반
- `retention_status`: 리텐션 상태 (Retained_d0/d1/d2/d3/d4/d5/d6/d7+)
- `language`: 사용 언어 (body.language from login_comp 최신 기록)
- `country`: 국가 정보 (country from login_comp)
- `device`: 디바이스 정보 (body.device_mod)
### 3.2 플레이 시간 및 세션
- `session_count`: D+0 접속 횟수 (login_comp 카운트)
- `active_seconds`: D+0 실제 활동 시간 (초) - heartbeat 기반 계산 (--full 옵션 사용 시)
- `total_playtime_minutes`: D+0 총 플레이 시간 (분) (--full 옵션 사용 시)
- `avg_session_length`: 평균 세션 길이 (분) (--full 옵션 사용 시)
- `logout_abnormal`: 비정상 종료 여부 (0/1) - last_logout < last_login 체크 (--full 옵션 사용 )
### 3.3 던전 플레이 성과
- **공통 지표**
- `dungeon_first_mode`: 처음 플레이한 던전 모드
- `dungeon_first_stalker`: 처음 선택한 스토커
- `dungeon_first_result`: 던전 결과 (0: 사망, 1: 탈출, null: 미플레이)
- **모드별 지표** (COOP, Solo, Survival, Survival_BOT, Survival_Unprotected)
- `{mode}_entry_count`: 모드별 던전 진입 횟수
- `{mode}_first_result`: 모드별 플레이 결과
- `{mode}_escape_count`: 모드별 탈출 성공 횟수
- `{mode}_avg_survival_time`: 모드별 평균 생존 시간
- `{mode}_max_survival_time`: 모드별 최대 생존 시간
- `{mode}_armor_break_count`: 모드별 갑옷 파괴 횟수
- `{mode}_raid_play_count`: 모드별 레이드 횟수
### 3.4 전투 성과
- **모드별 **
- `{mode}_monster_kill_count`: 모드별 몬스터 처치
- `{mode}_player_kill_count`: 모드별 플레이어
- **사망 원인 분석**
- `death_PK`: PK로 인한 사망
- `death_GiveUp`: 포기로 인한 사망 (inter_type = 0)
- `death_Mob`: 몬스터에게 사망 (inter_type = 1)
- `death_Trap`: 함정에 의한 사망 (inter_type = 10)
- `death_Red`: 레드존 사망 (inter_type = 11)
- `death_Others`: 기타 원인 사망
### 3.5 진행도 및 성장
- `level_max`: 도달한 최고 레벨 (body.level from level_up 최댓값)
- `level_max_stalker`: 최고 레벨 달성 스토커 이름 (body.stalker from level_up)
- `tutorial_entry`: 튜토리얼 진입 여부 (body.action = "Start")
- `tutorial_completed`: 튜토리얼 완료 여부 (body.action_type = "Complete" and body.stage_type = "result")
- `guide_quest_stage`: 가이드 퀘스트 최대 진행 단계 (body.guide_step)
- `skill_points_earned`: 획득한 스킬 포인트 (skill_point_get 카운트)
### 3.6 아이템 및 경제
- `highest_item_grade`: 획득한 최고 등급 장비 아이템 (body.item_grade, base_type = 2)
- `blueprint_use_count`: 블루프린트 사용 횟수 (craft_from_blueprint 카운트)
- `shop_buy_count`: 상점 구매 횟수 (shop_buy 카운트)
- `shop_sell_count`: 상점 판매 횟수 (shop_sell 카운트)
- `gold_spent`: 소비 금화 (body.cost_id = "i108000" 경우 body.amt 합계)
- `gold_earned`: 획득 금화 (판매로 얻은 금화)
- `storage_in_count`: 창고 입고 횟수 (body.oper_type = 1 카운트)
- `storage_out_count`: 창고 출고 횟수 (body.oper_type = -1 카운트)
- `enchant_count`: 강화 시도 횟수 (enchant 카운트)
- `enchant_gold_spent`: 강화 소비 금화 (body.amt 합계)
### 3.7 장비 관리
- `ingame_equip_count`: 인게임 장비 장착 횟수 (body.base_type = 2)
- `equip_by_grade`: 등급별 장착 아이템 (body.item_grade 분포)
- `equip_by_type`: 타입별 장착 아이템 (body.item_type 분포)
- `equip_by_part`: 부위별 장착 아이템 (body.part_type 분포)
### 3.8 오브젝트 상호작용
- `object_interaction_count`: 오브젝트 상호작용 횟수 (obj_inter 카운트)
- `interaction_by_type`: 타입별 상호작용 횟수 (body.inter_type 분포)
### 3.9 매칭 시스템
- `matching_start_count`: 매칭 시작 횟수 (matching_start 카운트)
- `matching_complete_count`: 매칭 성공 횟수 (matching_complete 카운트)
- `matching_failed_count`: 매칭 실패 횟수 (matching_failed 카운트)
- `avg_matching_time`: 평균 매칭 시간 (body.matchingtime 평균)
- `matching_by_mode`: 모드별 매칭 횟수 (body.game_mode 분포)
- `matching_fail_types`: 실패 원인별 횟수 (body.fail_type 분포)
### 3.10 소셜 활동
- `friend_add_count`: 친구 추가 (body.oper_type = 0, body.friend_type = 0)
- `friend_delete_count`: 친구 삭제 (body.oper_type = 1)
- `party_invite_sent`: 파티 초대 보낸 횟수 (player_invite with uid = sender)
- `party_invite_received`: 파티 초대 받은 횟수 (body.target_uid = uid)
- `mail_read_count`: 메일 읽은 횟수 (mail_read 카운트)
### 3.11 거래소 및 경제 활동
- `exchange_register_count`: 거래소 아이템 등록 횟수 (exchange_reg 카운트)
- `exchange_use_count`: 거래소 구매 횟수 (exchange_use 카운트)
- `coupon_used`: 쿠폰 사용 여부 (coupon 인덱스 존재 여부)
- `coupon_codes`: 사용한 쿠폰 코드 목록 (body.coupon_code)
### 3.12 기타 활동
- `button_click_count`: UI 버튼 클릭 (button_click 카운트)
- `button_click_types`: 클릭한 버튼 종류 (body.button_id 고유값 )
- `hideout_upgrade_count`: 은신처 업그레이드 횟수 (log_hideout_upgrade 카운트)
- `hideout_max_level`: 은신처 최고 레벨 (body.hideout_level 최댓값)
- `hideout_types_upgraded`: 업그레이드한 은신처 종류 (body.hideout_type 고유값)
- `season_pass_buy`: 시즌패스 구매 여부 (body.cause = 1)
- `season_pass_max_step`: 시즌패스 최대 단계 (body.season_pass_step 최댓값)
## 4. 구현 계획
### 4.1 스크립트 구조
```
ds_new_user_analy.py
├── 설정 및 상수 정의
├── OpenSearch 연결
├── 명령줄 인자 파싱
├── 신규 유저 코호트 추출
├── 리텐션 그룹 분류
├── 배치 단위 데이터 수집 (msearch 활용)
├── 데이터 집계 및 가공
└── CSV 파일 출력
```
### 4.2 주요 기능
1. 명령줄 인터페이스
- `--start-time`: 분석 시작 시간 (KST)
- `--end-time`: 분석 종료 시간 (KST)
- `--output-dir`: 결과 파일 저장 경로 (기본값: 현재 폴더)
- `--batch-size`: 배치 처리 크기 (기본값: 1000)
- `--max-workers`: 병렬 처리 스레드 (기본값: 16)
- `--full`: 세션 관련 상세 지표 포함 (기본값: False)
- `--sample-size`: 샘플 분석 크기 (None이면 전체 분석)
2. 데이터 처리
- OpenSearch `scan` API를 활용한 대용량 데이터 스캔
- `msearch` 활용한 효율적인 배치 쿼리 ( 번에 여러 인덱스 쿼리)
- nested 필드 처리 (body. 시작하는 필드)
- ThreadPoolExecutor를 활용한 병렬 처리
- 진행 상황 표시 (tqdm)
3. 에러 처리 최적화
- 연결 실패 재시도
- 부분 실패 로깅 계속 진행
- timeout 설정 (120초)
- scroll timeout 설정 (5분)
- 실시간 타이머 표시
4. 쿼리 최적화
- uid와 auth.id를 모두 포함하는 user_identity_filter 사용
- D+0 범위 필터링 ( 로그인 ~ 24시간)
- D+1 리텐션 판정 (24시간 ~ 48시간)
- 시간대 설정 (KST/UTC 변환)
### 4.3 핵심 알고리즘 (hack-detector 기법 적용)
1. **신규 유저 코호트 선정 (Composite Aggregation 최적화)**
- composite aggregation으로 메모리 효율적 처리
- 페이징 방식으로 대용량 데이터 안정적 처리
- auth.id별 로그인 시간을 aggregation으로 직접 계산
2. **Active Hours 계산 (스트리밍 방식)**
- Generator 패턴으로 메모리 사용 최소화
- 세션 간격 5분 기준으로 자동 세션 분리
- 최대 세션당 3시간 제한으로 이상값 처리
3. **OpenSearch 쿼리 최적화 (매핑 기반)**
```python
# body 필드는 nested가 아닌 object 타입으로 처리
# 잘못된 nested 쿼리 대신 일반 필드 쿼리 사용
{"term": {"body.field_name.keyword": "value"}} # nested 제거
# 키워드 필드 사용으로 정확성 향상
{"term": {"body.dungeon_mode.keyword": mode}} # .keyword 추가
```
4. **msearch 배치 처리 (NDJSON + 백오프 재시도)**
```python
# NDJSON 직접 생성으로 성능 향상
body_ndjson = "\n".join(json.dumps(x, ensure_ascii=False) for x in body_list) + "\n"
# 지수 백오프 재시도
for delay in [1, 2, 4, 8, 16]:
try:
response = client.msearch(body=body_ndjson, request_timeout=60)
break
except: time.sleep(delay)
```
5. **병렬 처리 최적화 (Future 패턴)**
- ThreadPoolExecutor의 Future 패턴으로 비동기 결과 수집
- 동적 청크 크기 조정 (사용자 수 기반)
- 실패한 청크 자동 재처리 메커니즘
6. **메모리 최적화 기법**
- 스트리밍 CSV 작성 (결과를 메모리에 모우지 않음)
- defaultdict와 generator 활용
- track_total_hits=False로 불필요한 카운트 생략
### 4.4 OpenSearch 매핑 기반 수정사항
**중요한 발견**: OpenSearch 매핑 분석 결과 `body` 필드가 `nested` 타입이 아닌 `object` 타입으로 확인됨
1. **nested 쿼리 제거**: `{"nested": {"path": "body", ...}}` → `{"term": {"body.field": value}}`
2. **키워드 필드 사용**: 문자열 필드에 `.keyword` 추가로 정확한 매칭
3. **retention_d1 필드 제거**: `retention_status`와 중복으로 제거
4. **필드 경로 정확성**: 실제 매핑 구조에 맞춘 필드 참조
### 4.5 개발 과정의 주요 시행착오 및 해결 방법
#### 4.5.1 데이터 수집 0값 문제 해결 (Critical Issue)
**문제 현상**:
- 초기 실행 시 모든 게임 관련 지표(dungeon_entry_count, monster_kill_count, player_kill_count 등)가 0으로 수집됨
- 사용자가 실제로 게임을 플레이했음에도 불구하고 활동 데이터가 누락됨
**근본 원인 분석**:
1. **잘못된 nested 쿼리 구조**: OpenSearch 매핑이 `object` 타입인데 `nested` 쿼리 사용
2. **필드 경로 오류**: `body.uid` 대신 직접 `uid` 필드 사용해야 함
3. **track_total_hits 설정**: `track_total_hits=False`로 인한 count 집계 실패
4. **필드명 불일치**: 실제 인덱스 필드명과 쿼리 필드명 mismatch
**해결 과정**:
```python
# 문제가 있던 코드 (Before)
{
"nested": {
"path": "body",
"query": {"term": {"body.uid": uid}}
}
}
# 수정된 코드 (After)
{"term": {"uid.keyword": uid}}
```
**검증 방법**:
- OpenSearch DevTools에서 직접 쿼리 테스트
- 샘플 사용자 100명으로 실제 데이터 확인
- 각 지표별 개별 쿼리 검증 후 msearch 통합
#### 4.5.2 first_value 쿼리 타입 문제 해결
**문제 현상**:
- `dungeon_first_mode`와 `dungeon_first_stalker`가 모두 0으로 출력
- 실제로는 "COOP", "Rene", "Hilda" 등의 문자열 데이터 존재
**근본 원인**:
1. **정렬 필드 오류**: 문자열 필드(`body.dungeon_mode`)로 정렬 시도
2. **기본값 처리 오류**: 문자열 필드에 숫자 기본값 0 할당
3. **쿼리 구조 문제**: `@timestamp`로 정렬 후 첫 번째 문서 추출 필요
**해결 방법**:
```python
# 문제 코드
sort_field = config.get("field", "@timestamp") # body.dungeon_mode로 정렬 시도
query_body["sort"] = [{sort_field: {"order": sort_order}}]
# 수정 코드
if agg_type == "first_value":
query_body["sort"] = [{"@timestamp": {"order": "asc"}}] # 항상 timestamp로 정렬
# 기본값 처리 수정
if metric_name in ["dungeon_first_mode", "dungeon_first_stalker"]:
result[metric_name] = "" # 문자열 기본값
```
**결과 검증**:
- COOP, Survival, Survival_Unprotected 등 다양한 모드 수집 확인
- Rene, Nave, Hilda, Rio, Baran, Lian 등 스토커 이름 정상 수집
#### 4.5.3 게임당 평균 데미지 로직 구현
**요구사항 변경**:
- 기존: `damage_dealt_monster`, `damage_dealt_player` 절대값
- 변경: 게임당 평균 데미지로 수평 비교 가능하게 수정
**구현 방식**:
1. **기존 damage 필드 제거**: metrics_config에서 완전 제거
2. **실시간 계산**: 던전 입장 횟수가 있는 경우에만 평균 계산
3. **별도 쿼리 실행**: survival_end 인덱스에서 직접 aggregation 수행
```python
# 평균 데미지 계산 로직
if result.get('dungeon_entry_count', 0) > 0:
damage_query = {
"aggs": {
"total_monster_damage": {"sum": {"field": "body.play_stats.damage_dealt_monster"}},
"total_player_damage": {"sum": {"field": "body.play_stats.damage_dealt_player"}}
}
}
result['avg_damage_per_game_monster'] = round(monster_damage / dungeon_count, 2)
result['avg_damage_per_game_player'] = round(player_damage / dungeon_count, 2)
```
#### 4.5.4 false positive 디버깅 - party_invite_received & season_pass_buy
**문제 제기**:
- 100명 샘플에서 `party_invite_received`와 `season_pass_buy`가 모두 0
- 실제 오류인지 정상 동작인지 확인 필요
**디버깅 과정**:
1. **party_invite_received 검증**:
```bash
# OpenSearch에서 직접 확인
curl -X GET "ds-logs-live-player_invite/_search"
# 결과: 데이터 존재하지만 테스트 사용자들은 실제로 초대받지 않음
```
2. **season_pass_buy 검증**:
```bash
# cause 값 분포 확인
{"aggs": {"cause_values": {"terms": {"field": "body.cause"}}}}
# 결과: cause:0 (진행) 95,106개, cause:1 (구매) 0개
```
**결론**:
- `party_invite_received`: 정상 동작, 실제로 테스트 기간 중 초대받은 사용자 없음
- `season_pass_buy`: 정상 동작, 실제로 시즌패스 구매한 사용자 없음
#### 4.5.5 성능 최적화 및 안정성 개선
**병렬처리 최적화**:
```python
# ThreadPoolExecutor 활용
with ThreadPoolExecutor(max_workers=max_workers) as executor:
for chunk in chunked_uids:
future = executor.submit(process_fixed_batch, client, chunk, cohort, metrics_config)
future_to_chunk[future] = chunk
```
**에러 처리 강화**:
- 지수 백오프 재시도 로직
- 실패한 배치 자동 재처리
- 상세한 로깅 및 진행상황 표시
**메모리 최적화**:
- Generator 패턴으로 스트리밍 처리
- 불필요한 데이터 early cleanup
- 배치 크기 동적 조정
#### 4.5.6 국가 정보 → 언어 정보 변경
**변경 사유**:
- VPN 사용으로 인한 국가 정보 부정확성
- 언어 설정이 사용자 특성 분석에 더 유효
**구현 방식**:
```python
"latest_info": {
"top_hits": {
"size": 1,
"sort": [{"@timestamp": {"order": "desc"}}],
"_source": ["body.nickname", "body.language"] # country → language
}
}
```
#### 4.5.7 최종 검증 및 품질 보증
**검증 단계**:
1. **5명 샘플 테스트**: 기본 로직 확인
2. **30명 중간 테스트**: 성능 및 안정성 확인
3. **100명 전체 테스트**: 모든 지표 정상 동작 확인
**품질 지표**:
- 총 66개 지표 중 66개 모두 정상 수집
- D+1 리텐션율: 40% (40/100명)
- 평균 처리 시간: 100명/2분
- 에러율: 0% (모든 배치 성공)
### 4.5.8 향후 유사 스크립트 개발 시 주의사항
**OpenSearch 쿼리 개발**:
1. **매핑 먼저 확인**: 개발 시작 전 필드 타입 (object vs nested) 반드시 확인
2. **DevTools 활용**: 복잡한 쿼리는 OpenSearch DevTools에서 먼저 검증
3. **샘플 테스트**: 5명 → 30명 → 100명 단계적 검증 후 전체 실행
4. **필드명 정확성**: `.keyword` 사용 여부, 실제 필드 경로 정확히 매칭
**성능 및 안정성**:
1. **배치 처리**: msearch를 활용한 효율적 대량 쿼리 수행
2. **에러 처리**: 지수 백오프 재시도, 실패 배치 자동 재처리
3. **메모리 관리**: Generator 패턴, 스트리밍 처리로 메모리 사용량 최소화
4. **병렬 처리**: ThreadPoolExecutor로 성능 향상, 단 OpenSearch 부하 고려
**데이터 검증**:
1. **의심스러운 0값**: 실제 데이터 부재인지 쿼리 오류인지 반드시 검증
2. **문자열 필드**: 기본값, 정렬 방식 주의 (숫자 0 vs 빈 문자열 "")
3. **시간대 처리**: KST/UTC 변환, 사용자별 개별 시간 범위 적용
4. **FALSE POSITIVE 체크**: 0값이 의심될 때 OpenSearch에서 직접 데이터 확인
### 4.6 출력 파일 및 로깅
- **결과 파일명**: `ds-new_user_analy-YYYYMMDD_HHMMSS.csv`
- **로그 파일명**: `ds-new_user_analy-YYYYMMDD_HHMMSS.log`
- **인코딩**: UTF-8 with BOM (Excel 호환)
- **형식**: CSV (기본 약 80개 지표, --full 옵션 시 세션 지표 추가)
- **저장 위치**: 스크립트 실행 디렉토리 내 `analysis_results/` 폴더
- **시간 형식**: 모든 시간 필드는 KST 기준 `YYYY-MM-DDTHH:mm:ss+09:00` 형식
- **로깅**: hack-detector 수준의 상세 로깅 (파일 + 콘솔 동시 출력)
## 5. 필요 라이브러리
```python
import os
import csv
import json
import time
import yaml
import logging
import argparse
import threading
from datetime import datetime, timedelta, timezone
from collections import defaultdict, Counter
from typing import Dict, List, Optional, Tuple, Generator, Any, Set
from concurrent.futures import ThreadPoolExecutor, as_completed, Future
from pathlib import Path
import pandas as pd
from tqdm import tqdm
from opensearchpy import OpenSearch
from opensearchpy.helpers import scan
```
## 6. 참고 자료
- 이전 분석 스크립트: `E:\DS_Git\DS_data_center\DS Log 분석\archive\DS 파이널테스트 신규유저 분석.py`
- OpenSearch 맵핑: `E:\DS_Git\DS_data_center\DS INFO\ds_opensearch_mappings.json`
- OpenSearch 스펙: `E:\DS_Git\DS_data_center\DS INFO\오픈서치 라이브 스펙.txt`
- 코드 품질 참고: `E:\DS_Git\hack-detector\` 디렉터리의 각종 분석 스크립트

556
retention_analysis.md Normal file
View File

@ -0,0 +1,556 @@
# 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 테스트 파이프라인
- 가입 패턴 기반 마케팅 최적화 시스템
정기적인 분석을 통해 게임 개선 효과를 측정하고, 새로운 문제점을 조기에 발견하여 서비스의 지속 가능한 성장에 기여할 수 있습니다.

1212
retention_analysis.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
{
"analysis_settings": {
"correlation_threshold": 0.05,
"top_n_features": 20,
"min_sample_size": 10
},
"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"
],
"key_metrics_for_d0_analysis": [
"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,
"save_markdown": true,
"save_console_log": true,
"timestamp_format": "%Y%m%d_%H%M%S"
}
}