Files
clickstream-data-platform/docs/labs/1-5-utm-effectiveness.md
T
ddadminandClaude Fable 5.1 d955f7d694 docs(labs): файлы ТЗ переименованы по схеме S-N-slug
- Зачем:
  - в docs/labs/ будет много заданий; номер ступени и номер задания в имени
    делают папку оглавлением программы и избавляют от поиска по README (#230).
- Что:
  - шесть ТЗ переименованы через git mv: 1-1…1-5 для ступени 1,
    2-1-superset-chart.md для ступени 2; факультатив superset-dashboard.md
    остался без номера.
  - обновлены ссылки в docs/labs/README.md, 2-1-superset-chart.md,
    docs/architecture/dds/session.md и пути в dags/homework_check.py.
  - правило имен для docs/labs/ в AGENTS.md переписано под S-N-slug.
- Проверка:
  - make lint; скрипт обхода относительных ссылок в docs/ и AGENTS.md —
    битых нет.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 09:09:14 -04:00

9.0 KiB
Raw Blame History

Задание 5: dm.utm_effectiveness

Чему учит: понимать предупреждение в приставке declared_: это сумма со слов клиента, а не деньги отчетности. Самостоятельно находить причины расхождения двух сумм выручки.

Сначала прочитать

Роль витрины

Аналитик сравнивает рекламные каналы по дням: трафик, добавления в корзину, покупки и заявленные клиентом суммы.

На стенде поведение и атрибуцию считают по трекеру, деньги — по бэкенду. Здесь сделано исключение: UTM-метки есть только в кликстриме, поэтому для оценки покупок по меткам используют клиентскую сумму.

Что сделать

Читайте dds.event_v напрямую, без соседних витрин DM. Зерно — день события × utm_source × utm_medium × utm_campaign. Зерно новое, поэтому витрина хранится готовым агрегатом — см. «Как принято на ступени 1». Создайте пять объектов: dm.utm_effectiveness_rep / _dist, донор _stage_rep / _stage_dist и публичное представление dm.utm_effectiveness_v. Ключ партиции — день.

Состав колонок

Колонка Тип Значение
report_date Date День события
utm_source LowCardinality(String) Источник кампании
utm_medium LowCardinality(String) Канал кампании
utm_campaign String Имя кампании
visitors UInt64 Число разных client_id
pageviews UInt64 Число просмотров страниц
add_to_carts UInt64 Число добавлений в корзину
purchases UInt64 Число покупок
declared_revenue Decimal(18, 2) Сумма покупок, заявленная клиентом
_load_id String Идентификатор запуска, собравшего день
_load_ts DateTime64(3, 'UTC') Время сборки

Для declared_revenue сложите purchase_revenue[1] событий purchase. В параллельных массивах purchase* по контракту хранятся один номер заказа и одна сумма на событие. В сверке purchase_vs_orders используют тот же индекс.

Тип purchase_revenue — Array(Decimal(18, 2)): исходный Float64 преобразован в ODS. Сложение Decimal точное; CAST до или после sum не меняет результат и не нужен. Тип итога — Decimal(18, 2).

Для utm_campaign выбран тип String: имен кампаний со временем становится много, и затраты на словарь LowCardinality перестают окупаться.

Ритм пересборки

События прожитого дня больше не меняются. Как в задании 2, соберите в донор дни от первого несобранного до последнего прожитого и замените дневные партиции. Добавьте задачу в dm_transform.

Ключ шардирования

Выберите ключ самостоятельно. Если берете хеш одной из трех UTM-колонок, посчитайте долю строк с ее самым частым значением и обоснуйте выбор.

Ключ донора — тот же, что у цели (см. «Как принято на ступени 1» и раздел «Раскладка по шардам» справочника хранилища).

Атрибуция по событию, а не по визиту

Группируйте события по их собственным меткам. Модели «первое касание» и «последнее касание» здесь не рассчитываются.

В dds.session_v записаны UTM первого события визита. Распределение по каналам может отличаться от этой витрины, и это не означает ошибку в расчете. В отчете различайте атрибуцию визитов и событий.

Прямой трафик остается в витрине

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

Оставьте пустые метки пустыми строками: новое обозначение прямого трафика потребовало бы объяснения.

Расхождение с dm.revenue_daily_v

Сравните за один день sum(declared_revenue) своей витрины и sum(revenue) из dm.revenue_daily_v. Суммы не сходятся; проверять их равенство не нужно.

В revenue_daily складываются цены позиций, умноженные на количество, без скидок и доставки — та же основа, что у клиентской суммы. Расхождение объясняется историей заказов, а не способом расчета:

  • событие покупки остается после отмены заказа, а revenue_daily учитывает только оплаченные заказы;
  • событие покупки может потеряться при доставке, хотя заказ поступил, или поступить дважды;
  • клиентская сумма может отличаться от серверной суммы позиций: генератор намеренно создает это расхождение у одного-двух заказов из ста.

Запишите свой вывод в двух-трех фразах. Число расхождений по заказам и их причины можно посмотреть в dm.purchase_vs_orders_v, где выделены классы расхождений.

Как проверить себя

Деловой ключ — utm_source, utm_medium, utm_campaign внутри дня. Получите прямой агрегат из dds.event_v за день и соедините его с dm.utm_effectiveness_v полным внешним соединением по ключу. Посчитайте строки, где ключ отсутствует с одной из сторон или различается любая из пяти величин. Расхождений должно быть ноль.

Дополнительно сверьте дневные итоги:

  • суммы pageviews, add_to_carts и purchases равны числу событий соответствующих типов в dds.event_v;
  • сумма declared_revenue равна сумме purchase_revenue[1] по всем событиям purchase того же дня.

Сумма visitors по строкам повторно учитывает одного посетителя, приходящего по разным меткам. Для числа посетителей дня она не подходит.

Приемка

  • созданы пять объектов, донор объявлен через AS и шардируется ключом цели;
  • сохранены строки без UTM-меток;
  • declared_revenue хранится в Decimal(18, 2);
  • расхождение с dm.revenue_daily_v объяснено письменно;
  • задача utm_effectiveness в homework_check завершилась успешно;
  • задача витрины добавлена в dm_transform.