Собесов
12 августа 2026 г. · 2,2 тыс. просмотров

Пет-проект: воронка продукта — drop-off по шагам, сквозная конверсия, разрез по каналам

Пошаговая сборка пет-проекта по продуктовой воронке: события visit-signup-activate-purchase, конверсии шаг к шагу, поиск узкого места и разрез по каналам привлечения. С реальными цифрами прогона и вопросами с собеседования.

Это четвёртая инструкция серии про пет-проекты аналитика. Общий контекст — зачем нужен проект, где брать данные, как упаковать — в хабе серии. Остальные инструкции: 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/

Проект закрывает «покажите, как вы думаете про воронку». Кейсы на конверсию, каналы и падение метрик — в каталоге задач с разборами; часть открыта бесплатно.

Другие статьи