Условие
Сервис стримов берёт 499 ₽/мес с подписчика. Маркетинг хочет знать LTV нового пользователя за 24 месяца, чтобы понять допустимый CAC. У вас есть retention curve по 12 месяцам:
m1=0.65, m2=0.50, m3=0.42, m4=0.37, m5=0.34, m6=0.31,
m7=0.29, m8=0.27, m9=0.26, m10=0.25, m11=0.24, m12=0.23
(доля от месяца 0, который = 100% по построению — все, кто оплатил первый месяц).
- Прогнозируйте retention на месяцы 13-24 (экстраполяция).
- Посчитайте 12m / 24m LTV (gross, без discount rate).
- Посчитайте 24m discounted LTV при ставке 10% годовых.
Решение
1) Экстраполяция retention
Retention обычно затухает как power law или exponential decay. Подгоняем оба:
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
months = np.arange(1, 13)
ret = np.array([0.65, 0.50, 0.42, 0.37, 0.34, 0.31,
0.29, 0.27, 0.26, 0.25, 0.24, 0.23])
# Power law: r(t) = a * t^(-b)
def power_law(t, a, b):
return a * t**(-b)
# Exponential: r(t) = a * exp(-b*t)
def exp_decay(t, a, b):
return a * np.exp(-b * t)
popt_pow, _ = curve_fit(power_law, months, ret, p0=(0.7, 0.4))
popt_exp, _ = curve_fit(exp_decay, months, ret, p0=(0.7, 0.1))
future = np.arange(13, 25)
ret_pow_future = power_law(future, *popt_pow)
ret_exp_future = exp_decay(future, *popt_exp)
# Сравним MAPE на исторических
mape_pow = np.mean(np.abs(power_law(months, *popt_pow) - ret) / ret) * 100
mape_exp = np.mean(np.abs(exp_decay(months, *popt_exp) - ret) / ret) * 100
print(f"power MAPE: {mape_pow:.2f}% exp MAPE: {mape_exp:.2f}%")Для подписочных сервисов power law обычно лучше — «хвост» дольше. Выбираем модель с меньшим MAPE на in-sample (и проверяем на out-of-time, если данных хватит).
2) LTV gross
LTV_12m = price × (m0 + Σ retention_t) = price × Σ_{t=0..12} r_t, r_0 = 1
Месяц 0 — все 100% оплачивают (первый платёж). Дальше — m1, m2, ....
PRICE = 499
# месяц 0 = 1.0, месяцы 1..12 — реальные данные, 13..24 — прогноз
ret_full = np.concatenate(([1.0], ret, ret_pow_future))
ltv_12 = PRICE * ret_full[:13].sum()
ltv_24 = PRICE * ret_full.sum()
print(f"LTV 12m = {ltv_12:.0f} ₽") # ≈ 499 * (1+0.65+0.50+...+0.23) = ~2580
print(f"LTV 24m = {ltv_24:.0f} ₽") # ≈ ~37003) Discounted LTV (NPV)
Ставка 10% годовых → месячная ≈ (1+0.10)^(1/12) − 1 ≈ 0.00797. Или часто упрощают: 0.10 / 12 ≈ 0.00833.
ANNUAL = 0.10
m_rate = (1 + ANNUAL)**(1/12) - 1 # ≈ 0.00797
t = np.arange(0, 25) # 0..24
df_factor = 1 / (1 + m_rate)**t
ltv_24_disc = PRICE * (ret_full * df_factor).sum()
print(f"LTV 24m discounted = {ltv_24_disc:.0f} ₽")Дисконт «съест» 5-10% за 24 месяца — типичный эффект.
CAC threshold
Если CAC = 1500 ₽:
- 12m LTV ≈ 2580 ₽ → ROI = 72%, окупаемость за ~6 мес — ок.
- Если CAC = 3000 ₽ → не окупается за 24 мес → канал в минусе.
Менеджеры обычно требуют LTV / CAC ≥ 3 на горизонте 24 мес.
Альтернатива: BG/NBD + Gamma-Gamma
Для non-subscription моделей (e-commerce) используют статистические модели lifetimes из библиотеки lifetimes:
from lifetimes import BetaGeoFitter, GammaGammaFitter
# ... подбирают на RFM-данныхЗдесь подписка → проще: retention curve достаточно.
Подводные камни
- Месяц 0 vs месяц 1: в задаче
m1= месяц 1 (доля, оставшаяся после первого месяца). Месяц 0 — все, кто оплатил первый платёж (100%). - Экстраполяция power law на 100 месяцев даёт бесконечный LTV — опасно. Обрезать на 24-36 мес или использовать модели с плато.
- Линейная экстраполяция retention → негативные значения за 24 мес.
- Inflation vs discount rate: 10% — часто номинальный; для реального NPV учитывают инфляцию (3-7%/год).
- Cohort heterogeneity: новые когорты могут иметь разный shape. Считать LTV на актуальной cohort, не на агрегате 5 лет назад.
- Refund / churn-during-month: если есть возвраты —
revenue ≠ price × retention. - «Predict 24m по 12m» требует допущения о стабильности retention curve. Если продукт меняется — экстраполяция врёт.
Эталонный ответ
ret_full = np.concatenate(([1.0], ret, power_law(np.arange(13,25), *popt_pow)))
LTV_24 = 499 * ret_full.sum() # gross
LTV_24_NPV = 499 * (ret_full / (1 + (1.1)**(1/12) - 1) ** np.arange(25)).sum()Шаги: (1) подогнать power law на 12 месяцев retention; (2) экстраполировать до 24; (3) LTV = price × sum(retention) с month=0..24 (где r_0 = 1); (4) дисконт = делим на (1+m_rate)^t. Подвох — power law даёт бесконечность при extrapolation, надо обрезать.