маркетинговая-аналитикаатрибуцияметрикиSQL

Модели атрибуции в маркетинговой аналитике

2026-07-11 10 мин

Модель атрибуции — это правило, по которому вы распределяете «заслугу» за конверсию между каналами и касаниями, через которые прошёл пользователь по пути к покупке. Если нужно решение прямо сейчас: для операционных отчётов начните с last touch, добавьте position-based, когда поймёте, что верх воронки недооценён, и переходите к data-driven только когда накопились тысячи конверсий и есть кому поддерживать модель. Ниже я разберу каждую модель на пальцах, покажу, как посчитать их в SQL, и расскажу, где обычно спотыкаются кандидаты на собеседовании.

Что такое атрибуция простыми словами?

Представьте, что человек купил у вас подписку. За две недели до этого он пришёл из контекстной рекламы, потом увидел ваш пост в соцсети, кликнул на письмо из рассылки и наконец зашёл напрямую и оплатил. Кому засчитать эту продажу? Рекламе, которая привела впервые? Рассылке, которая напомнила? Или прямому заходу, на котором произошла оплата?

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

Ключевых понятий три. Касание (touchpoint) — это любое взаимодействие пользователя с каналом: клик по рекламе, переход из письма, визит из органики. Путь (journey) — упорядоченная по времени цепочка касаний до конверсии. Окно атрибуции (lookback window) — период, в течение которого касание вообще имеет право претендовать на заслугу: обычно 7, 14 или 30 дней. Всё, что было раньше окна, мы просто игнорируем.

Чем last touch отличается от first touch?

Это две простейшие однокасательные модели, и разница между ними принципиальная. Last touch отдаёт всю заслугу последнему касанию перед конверсией. First touch — первому касанию в цепочке. Всё, что между ними, обнуляется.

Last touch хвалит каналы «дна воронки»: прямые заходы, брендовый поиск, ретаргетинг. Именно они обычно оказываются последними перед оплатой, поэтому в отчёте last touch они выглядят героями. Проблема в том, что прямой заход не «привёл» клиента — клиент уже был прогрет, просто пришёл дозреть. Модель систематически недооценивает всё, что работает на узнаваемость.

First touch — зеркальная история. Она хвалит верх воронки: медийку, охватные кампании, первый контакт из органики. И так же систематически недооценивает то, что реально закрывает сделку. Обе модели удобны тем, что их легко посчитать и легко объяснить менеджеру. Обе врут, но врут предсказуемо и в разные стороны, поэтому их полезно смотреть в паре: если канал силён в first touch, но слаб в last touch, он про привлечение; если наоборот — про закрытие.

На практике 80% дашбордов в компаниях крутятся именно на last touch просто потому, что рекламные кабинеты отдают её по умолчанию. Знать её реализацию наизусть — обязательный минимум, и её любят спрашивать на собеседованиях.

Как работают линейная, time decay и position-based модели?

Когда однокасательных моделей становится мало, в ход идут многокасательные (multi-touch). Они делят заслугу между всеми касаниями пути, но по разным правилам.

Линейная (linear) — самая честная и самая наивная одновременно. Каждому из N касаний достаётся ровно 1/N конверсии. Если путь состоял из четырёх касаний, каждое получит по 0.25. Плюс — никто не забыт. Минус — она считает, что случайный показ баннера и финальный клик по промокоду одинаково важны, а это редко правда.

Time decay (затухание по времени) исходит из идеи, что чем ближе касание к конверсии, тем оно ценнее. Вес считается по формуле экспоненциального затухания: weight = 2^(-t / H), где t — сколько дней прошло от касания до конверсии, а H — период полураспада (half-life), часто 7 дней. Касание за день до покупки весит почти единицу, касание двухнедельной давности — копейки. Модель хороша для длинных циклов сделки, где недавние контакты действительно важнее.

Position-based (позиционная, она же U-образная) — компромисс, который любят продуктовые команды. Классическая раскладка: 40% заслуги первому касанию, 40% последнему, оставшиеся 20% поровну делятся между всеми средними. Логика простая: важнее всего то, что нас познакомило, и то, что закрыло сделку, а середина — поддержка. Это чаще всего разумный дефолт, если вы уже переросли last touch, но ещё не дозрели до data-driven.

Что такое data-driven атрибуция и стоит ли её внедрять?

Data-driven атрибуция (DDA) отличается тем, что веса не заданы вами вручную, а выводятся из самих данных. Модель смотрит на реальные пути и оценивает, насколько присутствие канала в цепочке повышает вероятность конверсии по сравнению с путями без него. Классический подход — значения Шепли (Shapley values) из теории игр: канал получает столько заслуги, сколько он в среднем добавляет к результату во всех возможных комбинациях каналов. Другой распространённый вариант — марковские цепи, где считают, насколько упадёт конверсия, если из графа переходов «удалить» канал (removal effect).

Звучит красиво, но у DDA высокая цена входа. Ей нужны объёмы: на редких конверсиях статистика шумит и веса скачут от месяца к месяцу. Ей нужна чистая склейка касаний в единый путь — а это самая больная часть из-за разных устройств и потери идентификаторов. И её тяжело объяснить бизнесу: «модель так решила» — плохой аргумент в разговоре про урезание бюджета.

Мой практический критерий: внедряйте DDA, когда у вас стабильно тысячи конверсий в месяц, налажена склейка пользователей и есть человек, который будет модель поддерживать и защищать её выводы. Если хоть один пункт не выполнен — position-based даст 80% пользы за 10% усилий. Разбор Shapley и марковских цепей удобно тренировать на Python, где есть готовые структуры для перебора комбинаций.

Как посчитать last-touch атрибуцию в SQL?

Перейдём к коду. Договоримся о двух таблицах. touchpoints(user_id, channel, touched_at) — по строке на каждое касание. conversions(user_id, converted_at, revenue) — по строке на каждую конверсию. Задача — приклеить к каждой конверсии её касания внутри окна атрибуции и распределить заслугу.

Last touch делается через оконную функцию: нумеруем касания в обратном порядке по времени и берём первое.

WITH journey AS (
  SELECT
    c.user_id,
    c.converted_at,
    c.revenue,
    t.channel,
    ROW_NUMBER() OVER (
      PARTITION BY c.user_id, c.converted_at
      ORDER BY t.touched_at DESC
    ) AS rn_last
  FROM conversions c
  JOIN touchpoints t
    ON t.user_id = c.user_id
   AND t.touched_at <= c.converted_at
   AND t.touched_at >= c.converted_at - INTERVAL '30 days'  -- окно атрибуции
)
SELECT
  channel,
  COUNT(*)      AS conversions,
  SUM(revenue)  AS revenue
FROM journey
WHERE rn_last = 1
GROUP BY channel
ORDER BY revenue DESC;

First touch — тот же запрос, только ORDER BY t.touched_at ASC в оконной функции. Обратите внимание на два условия в JOIN: touched_at <= converted_at отсекает касания, случившиеся уже после покупки (иначе вы засчитаете постпродажные письма как «приведшие» к сделке), а нижняя граница задаёт то самое окно атрибуции. Синтаксис интервала я пишу в стиле PostgreSQL; в других движках это будет dateadd или вычитание дней. Разобраться с оконными функциями и вычитанием дат можно в SQL-справочнике.

Как реализовать linear и position-based в SQL?

Линейная модель проще, чем кажется: считаем число касаний в пути оконным COUNT, а дальше каждому касанию отдаём долю 1/N.

WITH journey AS (
  SELECT
    c.user_id, c.converted_at, c.revenue, t.channel,
    COUNT(*) OVER (PARTITION BY c.user_id, c.converted_at) AS touches
  FROM conversions c
  JOIN touchpoints t
    ON t.user_id = c.user_id
   AND t.touched_at <= c.converted_at
   AND t.touched_at >= c.converted_at - INTERVAL '30 days'
)
SELECT
  channel,
  ROUND(SUM(1.0 / touches), 2)          AS attributed_conversions,
  ROUND(SUM(revenue / touches), 2)      AS attributed_revenue
FROM journey
GROUP BY channel
ORDER BY attributed_revenue DESC;

Position-based требует знать и позицию касания, и общее их число, чтобы применить правило 40/20/40. Нумеруем касания в прямом порядке, отдельно считаем длину пути и раскидываем веса через CASE.

WITH ranked AS (
  SELECT
    c.user_id, c.converted_at, c.revenue, t.channel,
    ROW_NUMBER() OVER (PARTITION BY c.user_id, c.converted_at
                       ORDER BY t.touched_at) AS pos,
    COUNT(*)    OVER (PARTITION BY c.user_id, c.converted_at) AS n
  FROM conversions c
  JOIN touchpoints t
    ON t.user_id = c.user_id
   AND t.touched_at <= c.converted_at
   AND t.touched_at >= c.converted_at - INTERVAL '30 days'
),
weighted AS (
  SELECT channel, revenue,
    CASE
      WHEN n = 1              THEN 1.0            -- одно касание получает всё
      WHEN n = 2              THEN 0.5            -- два делят пополам
      WHEN pos = 1 OR pos = n THEN 0.4            -- первое и последнее
      ELSE 0.2 / (n - 2)                          -- середина делит 20%
    END AS weight
  FROM ranked
)
SELECT
  channel,
  ROUND(SUM(weight), 2)            AS attributed_conversions,
  ROUND(SUM(weight * revenue), 2)  AS attributed_revenue
FROM weighted
GROUP BY channel
ORDER BY attributed_revenue DESC;

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

Какую модель атрибуции выбрать?

Универсального ответа нет, и это правильный ответ на собеседовании. Выбор зависит от длины цикла сделки, объёма конверсий и того, какое решение вы собираетесь принять по итогам отчёта.

Мой рабочий алгоритм такой. Если продукт с коротким циклом и импульсной покупкой — last touch адекватен, тут почти нет пути, чтобы его делить. Если цикл длинный и вы хотите справедливее оценить верх воронки — берите position-based как разумный дефолт. Если недавние касания объективно важнее (дорогая покупка, много касаний перед сделкой) — time decay. И только на больших стабильных объёмах с чистой склейкой — data-driven.

Важнее самой модели — не менять её молча. Как только вы переключаете атрибуцию, отчёты всех каналов меняются, и любой канал можно «выключить» или «включить» просто сменив правило. Поэтому фиксируйте модель, документируйте окно атрибуции и всегда показывайте пару моделей рядом, чтобы было видно, где именно результат чувствителен к выбору. Отдельно держите в голове, что атрибуция отвечает на вопрос «кому засчитать», а не «что было бы, если убрать канал» — на последний честно отвечает только эксперимент.

Какие ошибки в атрибуции встречаются чаще всего?

Первая и самая частая — путать атрибуцию с причинностью. Модель показывает корреляцию касаний с конверсиями, а не причинный вклад канала. Если хотите узнать реальный эффект — нужен инкрементальный тест (geo-lift, holdout-группа), а не перекладывание весов. На собеседовании фраза «атрибуция не равна инкременту» сразу выделяет кандидата.

Вторая — забыть про окно атрибуции и границу touched_at <= converted_at. Без верхней границы вы засчитаете постпродажные касания, без нижней — притянете касания годичной давности. И то и другое искажает картину.

Третья — двойной счёт при нескольких конверсиях одного пользователя. Если человек купил дважды, партиционировать надо по паре (user_id, converted_at), а не только по user_id, иначе касания приклеятся ко всем его покупкам разом. Четвёртая — игнорировать пользователей без касаний: прямые заходы и органика часто вообще не пишут touchpoint, и такие конверсии молча выпадают из знаменателя, завышая долю платных каналов. Пятая — сравнивать выручку между отчётами на разных моделях, будто это одна метрика; это разные разрезы одного и того же пирога. Полезно держать рядом стабильные продуктовые метрики вроде DAU и retention, чтобы видеть, реальный ли это рост или просто перекладка заслуги внутри той же аудитории.

Атрибуция — тема, которую любят на интервью в продуктовых командах: она проверяет и SQL с оконными функциями, и понимание бизнес-логики, и умение отличить корреляцию от эффекта. Если хотите отработать её вместе с остальным арсеналом аналитика — SQL, Python, метрики, A/B — на тренажёре есть задачи с реальных собеседований, а по Python пригодится справочник для расчёта Shapley и марковских весов. Полный доступ к банку задач и AI-разбору собеседований открывает Pro — рекомендую попробовать бесплатную часть и посмотреть, зайдёт ли формат.

Считай метрики в тренажёре
Воронки, LTV и атрибуция на SQL-задачах с автопроверкой — бесплатно.
SQL-тренажёр →