작성
·
33
0
WITH
-- 1단계: 필수 데이터만 추출하기
base_events AS (
SELECT
user_pseudo_id,
DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul") AS event_date
FROM advanced.app_logs
),
-- 2단계: 사용자별 첫 방문 주차와 각 활동 주차 구하기
user_weeks AS (
SELECT DISTINCT -- 중복 제거
user_pseudo_id,
-- 사용자별 첫 방문 주차 (월요일 기준)
DATE_TRUNC(MIN(event_date) OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
-- 실제 방문한 주차 (월요일 기준)
DATE_TRUNC(event_date, WEEK(MONDAY)) AS visit_week
FROM base_events
),
-- 3단계: 첫 방문 이후 몇 주차인지 계산하기
week_numbers AS (
SELECT
user_pseudo_id,
-- 첫 방문 이후 경과된 주차 계산
DATE_DIFF(visit_week, first_week, WEEK) AS week_number
FROM user_weeks
-- 최대 12주까지만 분석
WHERE DATE_DIFF(visit_week, first_week, WEEK) <= 12
),
-- 4단계: 주차별 총 사용자 수 계산하기
weekly_users AS (
SELECT
week_number,
COUNT(DISTINCT user_pseudo_id) AS user_count
FROM week_numbers
GROUP BY week_number
)
-- 5단계: 최종 리텐션 계산하기
SELECT
week_number,
user_count as active_users,
FIRST_VALUE(user_count) OVER(ORDER BY week_number) as first_week_users,
ROUND(100.0 * user_count / FIRST_VALUE(user_count) OVER(ORDER BY week_number), 2) as retention_rate
FROM weekly_users
ORDER BY week_number;
WITH
-- 1단계: 사용자별 주차 데이터 준비
user_weeks AS (
SELECT DISTINCT
user_pseudo_id,
-- 첫 방문 주차
DATE_TRUNC(MIN(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul"))
OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
-- 활동 주차
DATE_TRUNC(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul"),
WEEK(MONDAY)) AS event_week
FROM advanced.app_logs
),
-- 2단계: 사용자 상태 확인
user_status AS (
SELECT
user_pseudo_id,
event_week,
first_week,
-- 이전 방문 주차
LAG(event_week) OVER(PARTITION BY user_pseudo_id ORDER BY event_week) AS prev_week
FROM user_weeks
),
-- 3단계: 상태 분류
weekly_status AS (
SELECT
event_week,
user_pseudo_id,
CASE
WHEN event_week = first_week THEN 'New'
WHEN DATE_DIFF(event_week, prev_week, WEEK) = 1 THEN 'Current'
WHEN DATE_DIFF(event_week, prev_week, WEEK) > 1 THEN 'Resurrected'
END as user_type
FROM user_status
),
-- 4단계: 각 주차별 전체 유저 수
total_users AS (
SELECT
fwd.event_week,
COUNT(DISTINCT user_pseudo_id) AS total_user_count
FROM user_status
CROSS JOIN (SELECT DISTINCT event_week FROM user_weeks) AS fwd
WHERE first_week <= fwd.event_week
GROUP BY event_week
),
-- 5단계: 주차별 활성 유저 수 계산
active_users AS (
SELECT
event_week,
COUNTIF(user_type = 'New') AS new_users,
COUNTIF(user_type = 'Current') AS current_users,
COUNTIF(user_type = 'Resurrected') AS resurrected_users,
COUNT(DISTINCT user_pseudo_id) AS retain_users
FROM weekly_status
GROUP BY event_week
)
-- 6단계: 최종 결과
SELECT
FORMAT_DATE('%Y-%m-%d', a.event_week) as week_start,
new_users,
current_users,
resurrected_users,
(t.total_user_count - a.retain_users) as dormant_users,
ROUND(100.0 * current_users / NULLIF(LAG(new_users)
OVER(ORDER BY a.event_week), 0), 1) as retention_rate,
retain_users as active_users,
t.total_user_count as total_users
FROM active_users a
JOIN total_users t ON a.event_week = t.event_week
ORDER BY a.event_week;
WITH
-- 1단계: 기본 데이터 준비
user_weeks AS (
SELECT DISTINCT
user_pseudo_id,
DATE_TRUNC(MIN(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul"))
OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
DATE_TRUNC(DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul"),
WEEK(MONDAY)) AS event_week
FROM advanced.app_logs
),
-- 2단계: 사용자별 방문 주차 수 계산
user_visit_frequency AS (
SELECT
user_pseudo_id,
COUNT(DISTINCT event_week) as total_visit_weeks,
DATE_DIFF(MAX(event_week), MIN(event_week), WEEK) + 1 as weeks_since_first,
MIN(event_week) as first_visit_week
FROM user_weeks
GROUP BY user_pseudo_id
),
-- 3단계: 사용자별 리텐션 점수 계산
user_retention_score AS (
SELECT
user_pseudo_id,
total_visit_weeks,
weeks_since_first,
ROUND(100.0 * total_visit_weeks / weeks_since_first, 2) as visit_rate,
FORMAT_DATE('%Y-%m', first_visit_week) as cohort_month
FROM user_visit_frequency
WHERE weeks_since_first >= 4 -- 최소 4주 이상 경과된 사용자만
),
-- 4단계: 사용자 행동 데이터 분석
user_behavior AS (
SELECT
r.user_pseudo_id,
r.visit_rate,
r.cohort_month,
COUNT(DISTINCT DATE(TIMESTAMP_MICROS(l.event_timestamp), "Asia/Seoul")) as active_days,
COUNT(DISTINCT l.event_timestamp) as total_events,
COUNT(DISTINCT l.event_name) as unique_event_types
FROM user_retention_score r
JOIN advanced.app_logs l ON r.user_pseudo_id = l.user_pseudo_id
GROUP BY r.user_pseudo_id, r.visit_rate, r.cohort_month
),
-- 5단계: 리텐션 세그먼트별 행동 패턴 분석
retention_segments AS (
SELECT
cohort_month,
CASE
WHEN visit_rate >= 75 THEN 'Very High (75%+)'
WHEN visit_rate >= 50 THEN 'High (50-74%)'
WHEN visit_rate >= 25 THEN 'Medium (25-49%)'
ELSE 'Low (<25%)'
END as retention_segment,
COUNT(DISTINCT user_pseudo_id) as user_count,
ROUND(AVG(visit_rate), 2) as avg_retention_rate,
ROUND(AVG(active_days), 1) as avg_active_days,
ROUND(AVG(total_events), 1) as avg_total_events,
ROUND(AVG(unique_event_types), 1) as avg_unique_events
FROM user_behavior
GROUP BY
cohort_month,
CASE
WHEN visit_rate >= 75 THEN 'Very High (75%+)'
WHEN visit_rate >= 50 THEN 'High (50-74%)'
WHEN visit_rate >= 25 THEN 'Medium (25-49%)'
ELSE 'Low (<25%)'
END
)
-- 최종 결과
SELECT
cohort_month,
retention_segment,
user_count,
avg_retention_rate,
avg_active_days,
avg_total_events,
avg_unique_events,
ROUND(100.0 * user_count / SUM(user_count) OVER (PARTITION BY cohort_month), 2) as segment_percentage
FROM retention_segments
ORDER BY
cohort_month,
CASE retention_segment
WHEN 'Very High (75%+)' THEN 1
WHEN 'High (50-74%)' THEN 2
WHEN 'Medium (25-49%)' THEN 3
WHEN 'Low (<25%)' THEN 4
END;
WITH
-- 1단계: 결제 이벤트 기본 데이터
payment_base AS (
SELECT DISTINCT
user_pseudo_id,
DATE(TIMESTAMP_MICROS(event_timestamp), "Asia/Seoul") AS event_date
FROM advanced.app_logs
WHERE event_name = "click_payment"
),
-- 2단계: 사용자별 첫 결제일과 주차 계산
user_weeks AS (
SELECT
user_pseudo_id,
-- 첫 결제 주차
DATE_TRUNC(MIN(event_date) OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week,
-- 실제 결제 주차
DATE_TRUNC(event_date, WEEK(MONDAY)) AS event_week
FROM payment_base
),
-- 3단계: 주차별 상태 계산
week_status AS (
SELECT
user_pseudo_id,
event_week,
first_week,
-- 첫 주차와의 차이
DATE_DIFF(event_week, first_week, WEEK) AS week_number
FROM user_weeks
WHERE DATE_DIFF(event_week, first_week, WEEK) <= 12 -- 최대 12주까지만 분석
),
-- 4단계: 주차별 활성 사용자 수 계산
weekly_users AS (
SELECT
week_number,
COUNT(DISTINCT user_pseudo_id) AS active_users
FROM week_status
GROUP BY week_number
),
-- 5단계: 첫 주 사용자 수 (코호트 크기) 구하기
first_week_users AS (
SELECT
COUNT(DISTINCT user_pseudo_id) AS cohort_size
FROM week_status
WHERE week_number = 0
)
-- 최종 결과
SELECT
week_number as week,
active_users,
cohort_size,
ROUND(100.0 * active_users / cohort_size, 2) as retention_rate
FROM weekly_users
CROSS JOIN first_week_users
ORDER BY week_number;
총 사용자: 52,823명까지 증가
8월부터 12월까지 꾸준한 성장
10월에 가장 높은 신규 유저 유입 (4,048명)
전체 리텐션: 8월 2.6% → 1월 47.9%로 큰 폭 개선
12월부터 40% 이상의 안정적인 리텐션 유지
재방문 사용자 비중이 지속적으로 증가
결제 리텐션이 매우 낮음 (1% 수준)
신규 사용자 유입이 감소 추세
휴면 사용자가 지속적으로 증가
답변 1
0
안녕하세요, 인프런 AI 인턴입니다. 김세현님께서 제공한 여러 문제를 풀면서 리텐션 데이터를 분석하신 것 같습니다. 여기에 대한 유사한 질문과 해결 방법을 찾아보았지만, 해당 과제의 복잡성과 세부 사항에 맞춘 명확한 기존 답변을 직접적으로 찾기 어려웠습니다. 그럼에도 불구하고, 아래 제공된 몇 가지 정보를 통해 비슷한 맥락의 문제 해결을 위한 방향성을 제안드리겠습니다:
리텐션 데이터 분석의 기초: 리텐션 분석을 위한 적절한 SQL 쿼리를 통해 각 주차별로 사용자 활동을 추적하는 방법은 강의의 기본 사항입니다. 이때, 사용자 데이터를 파티셔닝하여 첫 방문 주차와 이후의 활동 주차를 비교하는 방식은 효과적인 리텐션 분석을 위한 핵심 기술입니다.
사용자 상태 분석: 유사한 질문에서 사용자 상태를 구분하기 위해 ‘New’, ‘Current’, ‘Resurrected’로 상태를 분리하여 분석하는 방법이 설명된 바 있습니다. 이는 리텐션 과제_연습문제 2번에서 다룬 내용과 비슷합니다. 해당 질문의 답변을 통해 상태 분류를 보다 명확하게 이해할 수 있을 것입니다.
세그먼트 분석: 리텐션 세그먼트를 월별로 구분하고, 사용자의 행동 패턴에 따라 세분화하는 과정은 리텐션을 더욱 깊이있게 이해하는 데 필수적입니다. 이를 통해 각 세그먼트 별로 사용자 행동을 관찰하고, 전략적인 인사이트를 도출할 수 있습니다. 이러한 접근은 같은 강의에서 제공된 예제를 잘 참조하여 학습할 수 있습니다.
결제 리텐션: 이는 주차별 결제 사용자 수와 처음 결제를 한 사용자 수를 비교하여 리텐션 값을 계산하는 방법입니다. 만약 결제 리텐션이 낮다면, 이는 전환율 개선 및 마케팅 전략 수정이 필요할 수 있다는 것을 의미할 수 있습니다. 리텐션 과제 연습문제 4번(core_event)와 같은 문제는 결제 관련 지표를 어떻게 설정하고 해석할지를 탐구하며, 저조한 전환율을 개선할 수 있는 전략적 인사이트를 제공합니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.