Перейти к содержанию

Воронка просмотра и завершения

Простые счётчики «Событий» показывают семь просмотров и пять завершений, но их отношение не объясняет переход конкретного читателя. Построим воронку: сначала просмотр урока, затем завершение в течение суток. Единицей станет пара анонимный браузер–урок, поэтому повторные просмотры одного материала не увеличат число участников.

Используем windowFunnel, документированную параметрическую агрегатную функцию ClickHouse. Все даты фикстуры переведены в UTC, запросы не выполнялись. Пять из шести пар ожидаются на втором шаге после получения позднего события. Это учебный вывод по заданному набору, а не измеренная конверсия ProfessorWeb.

Участник определяется до агрегирования

Если группировать только по anon_id, просмотр HTML и завершение CSS могут оказаться двумя шагами одной воронки. Для вопроса «завершили ли показанный урок» это неверно. Поэтому lesson_id входит в группировку вместе с браузером. Один человек, изучающий два материала, участвует в двух предметных попытках.

Курс тоже можно включить в ключ, если идентификаторы уроков уникальны только внутри него. В нашей фикстуре они уникальны во всём наборе, но для внешней схемы такое правило должно быть закреплено. Не полагайтесь на красивый slug, если разные производители используют одинаковые имена.

Полный запрос сначала определяет максимальный достигнутый шаг каждой пары, затем считает пары. Условия фильтруют только два нужных события; закладка не входит в эту конкретную воронку.

WITH attempts AS
(
  SELECT anon_id, lesson_id,
    windowFunnel(86400)(
      toDateTime(occurred_at),
      event_name = 'lesson_view',
      event_name = 'lesson_complete'
    ) AS level
  FROM events_lab.logical_events
  WHERE occurred_at >= toDateTime64('2026-10-01 00:00:00', 3, 'UTC')
    AND occurred_at < toDateTime64('2026-10-05 00:00:00', 3, 'UTC')
    AND event_name IN ('lesson_view', 'lesson_complete')
  GROUP BY anon_id, lesson_id
)
SELECT countIf(level >= 1) AS viewed_pairs,
       countIf(level >= 2) AS completed_pairs,
       completed_pairs / nullIf(viewed_pairs, 0) AS conversion
FROM attempts;

86400 задаёт окно в секундах при использовании DateTime. В примере миллисекунды исходного времени приводятся к секундам. Для близких действий с совпадающей секундой понадобилось бы отдельно определить правило порядка и точность. У фикстуры шаги разнесены минутами, поэтому такое преобразование не меняет ожидаемый результат.

Что считается вторым шагом

windowFunnel ищет последовательность выполненных условий в заданном временном окне. В этой модели завершение до просмотра не подтверждает прохождение. Завершение через двое суток тоже не достигает второго шага для окна сутки, хотя существует в общих счётчиках. Это не потеря события, а выбранная граница вопроса.

Параметрические агрегатные функции описаны в официальном справочнике. Наша группировка, окно и значения условий определяют собственную бизнес-метрику. Их следует сохранить рядом с названием отчёта, чтобы будущая смена окна не выглядела неожиданным изменением поведения читателей.

Функция возвращает уровень, а не количество всех успешных прохождений пары. Если браузер изучал один урок несколько раз, мы считаем, достиг ли он второго шага хотя бы в одной подходящей последовательности за период. Для подсчёта отдельных учебных попыток понадобился бы дополнительный идентификатор попытки или другое правило разбиения.

Разбор шести пар

Посетитель a имеет две пары: HTML и CSS, обе доходят до завершения. b открывает HTML дважды и получает одно поздно доставленное завершение первого просмотра. c завершает HTML, но только открывает JavaScript. d открывает и завершает CSS. Всего шесть пар со просмотром, пять со вторым шагом.

Поздняя доставка b меняет отчёт: до её получения было четыре успешных пары, после — пять. Время действия остаётся внутри суток, поэтому событие подходит воронке, хотя сборщик увидел его через несколько дней. Если использовать received_at, оно ошибочно оказалось бы далеко от просмотра и не прошло окно.

Ожидаемая конверсия равна пяти шестым, примерно 0,8333, или 83,33 процента. Точное представление числа в клиенте зависит от форматирования. Не выводите лишнюю точность как признак высокой уверенности: в наборе всего шесть пар и нет оснований переносить эту долю на реальную аудиторию.

Граница периода и окно

Запрос ограничивает события первыми четырьмя днями октября. Если просмотр произошёл четвёртого вечером, а завершение пятого утром, оба действия укладываются в сутки, но фильтр исключит завершение. Поэтому отчёт по участникам, начавшим в период, требует чтения дополнительного хвоста времени и отдельного отбора начального шага.

Наш учебный набор не содержит такого пограничного случая, однако ограничение обязано быть названо. Текущий запрос отвечает на вопрос о последовательностях, полностью наблюдаемых внутри окна данных. Для продуктового отчёта можно выбрать когорту просмотров периода и читать завершения ещё сутки после конца. Тогда незавершённый последний день должен оставаться предварительным.

Не исправляйте границу простым расширением периода без изменения подписи: в знаменатель могут попасть новые просмотры пятого числа. Нужно отделить период набора участников от периода наблюдения результата. Это различие подготовит нас к удержанию, где начальная когорта и последующие дни тоже играют разные роли.

Почему закладка не обязательный шаг

Сохранение урока не является необходимым условием завершения. Если добавить его между просмотром и завершением, люди, которые прочитали без закладки, станут выглядеть потерянными. Такая воронка могла бы изучать конкретный путь сохранённого материала, но не общее завершение курса.

Завершение без первого шага

Пара может иметь только lesson_complete из-за потери просмотра или начала чтения до выбранного периода. Её уровень не достигает первого шага, и она не входит в знаменатель просмотренных пар. Это не означает, что завершение удалено из данных: общий отчёт продолжает его считать.

Полезно отдельно показать число таких пар как качество наблюдения. Если оно растёт, сначала проверьте производителя, границы периода и историю, а не объявляйте людей прошедшими обратную воронку. События могли быть доставлены в другом порядке, но сама функция использует время действия.

Для нашей фикстуры каждый завершённый материал имеет предшествующий просмотр, поэтому особый случай не меняет ожидаемые пять из шести. Его отсутствие в маленьком наборе не освобождает внешнюю систему от определения поведения. В договоре отчёта должны быть описаны и типичные последовательности, и данные, которым не хватает наблюдаемого начала.

Поэтому выбор шагов следует предметной гипотезе, а не желанию использовать все доступные имена событий. Дополнительная воронка просмотра и закладки может существовать рядом, с отдельным названием и окном. Следующий урок сгруппирует браузеры по первому наблюдаемому дню и разберёт возвращение через день, сохранив ясное различие между участником и событием.

Оглавление серии