Case · A/B

Cookie Cats: перенос гейта 40→30 ↑ D7 (A/B-тест)

Статистически значимый рост удержания D7 при gate_30 → раскатка победителя.

Роль: Product/Data AnalystТаймлайн: 5 дней (подготовка → анализ → рекомендации)Статус: prodA/BSQLPostgreSQL (Docker)Python/statsmodels+0.82 п.п. D7 (p=0.0016)

Вариант gate_30 даёт +0.82 п.п. к удержанию D7 (19.02% vs 18.20%, p=0.0016+4.5% относит.). Рекомендовано раскатить gate_30 на 100% с пост-мониторингом.

D7: +0.82 п.п. (p=0.0016)Выборка: gate_30 = 44 700; gate_40 = 45 489

Контекст

Free-to-play головоломка. «Гейт» — обязательная задержка/барьер; позиция влияет на ритм игры и удержание.

Задача

Проверить гипотезу: перенос «гейта» с 40 → 30 уровня улучшит вовлечённость и удержание D7.

Моя роль

  • Сформулировал метрики (D1/D7), дизайн теста, критерии успеха.
  • Построил ETL: CSV → PostgreSQL (Docker) → агрегации SQL.
  • Провёл проверку значимости (Z-тест разности долей).
  • Сформировал план раскатки и пост-мониторинга.

Данные и подготовка

Исторические события по пользователям (CSV) загружаются в базу данных и агрегируются SQL (считаем размер групп и удержание).

Показать технические детали (SQL)
Агрегирование метрик
SELECT 
  version,
  COUNT(userid)                         AS total_users,
  SUM(CAST(retention_1 AS int))         AS d1_retained,
  SUM(CAST(retention_7 AS int))         AS d7_retained,
  100.0 * SUM(CAST(retention_1 AS int)) / COUNT(*) AS d1_rate,
  100.0 * SUM(CAST(retention_7 AS int)) / COUNT(*) AS d7_rate
FROM cookie_cats
GROUP BY version;

Сводные метрики (из БД)

ГруппаПользователиD1 (%, agg)D7 (%, agg)
gate_3044 70044.8219.02
gate_4045 48944.2318.20

Методология (простыми словами)

Мы сравнили долю пользователей, вернувшихся на 7-й день, между группами gate_30 и gate_40.
Использовали Z-тест разности долей: он отвечает, насколько вероятно увидеть такое превосходство gate_30, если на самом деле разницы нет. Получили p=0.0016 — это значительно ниже 0.05, значит выигрыш статистически значим.

Показать расчёты (Python)
Проверка значимости
from statsmodels.stats.proportion import proportions_ztest
 
count = [8502, 8279]    # удержались на D7: gate_30, gate_40
nobs  = [44700, 45489]  # всего пользователей: gate_30, gate_40
z, p = proportions_ztest(count, nobs, alternative='larger')
print(p)  # ≈ 0.0016

Результаты → решение

  • D7: +0.82 п.п. (19.02% vs 18.20%), p=0.0016 — значимо.
  • D1: +0.59 п.п., p≈0.074 — тренд без значимости.

Рекомендации:

  1. Раскатить gate_30 на 100%.
  2. Мониторить: D1/D7, активные раунды, воронка IAP/ADS, SRM/сезонность (1–2 цикла апдейтов).
  3. Rollback-критерии: деградация D7/денежных метрик за пределы доверительных интервалов.

Риски/ограничения

  • Микс трафика и сезонность могут смещать эффект.
  • Влияние на монетизацию проявляется с лагом (нужно пост-наблюдение).

Стек и запуск

Ниже — краткие шаги для воспроизведения результатов. Для менеджеров этого блока можно не касаться — он для инженера/аналитика.

Показать шаги запуска (локально)
Локально: запуск анализа
docker compose up -d
pip install -r requirements.txt
python load_data.py
# анализ — в analysis.ipynb
Готовы подключиться

Нужно обсудить похожий кейс?

Расскажите коротко о задаче — вернёмся с предложением в течение дня. Открыты к пилотам, MVP и постоянной поддержке.