LabHub
시작하기
배우기 러닝패스 코스

개발자의 창업 — 만들기 전에 검증하기

유지율 삼각형을 세우고 세 함정을 숫자로 본다

LabHub 에서 이어서 보기

목표

'모아노트' 의 주간 가입 코호트로 유지율 삼각형을 만들고, 분모·빈칸·생존자라는 세 함정을 숫자로 확인한 뒤 올바른 가중 유지율과 가입 경로별 유지율로 판단한다.

왜 중요한가

전체 활성 수는 새로 들어오는 사람과 떠나는 사람을 한 숫자에 섞는다. 같은 주에 가입한 사람들을 따로 따라가야 제품이 사람을 붙잡는지 보인다. 그리고 그 계산은 분모를 바꾸거나 빈칸을 0 으로 채우는 것만으로 쉽게 좋아지거나 나빠진다 — 광고비와 로드맵이 걸린 숫자라 정의대로 다시 계산되어야 한다.

재료와 정의

단계

  1. /root/founder/cohort/sizes.json 에 코호트별 가입자 수를 {"0": n, ..., "11": n} 으로 쓴다.
  2. /root/founder/cohort/cohort.pyretention(fix, max_n) 을 만든다 — {"0": [r0, r1, ..., r_max_n], ...}, 관측 전 칸은 None.
  3. /root/founder/cohort/denominator.json 에 코호트 4 의 4주차 유지율을 두 분모로 쓴다: rate_cohort_size(올바른 분모), rate_week0_active(0주차 활동자를 분모로 — 일부러 저지르는 실수).
  4. /root/founder/cohort/censor.json 에 6주차 유지율을 두 방법으로 쓴다: pooled_rate(관측이 끝난 코호트만, 사람 수 가중), naive_rate_with_zeros(모든 코호트, 관측 전 칸을 0 으로), 그리고 eligible_cohorts(관측이 끝난 코호트 수).
  5. /root/founder/cohort/survivor.json 에 4주차 유지율을 두 명단으로 쓴다: all_users_rate(코호트 0~11 전원, 관측이 끝난 코호트만), survivors_only_rate(15주차에 활동한 사람만 골라 같은 계산), survivors(그 사람 수).
  6. cohort.pypooled(fix, n, unbounded=False) 를 더한다 — 관측이 끝난 코호트만 모은 사람 수 가중 유지율 {"cohorts": k, "users": u, "retained": r, "rate": 0.0000}. unbounded 가 참이면 "c+N 주차 이후 아무 주에나(c+N 포함) 활동" 으로 센다.
  7. /root/founder/cohort/channel.json 에 가입 경로(마지막 접점)별 4주차 가중 유지율을 {"organic_search": r, ...} 다섯 채널로 쓴다.
  8. /root/founder/cohort/report.jsoncurve(n=0..8 의 bounded 가중 유지율 9개), week4, week4_unbounded, flatten_week(전 주 대비 하락이 0.02 미만이 되는 첫 n, 1 이상), best_channel, worst_channel 을 쓴다.

참고

코호트별 가입자 수

/root/founder/cohort/sizes.json 에 가입 주차(0~11)별 외부 가입자 수를 문자열 키로 쓴다.

signup_at 을 서울 시각으로 바꿔 2026-03-02 로부터 며칠인지 7 로 나눈 몫이 코호트입니다. 내부 계정은 뺍니다.

유지율 삼각형 — 관측 전 칸은 None

/root/founder/cohort/cohort.py 에 retention(fix, max_n) 을 만든다. 코호트 문자열 키 → n=0..max_n 유지율 목록, c+n>15 인 칸은 None.

사람별로 session_start 가 있던 주차 집합을 먼저 만드세요. 분모는 코호트 가입자 수이고, 관측이 끝나지 않은 칸은 0 이 아니라 None 입니다.

분모를 바꾸면 유지율이 좋아 보인다

/root/founder/cohort/denominator.json 에 코호트 4 의 4주차 유지율을 rate_cohort_size 와 rate_week0_active 두 분모로 쓴다.

분자는 같습니다(주차 8 에 활동한 코호트 4 사람). 분모만 가입자 수와 0주차(주차 4) 활동자 수로 바꿔 봅니다.

관측 전 칸을 0 으로 채우면 곡선이 가짜로 꺾인다

/root/founder/cohort/censor.json 에 6주차 유지율의 pooled_rate·naive_rate_with_zeros·eligible_cohorts 를 쓴다.

올바른 쪽은 c+6 ≤ 15 인 코호트만 모아 전체 유지자 ÷ 전체 가입자입니다. 틀린 쪽은 모든 코호트의 가입자를 분모에 넣습니다(관측 전 사람은 유지 0 으로 세어짐).

지금 남은 사람만 보면 과거가 좋아 보인다

/root/founder/cohort/survivor.json 에 4주차 가중 유지율을 all_users_rate(전원)와 survivors_only_rate(15주차 활동자만)로, 그리고 survivors(그 사람 수)를 쓴다.

두 계산은 코호트·주차 규칙이 같고 사람 명단만 다릅니다. 명단은 가입 시점에 고정하는 것이 원칙이라, 두 번째 숫자는 일부러 저지르는 실수입니다.

가중 유지율 함수 — bounded 와 unbounded

cohort.py 에 pooled(fix, n, unbounded=False) 를 더한다. 관측이 끝난 코호트만, 사람 수 가중, {cohorts, users, retained, rate} 를 돌려준다.

코호트 비율의 단순 평균이 아니라 유지자 합 ÷ 가입자 합입니다. unbounded 는 c+n 주차와 그 뒤 어느 주에든 활동했는지를 봅니다.

가입 경로별 4주차 유지율

/root/founder/cohort/channel.json 에 마지막 접점 채널 다섯 개 각각의 4주차 가중 유지율을 쓴다.

touches.csv 에서 사람마다 ts 가 가장 늦은 줄의 channel 이 가입 경로입니다. 명단만 그 채널 사람으로 좁혀 pooled 와 같은 계산을 합니다.

곡선과 판단을 한 장에

/root/founder/cohort/report.json 에 curve(n=0..8), week4, week4_unbounded, flatten_week, best_channel, worst_channel 을 쓴다.

flatten_week 는 n=1 부터 보며 curve[n-1] − curve[n] < 0.02 인 첫 n 입니다. 채널은 7단계 결과에서 가장 높은 것과 낮은 것입니다.