Это четвёртая инструкция серии про пет-проекты аналитика. Общий контекст — зачем нужен проект, где брать данные, как упаковать — в хабе серии. Остальные инструкции: A/B-тест с нуля, когортный анализ и retention, дашборд продаж в DataLens. Код — в репозитории серии, папка 04-product-funnel.
Что собираем
Пользователь проходит путь visit → signup → activate → purchase. Три вопроса проекта: на каком шаге теряем больше всего людей, какая сквозная конверсия из визита в покупку и какой канал привлечения приводит платящих пользователей, а какой — только трафик.
Воронка — базовый инструмент продуктового аналитика, и на собеседованиях она всплывает в двух видах: «посчитайте конверсию» (техника) и «куда смотреть в первую очередь» (мышление). Проект тренирует оба, а третий его слой — разрез по каналам — закрывает ещё и маркетинговую часть: связку CAC-логики с конверсией.
Данные: синтетические события продукта, np.random.seed(21), 15 000 пользователей, 24 951 событие. У каждого пользователя канал привлечения (organic / ads / referral) и время первого визита в окне 90 дней; события идут с задержками в часах. У каждого канала своя «сила» на каждом переходе — referral конвертит лучше, ads хуже, как это обычно и бывает. Стек: pandas, numpy, matplotlib. Время: 2–3 часа. Цифры ниже — из реального прогона.
Шаг 1. Сформулировать воронку
Зачем: воронка — это упорядоченный список шагов, и порядок фиксируется до любого кода. Визит → регистрация → активация → покупка. «Активация» здесь — первое целевое действие в продукте после регистрации; в реальном проекте определение активации само по себе аналитическая задача, и то, что вы это проговариваете, — плюс на собеседовании.
Что должно получиться: четыре шага, выписанные по порядку. Это скелет всего анализа.
Шаг 2. Придумать модель данных
Зачем: решение «одна строка = одно событие» определяет всё дальнейшее. Событийная модель (user_id, channel, event, timestamp) — стандарт продуктовой аналитики: из неё воронка считается и целиком, и в любом разрезе.
Что должно получиться: схема двух файлов — events.csv по строке на событие и users.csv по строке на пользователя с флагами did_signup / did_activate / did_purchase. Двойное хранение осознанное: события — источник правды, сводка по людям — удобство расчёта.
Шаг 3. Сгенерировать данные с фиксированным seed
Зачем: воспроизводимость — любой прогон даёт одни и те же числа, проект можно проверить. Раздаём пользователям каналы по долям трафика и время визита в окне 90 дней.
import numpy as np
np.random.seed(21)
N = 15_000
channels = np.random.choice(["organic", "ads", "referral"],
size=N, p=[0.50, 0.35, 0.15])
Что должно получиться: 15 000 пользователей с каналами и временем первого визита.
Шаг 4. Прогнать людей по шагам
Зачем: главное правило воронки зашивается в генерацию — на следующий шаг попадают только прошедшие предыдущий. Для каждого перехода берём базовую конверсию, умножаем на силу канала и случайно решаем, прошёл человек дальше или отвалился.
BASE = {"signup": 0.39, "activate": 0.54, "purchase": 0.30}
POWER = {"referral": 1.25, "organic": 1.0, "ads": 0.85} # сила канала
alive = np.ones(N, dtype=bool)
flags = {}
for step, base_p in BASE.items():
p = np.array([base_p * POWER[c] for c in channels])
passed = alive & (np.random.rand(N) < p)
flags["did_" + step] = passed
alive = passed
Что должно получиться: у каждого пользователя монотонные флаги — нельзя купить, не активировавшись. Проверка этой монотонности — первый санити-чек анализа.
Шаг 5. Сохранить в CSV
Зачем: генерация и анализ разделены — анализ читает готовые файлы, как читал бы выгрузку из продуктовой БД. События пишутся в data/events.csv (24 951 строка), сводка — в data/users.csv.
Что должно получиться: два файла, дальше работаем только с ними.
Шаг 6. Посчитать воронку
Зачем: ядро проекта. Для каждого шага считаем, сколько людей дошло, — отсюда три набора цифр: конверсия шаг к шагу, конверсия от визита и drop-off на каждом шаге.
import pandas as pd
users = pd.read_csv("data/users.csv")
steps = ["visit", "signup", "activate", "purchase"]
counts = [len(users),
users["did_signup"].sum(),
users["did_activate"].sum(),
users["did_purchase"].sum()]
for i, (s, c) in enumerate(zip(steps, counts)):
step_conv = c / counts[i - 1] if i else 1.0
overall = c / counts[0]
print(f"{s:9s} {c:6d} шаг: {step_conv:6.1%} от визита: {overall:6.1%}")
Что должно получиться:
step count step_conv overall drop_rate
visit 15000 100.0% 100.0% 0.0%
signup 5839 38.9% 38.9% 61.1%
activate 3175 54.4% 21.2% 45.6%
purchase 937 29.5% 6.2% 70.5%
Сквозная конверсия visit -> purchase: 6.25%
Шаг 7. Найти узкое место
Зачем: воронка посчитана — теперь вывод. И здесь главная развилка проекта: узких мест два, смотря как считать.
В абсолюте больше всего людей теряется на первом шаге: visit → signup не проходят 61% всех пришедших — 9 161 человек. А по доле самый дырявый переход — последний: activate → purchase не проходят 70,5% активированных. Люди распробовали продукт и не заплатили.
Что должно получиться: оба наблюдения, а не одно. Куда смотреть первым делом — зависит от цели: нужен объём — чинить верх воронки, нужны деньги с уже вовлечённых — чинить низ. Умение развести абсолютную и пошаговую потерю — ровно то, что отличает аналитика от человека, один раз построившего воронку в шаблоне; на собеседовании эта развилка — готовая ловушка.
Шаг 8. Разрезать по каналам
Зачем: средняя воронка прячет разницу между источниками трафика. Повторяем расчёт отдельно для organic / ads / referral.
by_channel = (users.groupby("channel")["did_purchase"]
.mean()
.sort_values(ascending=False))
Что должно получиться:
Сквозная конверсия по каналам (visit -> purchase):
referral 11.57%
organic 6.37%
ads 3.81%
Referral приводит платящих втрое эффективнее платной рекламы, причём выигрывает на каждом переходе (48,9% против 33,4% на регистрации, 38,8% против 23,5% на покупке). Практический смысл: сравнивать каналы по числу визитов — обманчиво; ads может лидировать по трафику и проигрывать по деньгам.
Шаг 9. Нарисовать и записать
Зачем: результат должен быть виден и перепроверяем. Funnel-chart целиком, столбики сквозной конверсии по каналам, и все числа — в текстовый отчёт.
Что должно получиться: outputs/funnel.png, outputs/funnel_by_channel.png и outputs/results.txt со всеми цифрами, включая детальные конверсии шаг к шагу по каждому каналу. Текстовый отчёт — не формальность: по нему выводы перепроверяются без перезапуска кода.
Как рассказывать про проект на собеседовании
Заготовка на 90 секунд: «Я собрала анализ продуктовой воронки на событийных данных: 15 000 пользователей, путь visit → signup → activate → purchase, у каждого пользователя канал привлечения. Сквозная конверсия 6,25%. Интересных вывода два. Первый: узкое место зависит от способа счёта — в абсолюте теряем больше всего на входе (61% визитов не регистрируются), а по доле самый дырявый переход последний: 70,5% активированных не покупают. Второй: канал решает в разы — referral даёт сквозную конверсию 11,57% против 3,81% у платной рекламы, то есть сравнение каналов по трафику вводило бы в заблуждение».
Акценты: событийная модель данных, монотонность флагов как санити-чек, развилка «абсолют против доли», каналы по деньгам, а не по визитам.
Вопросы, которые зададут по этому проекту
— Так какое всё-таки узкое место — первый шаг или последний? Оба, в разных смыслах. Абсолютная потеря максимальна на visit → signup: 9 161 человек, 61% пришедших. Относительная — на activate → purchase: 70,5% отвала. Выбор приоритета — вопрос цели и экономики: если LTV покупателя высокий, процент на нижнем шаге дороже процента на верхнем, потому что там люди уже вовлечены. Правильный ответ на собеседовании — показать развилку, а не назвать один шаг.
— Что вы называете активацией и почему это важно? Первое целевое действие после регистрации — момент, когда пользователь получил ценность продукта. В проекте активация задана генератором; в реальном продукте её определение — отдельная задача: выбрать действие, которое статистически связано с удержанием и покупкой. От определения активации зависит вся середина воронки — сместите его, и 54,4% превратятся в любое другое число.
— Referral конвертит 11,57%, ads — 3,81%. Переливаем бюджет в referral? Не сразу. Во-первых, объём: referral — 15% трафика, и неизвестно, масштабируется ли его качество — реферальные пользователи приходят тёплыми, и при росте канала конверсия обычно падает. Во-вторых, каналы сравнивают по стоимости покупателя (CAC), а не только по конверсии: у ads конверсия втрое ниже, но если клик дёшев, экономика может сходиться. Вывод из моих данных скромнее: не оценивать каналы по визитам и посчитать CAC по каждому.
— Как посчитать эту воронку на SQL?
Из таблицы событий: агрегирую до пользователя через MAX(CASE WHEN event = 'signup' THEN 1 ELSE 0 END) по каждому шагу, затем суммирую флаги и делю последовательно. С условием порядка (регистрация после визита по времени) — через оконные функции или self-join по user_id с условием на timestamp. Логика один в один как в pandas-версии.
— В ваших данных шаги строго упорядочены. Что делать в реальном логе? В реальности порядок ломается: покупка без активации (промокод в лоб), события теряются, дублируются, приходят с опозданием. Тогда воронку определяют строже: шаг засчитывается, только если его timestamp позже предыдущего шага, задаётся окно конверсии (например, 30 дней от визита), а «нарушители порядка» выносятся в отдельную категорию и исследуются — часто это баги логирования, а не поведение.
— Конверсия ads упала с 3,8% до 3,0% за месяц. Ваши действия? Это уже кейс про диагностику метрики: сначала проверить сам факт и данные (не сломалось ли логирование UTM), потом сегментировать — по кампаниям внутри ads, по платформам, по гео, — и разложить сквозную конверсию на пошаговые, чтобы увидеть, какой переход просел. Алгоритм целиком разобран в статье «Метрика упала».
— Чем воронка отличается от когортного анализа и когда что использовать? Воронка — движение к целевому действию по шагам, отвечает «где теряем до конверсии». Когорты — поведение после конверсии во времени, отвечает «возвращаются ли». Полная картина продукта требует обоих: у меня это два отдельных проекта, воронка и когортный retention, и на собеседовании я бы их связала: воронка приводит к первой покупке, когорты показывают, что происходит после.
Что дальше
Это последняя инструкция серии. Если собрали все четыре проекта — у вас портфолио, закрывающее A/B-тесты, retention, BI и воронки: осталось упаковать по правилам из хаба и отрепетировать рассказ по каждому.
Источники
- Alistair Croll, Benjamin Yoskovitz. Lean Analytics: Use Data to Build a Better Startup Faster. O'Reilly, 2013 — воронки и метрики каналов.
- Dave McClure. Startup Metrics for Pirates (AARRR), 2007 — каркас acquisition-activation-retention-revenue-referral.
- Документация pandas: groupby и агрегации — https://pandas.pydata.org/docs/
- Документация matplotlib — https://matplotlib.org/stable/
Проект закрывает «покажите, как вы думаете про воронку». Кейсы на конверсию, каналы и падение метрик — в каталоге задач с разборами; часть открыта бесплатно.