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

병호르님의 프로필 이미지
병호르

작성한 질문수

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

[인프런 빅쿼리 빠짝스터디 3주차] 리텐션 연습문제

작성

·

34

0

1번

with base as (
  select 
  user_pseudo_id,
  DATE_TRUNC(MIN(event_date) OVER (PARTITION BY user_pseudo_id), WEEK(MONDAY)) as event_first_week,   
  DATE_TRUNC(event_date,WEEK(monday)) as event_week, 
  DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')  as event_time
  from advanced.app_logs
) 

, diff_date_tbl as (
  select user_pseudo_id, 
  event_first_week, 
  event_week,
  DATE_DIFF(event_week, event_first_week, WEEK) as diff_date 
  from base 
  order by diff_date

) 
, user_counts as (
  select event_first_week, 
  diff_date, 
  count(distinct user_pseudo_id) as user_count
  from diff_date_tbl
  group by all 
  order by diff_date
) 

select * , 
ROUND(SAFE_DIVIDE(user_count, first_user_count), 2)  as retention_rate
from (
  select * , 
  FIRST_VALUE(user_count) OVER (PARTITION BY event_first_week ORDER BY diff_date) as first_user_count
  from user_counts 
) 

2번

with base as (
  select 
  user_pseudo_id,
  MIN(event_date) OVER (PARTITION BY user_pseudo_id) as event_first_day,   
  event_date, 
  DATETIME(TIMESTAMP_MICROS(event_timestamp), 'Asia/Seoul')  as event_time
  from advanced.app_logs
) 

, period as (
  select user_pseudo_id, 
  event_date, 
  LAG(event_date) OVER (PARTITION BY user_pseudo_id ORDER BY event_date) as before_event_date
  from base 
  GROUP BY ALL 
  ORDER BY user_pseudo_id
) 

-- 평균주기 구하기 
SELECT AVG(diff_day) as avg_diff_period
FROM (
select *, 
DATE_DIFF(event_date, before_event_date, DAY ) as diff_day 
from period
) 

-- 접속 주기 분포도 확인 

SELECT diff_day, 
COUNT(distinct user_pseudo_id) as user_count
FROM (
  select *, 
  DATE_DIFF(event_date, before_event_date, DAY ) as diff_day 
  from period
)
GROUP BY ALL 
ORDER BY diff_day 

1) new 유저 =  (최근월 - 1)월 = 첫접속일자(event_first_month) 
2) current 유저 =  (최근월 -1)월 , (최근월 -2)월 모두 활동 o 
3) dormant 유저 =  (최근월 - 1)월 , (최근월 -2)월 모두 활동 x
4) resurrected  유저 = (최근월 -2)월 활동 x, (최근월 -1)월 에는 활동

with base as (
select user_pseudo_id, 
event_date, 
DATE_TRUNC(event_date, MONTH) as event_month
from advanced.app_logs
order by 1 
) 

, min_max_month as (
  SELECT *, 
  MIN(event_month) over (partition by user_pseudo_id order by event_month) as first_month,
  MAX(DATE_TRUNC(event_date, MONTH)) OVER () as lastest_month
  from base 
) 

, filter_month  as (
  SELECT user_pseudo_id, 
  first_month, 
  lastest_month, 
  COUNT(CASE WHEN event_month = DATE_SUB(lastest_month, INTERVAL 1 MONTH) THEN 1 END) AS previous_1_lastest_month,
  COUNT(CASE WHEN event_month = DATE_SUB(lastest_month, INTERVAL 2 MONTH) THEN 1  END)  AS previous_2_lastest_month  
  FROM min_max_month 
  group by ALL 
) 

, user_classification as (
  SELECT *, 
  CASE WHEN first_month = DATE_SUB(lastest_month, INTERVAL 1 MONTH ) THEN 'New'
       WHEN previous_1_lastest_month > 0 and previous_2_lastest_month >= 0 THEN 'Current'
       WHEN previous_1_lastest_month = 0 and previous_2_lastest_month = 0 THEN 'Dormant'
       WHEN previous_1_lastest_month = 0 and previous_2_lastest_month > 0 THEN 'Resurrected'
       ELSE 'Others' END AS user_category
  FROM filter_month 
) 

select user_category, 
count(distinct user_pseudo_id)
from user_classification 
group by all 

4번

WITH base AS (
SELECT
    user_pseudo_id
  , first_value(datetime(timestamp_micros(event_timestamp), 'Asia/Seoul')) over(partition by user_pseudo_id order by event_timestamp) as first_visit
  , last_value(datetime(timestamp_micros(event_timestamp), 'Asia/Seoul')) over(partition by user_pseudo_id order by event_timestamp) as last_visit
  , datetime(timestamp_micros(event_timestamp), 'Asia/Seoul') as event_datetime
  , date_trunc(datetime(timestamp_micros(event_timestamp), 'Asia/Seoul'), day) as event_date
  , date_trunc(datetime(timestamp_micros(event_timestamp), 'Asia/Seoul'), week(monday)) as event_week
  , date_trunc(datetime(timestamp_micros(event_timestamp), 'Asia/Seoul'), month) as event_month
  , event_name
FROM advanced.app_logs
), click_payment AS (
SELECT DISTINCT user_pseudo_id
              , min(event_week) over(partition by user_pseudo_id) as first_week
              , event_week as week_date
FROM base
WHERE event_name = 'click_payment'
), diff_week AS (
SELECT
  first_week, 
  date_diff(week_date, first_week, week) as week_diff,
  count(distinct user_pseudo_id) as users_cnt
FROM click_payment
GROUP BY ALL
)
SELECT first_week
    , week_diff
    , users_cnt
    , safe_divide(users_cnt, first_value(users_cnt) over(partition by first_week order by week_diff)) as retention_rate
FROM  diff_week
order by 1, 2
;

답변 1

0

안녕하세요, 인프런 AI 인턴입니다. 병호르님의 질문과 관련해서 리텐션 분석을 위한 SQL 쿼리에 대한 설명과 참고할 수 있는 자료를 안내해 드리겠습니다.

리텐션 분석은 데이터 분석에서 자주 사용되며, 특정 시점의 사용자 중 일정 기간 후에도 남아 있는 사용자의 비율을 계산하는 과정입니다. 주어진 쿼리는 주차별 리텐션을 계산하고 있으며, 다양한 시나리오에 대한 사용자 분류를 포함하고 있습니다.

  1. 리텐션 계산의 핵심 개념:

    • 주차별로 최초 방문 시점과 현재 주차의 차이를 계산하여 얼마나 많은 사용자가 남아 있는지를 비율로 나타냅니다.
    • FIRST_VALUE 함수는 주차별 첫 사용자 수(first_user_count)를 얻어 각 주차별 사용자의 수와 비교합니다.
  2. 사용자 분류:

    • 새로운 사용자(New), 현재 사용자(Current), 휴면 사용자(Dormant), 복귀 사용자(Resurrected)로 분류됩니다.
    • 각 분류는 특정 월이나 주 기준으로 활동 유무를 파악하여 결정됩니다.

이러한 분석을 통해 사용자 유지율을 향상시키기 위한 전략을 도출할 수 있습니다.

유사한 주제에 관해 자세한 설명과 예제는 다음 URL에서 참조하실 수 있습니다:
- 3-7. 리텐션 SQL 쿼리 작성 및 질문
이 게시물에서는 주차별 리텐션 쿼리에 대한 더 나은 작성 방법과 이벤트 주차 간 사용자 집계 기준에 대한 논의가 이루어졌습니다.

  • 리텐션 SQL 작성하기
    이 답변에서는 DISTINCT의 사용 이유와 유니크한 사용자 집계의 중요성 등에 대해 설명하면서 리텐션 쿼리를 더욱 최적화할 수 있는 팁을 제공하고 있습니다.

병호르님이 작성하신 SQL 쿼리를 통해 분석하고자 하는 데이터의 흐름을 이해하고 이를 바탕으로 실무에서 적용 가능한 인사이트를 얻는 것이 중요합니다.

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

병호르님의 프로필 이미지
병호르

작성한 질문수

질문하기