인프런 커뮤니티 질문&답변

hayeonkimmie님의 프로필 이미지
hayeonkimmie

작성한 질문수

BigQuery(SQL) 활용편(퍼널 분석, 리텐션 분석)

[빠짝스터디 3주차 과제] 리텐션 연습문제

작성

·

28

0

 

문제 1) Weekly Retention을 구하는 쿼리를 바닥부터 스스로 작성해보세요

WITH base AS (
  SELECT
    DISTINCT
      user_pseudo_id,
      DATE_TRUNC(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul'), WEEK(MONDAY)) AS event_week,
      DATE_TRUNC(MIN(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')) OVER(PARTITION BY user_pseudo_id), WEEK(MONDAY)) AS first_week
  FROM `advanced.app_logs`
),
first_week_diff AS (
  SELECT
    user_pseudo_id,
    DATE_DIFF(event_week, first_week, WEEK) AS diff_of_week
  FROM base
),
user_counts AS (
  SELECT
    diff_of_week,
    COUNT(DISTINCT user_pseudo_id) AS user_count
  FROM first_week_diff
  GROUP BY diff_of_week
)
SELECT
  diff_of_week,
  user_count,
  ROUND(SAFE_DIVIDE(user_count, FIRST_VALUE(user_count) OVER (ORDER BY diff_of_week ASC)), 2) AS retention_rate
FROM user_counts
ORDER BY diff_of_week;

문제 2) Retain User를 New + Current + Resurrected + Dormant User로 나누는 쿼리를
작성해보세요.
WITH event_data AS (
    SELECT DISTINCT 
        DATE(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')) AS event_date,
        user_pseudo_id
    FROM advanced.app_logs
),

user_visits AS (
    SELECT 
        user_pseudo_id,
        MIN(event_date) AS first_visit,
        MAX(event_date) AS last_visit -- 최근 방믄
    FROM event_data
    GROUP BY user_pseudo_id
),

-- 최신일자 
current_date AS (
    SELECT MAX(event_date) AS max_date 
    FROM event_data
),

user_types AS (
    SELECT 
        u.user_pseudo_id,
        u.first_visit,
        u.last_visit,
        c.max_date as current_date,
        DATE_DIFF(c.max_date, u.last_visit, DAY) as days_since_last_visit,
        CASE 
            WHEN DATE_DIFF(c.max_date, u.last_visit, DAY) <= 14 
            THEN 'current' -- 최근 2주 내 방문
            
            WHEN DATE_DIFF(c.max_date, u.last_visit, DAY) > 60 
            THEN 'dormant' -- 60일 이상 미접속
            
            WHEN DATE_DIFF(c.max_date, u.last_visit, DAY) > 30 
                 AND DATE_DIFF(c.max_date, u.last_visit, DAY) <= 44
                 AND EXISTS (
                     SELECT 1 
                     FROM event_data e 
                     WHERE e.user_pseudo_id = u.user_pseudo_id
                     AND DATE_DIFF(c.max_date, e.event_date, DAY) <= 14
                 )
            THEN 'resurrected' -- 30일 이상 비활동 후 최근 14일 내 재접속
            
            WHEN DATE_DIFF(c.max_date, u.last_visit, DAY) > 60 
            THEN 'inactive' -- 60일 이상 비활동
            
            ELSE 'none'
        END AS user_type
    FROM user_visits u
    CROSS JOIN current_date c
),

weekly_retention AS (
    SELECT 
        ut.user_type,
        DATE_DIFF(DATE_TRUNC(ed.event_date, WEEK), DATE_TRUNC(ut.first_visit, WEEK), WEEK) as week_number,
        COUNT(DISTINCT ed.user_pseudo_id) as user_count
    FROM event_data ed
    JOIN user_types ut ON ed.user_pseudo_id = ut.user_pseudo_id
    GROUP BY 1, 2
),

retention_rates AS (
    SELECT 
        user_type,
        week_number,
        user_count,
        FIRST_VALUE(user_count) OVER (
            PARTITION BY user_type 
            ORDER BY week_number
        ) as initial_users
    FROM weekly_retention
)

SELECT 
    user_type,
    week_number,
    user_count as active_users,
    initial_users as cohort_size,
    ROUND(user_count * 100.0 / initial_users, 2) as retention_rate
FROM retention_rates
ORDER BY user_type, week_number;

문제 3) 주어진 데이터에서 어떤 사람들이 리텐션이 그나마 높을까요? 찾아보세요

- current의 경우 15주까지는 리텐션 유지 및 증가하며 큰 변동 없다가 그 후부터 지속적으로 하락하였다.
- current는 그래도 리텐션율이 9.X로 시작했으나 dormant는 7.X로 시작하였고 더 빠르게 유입율이 감소하며 리텐션이 좋지 않았다.

문제 4) Core Event를 “click_payment”라고 설정하고 Weekly Retention을 구해주세요
WITH base AS (
  SELECT 
    user_pseudo_id,
    DATE_TRUNC(DATE(DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')), WEEK(MONDAY)) as week
  FROM advanced.app_logs 
  WHERE event_name = "click_payment"
),
cal_week AS (
  SELECT 
    user_pseudo_id,
    week as visit_week,
    MIN(week) OVER (PARTITION BY user_pseudo_id) as first_week
  FROM base
),
weekly_retention AS (
  SELECT 
    first_week 
    DATE_DIFF(visit_week, first_week, WEEK) as week_number,
    COUNT(DISTINCT user_pseudo_id) as user_count
  FROM cal_week
  GROUP BY first_week, DATE_DIFF(visit_week, first_week, WEEK)
),
cohort_sizes AS (
  SELECT 
    first_week,
    MAX(CASE WHEN week_number = 0 THEN user_count END) as initial_users
  FROM weekly_retention
  GROUP BY cohort_week
)
SELECT 
  wr.first_week,
  cs.initial_users as total_users,
  wr.week_number,
  wr.user_count as active_users,
  ROUND(wr.user_count * 100.0 / cs.initial_users, 2) as retention_rate
FROM weekly_retention wr
JOIN cohort_sizes cs ON wr.cohort_week = cs.cohort_week
WHERE wr.week_number >= 0
ORDER BY wr.cohort_week, wr.week_number;

답변 1

0

안녕하세요, 인프런 AI 인턴입니다. hayeonkimmie님께서 질문하신 리텐션 분석 관련 SQL 쿼리에 대해 도움이 될 수 있는 기존 자료들을 찾아보았습니다.

  1. 리텐션 SQL 쿼리 작성하기에서 리텐션 분석 관련 쿼리를 작성하는 방법을 실습할 수 있습니다. 이 자료에서는 주간 및 월간 리텐션을 구하는 쿼리를 포함하고 있어, 리텐션 쿼리 작성의 기초와 응용을 배우기에 유용할 것입니다.

  2. 리텐션 연습 문제 설명에서 제시한 여러 유형의 사용자 그룹(신규, 현재, 복귀, 휴면 사용자)에 대한 코호트 분석 방법을 참고할 수 있습니다. 쿼리 작성 시 기준을 세우는 데 실질적인 도움이 될 것입니다.

  3. SQL DISTINCT 사용 이해하기 질문에서는 DISTINCT를 사용하는 목적과 실행 결과의 차이를 설명하고 있으니 쿼리의 중복 여부에 대한 이해를 돕는 데 유용할 것입니다.

이 자료들은 SQL 쿼리를 통해 효과적으로 데이터를 분석하고 리텐션 분석을 구현하는 데에 도움을 줄 것입니다. hayeonkimmie님께서 작성하신 쿼리에 대한 이해를 높이고, 최적화할 수 있는 힌트를 제공할 수 있을 것입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

hayeonkimmie님의 프로필 이미지
hayeonkimmie

작성한 질문수

질문하기