- Зачем:
- в 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>
9.0 KiB
Задание 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.