Атрибуция и правильное определение каналов в Google Analytics 4 и BigQuery
Одной из самых значимых возможностей Google Analytics 4 (GA App+Web) является передача сырых несэмплированных данных в BigQuery, что позволяет делать любые запросы и любые выборки данных для анализа. Мы часто используем эту возможность и работаем с данными напрямую в BigQuery даже чаще, чем с обычным интерфейсом новой Google Аналитики.
Однако, как и в любой сложной системе, здесь не обходится без ряда особенностей. О такой особенности определения источников и каналов я расскажу в этой статье.
Определение источника и канала в Google Analytics
Источник и канал — это очень важные параметры в аналитике и маркетинге. Они показывают откуда и как пришел к нам пользователь, то есть, с какого сайта и каким был этот переход: органическим, рекламным, реферальным и т.п. Существует несколько способов определения источника и канала:
- Принудительное, с помощью UTM-меток
- Автоматическое, по ряду правил
На самом деле при использовании UTM-меток мы получаем не только источник и канал, но и другие параметры, которые нам очень нужны для анализа:
- source — источник перехода.
- medium — канал перехода, то есть тип трафика.
- campaign — рекламная кампания.
- content — например, рекламное объявление, если их несколько в кампании.
- term — ключевые слова.
Обычно эти параметры определяются для первого хита сессии и характеризуют именно сессию пользователя, а не отдельный хит.
Что такое атрибуция каналов и модели атрибуции
В современных условиях пользователи могут совершать несколько взаимодействий с сайтом и по разным каналам. Например, пользователь увидел рекламное объявление и перешел на сайт (канал рекламы), через день он вспомнил об этом и вбил в поиске название компании и снова перешел на сайт (органический канал), а еще через день он просто открыл в браузер, выбрал адрес из истории ранее посещенных, опять перешел на сайт (прямой переход) и, наконец, что-то купил.
Вопрос: какому из этих каналов (реклама, органика, прямой) следует приписать эту конверсию?
Ответ на этот вопрос и является определением модели атрибуции канала. То есть, атрибуция — это просто приписывание какой либо активности пользователя, например, к каналу, то есть типу трафика. То есть, ответ на вопрос: как взаимодействовал пользователь перед тем, как что-то произошло.
Модель атрибуции — это способ учета этих взаимодействий, в нашем случае, этих каналов, если их несколько. Например, равномерное распределение самого факта конверсии на все каналы, которые использовались этим пользователем.
Существует множество моделей атрибуции: последний клик (учитываем только последний канал), первый клик (учитываем только первый канал), равномерное распределение, линейная модель, модель с учетом взаимодействий, цепи Маркова и другие более экзотические способы.
Всё это нам часто нужно определять при анализе трафика.
Google Analytics 4 при выгрузке данных в BigQuery определяет три параметра: источник, канал и кампанию в своих таблицах в виде структуры traffic_source:
- traffic_source.source — источник перехода
- traffic_source.medium — канал перехода
- traffic_source.name — кампания перехода
Однако часто нам этих данных не хватает. Вот несколько “проблемных” моментов:
- Зачастую требуется определение еще и content и term, а их в таблице нет.
- Переходы с yandex органики определяются неверно, как канал referral. В настоящий момент времени исправить это штатными способами в Google Analytics 4 невозможно.
- Несмотря на то, что Google явном виде пишет, что используется “модель Последний клик (несколько каналов)”, на самом деле атрибуция происходит не совсем так, как хотелось бы.
О последнем случае следует сказать отдельно. Google в своей документации пишет:
- При атрибуции в поле traffic_source используется модель Последний клик (несколько каналов). Значения traffic_source не изменяются, если пользователь после установки взаимодействует с последующими кампаниями.
- Cross-channel last click (Последний клик (несколько каналов). Google Analytics будет анализировать клики от всех источников и выполнять атрибуцию по последнему клику. Прямой трафик исключается, то есть все модели атрибуции в Аналитике не назначают долю ценности прямым переходам, за исключением случаев, когда конверсию невозможно связать с кампанией.
Фактически это хорошо нам знакомая модель “Последний непрямой клик”, но мы неоднократно сталкивались с ситуаций, когда в этой модели, например, игнорировались UTM-метки для второй и далее сессий.
Поэтому мы решили сделать свой собственный расчет определения и атрибуции каналов на базе сырых данных Аналитики.
Расчет источников и каналов в BigQuery
Я написал механизм определения всех пяти параметров для каждой сессии в отдельности. При этом определении используются следующие правила:
- Если первый хит сессии был с UTM-метками, данные берутся исключительно из UTM-меток, даже если это был внутренний переход. Это правило для тех малограмотных маркетологов, которые умудряются делать внутренние ссылки с UTM-метками.
- Если переход был с поисковиков Google, Яндекс, Рамблер, Yahoo, Bing (список настраивается), то это однозначно органика.
- Если переход был из соц.сетей Facebook, VK, Одноклассники, Instagram, Twitter (список настраивается), то это канал social. Google это делает по-другому, но мне проще их соотнести с отдельным выраженным каналом.
- Прямой переход фиксируется так, как принято у Google Analytics, то есть, значениями (direct)/(none).
- Для каждой сессии в отдельности считаются следующие модели атрибуции:
- Последний клик (считается для источника, канала и кампании)
- Первый клик (считается для источника, канала и кампании)
- Для каждой сессии в отдельности считаются шесть путей
- Путь каналов, например, “yandex > google > (direct)”
- Путь каналов, например, “cpc > organic > (none)”
- Путь кампаний
- Путь объявлений
- Путь ключевых слов
- Путь “источник/канал”, например, “ yandex/cpc > m.facebook.com/social
Вот запрос SQL, который это все делает. Это больше “болванка” запроса, на основе которой далее делаются свои запросы. Обязательно замените в нем исходный набор данных на ваш собственный.
WITH
raw_data AS (
-- Черновая выборка данных и UNNEST в 1НФ
SELECT
PARSE_DATE('%Y%m%d', event_date) AS event_date,
event_timestamp,
event_name,
user_pseudo_id,
-- Параметры сессии
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS ga_session_id,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') AS page_referrer,
FROM `project_id.analytics_**************` -- УКАЖИТЕ ЗДЕСЬ СВОЙ ИСТОЧНИК ДАННЫХ
WHERE event_name IN ('page_view', 'session_start')
),
entry_points AS (
-- Определяем точки входа в сессию
SELECT
raw_data.*,
CONCAT(user_pseudo_id, '.', ga_session_id) AS session_id,
FIRST_VALUE(page_location) OVER (PARTITION BY ga_session_id ORDER BY event_timestamp) AS first_page,
FIRST_VALUE(page_referrer) OVER (PARTITION BY ga_session_id ORDER BY event_timestamp) AS referrer,
FROM raw_data
),
hits AS (
-- Таблица хитов и определение источника, канала, кампании для Last Click
SELECT
entry_points.*,
-- Определение источника
CASE
-- Если указана UTM метка, дерем данные из нее
WHEN REGEXP_CONTAINS(first_page, 'utm_source.*')
THEN REGEXP_EXTRACT(first_page, 'utm_source=([\\w\\d-_%.]+)(?:&|$)')
-- Если это переход из поисковиков, берем домен без TLD
WHEN REGEXP_CONTAINS(referrer, '^https?://(?:www\\.)?(yandex|google|rambler|yahoo|bing).*')
THEN REGEXP_EXTRACT(referrer, '^https?://(?:www\\.)?(yandex|google|rambler|yahoo|bing).*')
-- Если referrer отсутствует, возвращаем (direct)
WHEN referrer IS NULL
THEN '(direct)'
-- Иначе берем домен перехода без www
ELSE REGEXP_EXTRACT(referrer, '^https?://(?:www\\.)?([a-zA-Z0-9_\\-.]+)/.*')
END AS hit_source,
-- Определение канала
CASE
-- Если указана UTM метка, дерем данные из нее
WHEN REGEXP_CONTAINS(first_page, 'utm_medium.*')
THEN REGEXP_EXTRACT(first_page, 'utm_medium=([\\w\\d-_%.]+)(?:&|$)')
-- Если referrer отсутствует, возвращаем (none)
WHEN referrer IS NULL THEN '(none)'
-- Если это переход из поисковиков, возвращаем organic
WHEN REGEXP_CONTAINS(referrer, '^https?://(?:www\\.)?(yandex|google|rambler|yahoo|bing).*')
THEN 'organic'
-- Если это переход из соц.сетей, возвращаем social
WHEN REGEXP_CONTAINS(referrer, '^https?://(?:.*\\.)?(facebook|fb|vk|ok|instagram|twitter)\\..*')
THEN 'social'
-- Иначе возвращаем referral
ELSE 'referral'
END AS hit_medium,
-- Определение кампании
CASE
-- Если указана UTM метка, дерем данные из нее
WHEN REGEXP_CONTAINS(first_page, 'utm_campaign.*')
THEN REGEXP_EXTRACT(first_page, 'utm_campaign=([\\w\\d-_%.]+)(?:&|$)')
-- Иначе возвращаем NULL
ELSE NULL
END AS hit_campaign,
-- Определение объявления
CASE
-- Если указана UTM метка, дерем данные из нее
WHEN REGEXP_CONTAINS(first_page, 'utm_content.*')
THEN REGEXP_EXTRACT(first_page, 'utm_content=([\\w\\d-_%.]+)(?:&|$)')
-- Иначе возвращаем NULL
ELSE NULL
END AS hit_content,
-- Определение ключевых слов
CASE
-- Если указана UTM метка, дерем данные из нее
WHEN REGEXP_CONTAINS(first_page, 'utm_term.*')
THEN REGEXP_EXTRACT(first_page, 'utm_term=([\\w\\d-_%.]+)(?:&|$)')
-- Иначе возвращаем NULL
ELSE NULL
END AS hit_term,
FROM entry_points
),
attribution AS (
-- Атрибуция источников, каналов и т.п. по сессиям
SELECT
hits.*,
FIRST_VALUE(hit_source) OVER (PARTITION BY session_id ORDER BY event_timestamp) AS source_last_click,
FIRST_VALUE(hit_medium) OVER (PARTITION BY session_id ORDER BY event_timestamp) AS medium_last_click,
FIRST_VALUE(hit_campaign) OVER (PARTITION BY session_id ORDER BY event_timestamp) AS campaign_last_click,
FIRST_VALUE(hit_source) OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS source_first_click,
FIRST_VALUE(hit_medium) OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS medium_first_click,
FIRST_VALUE(hit_campaign) OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS campaign_first_click,
-- Сессионные пути
STRING_AGG (CASE WHEN event_name='session_start' THEN hit_source ELSE NULL END, ' > ') OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS source_path,
STRING_AGG (CASE WHEN event_name='session_start' THEN hit_medium ELSE NULL END, ' > ') OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS medium_path,
STRING_AGG (CASE WHEN event_name='session_start' THEN hit_campaign ELSE NULL END, ' > ') OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS campaign_path,
STRING_AGG (CASE WHEN event_name='session_start' THEN hit_content ELSE NULL END, ' > ') OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS content_path,
STRING_AGG (CASE WHEN event_name='session_start' THEN hit_term ELSE NULL END, ' > ') OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS term_path,
STRING_AGG (CASE WHEN event_name='session_start' THEN CONCAT(hit_source, '/', hit_medium) ELSE NULL END, ' > ') OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS source_medium_path,
-- Число сессий пользователя
COUNT (DISTINCT session_id) OVER (PARTITION BY user_pseudo_id) AS session_count,
FROM hits
)
SELECT * FROM attribution
Важное замечание: этот запрос возвращает ТАБЛИЦУ ХИТОВ, то есть, ее можно использовать далее в своих расчетах. Если вам нужна таблица сессий, то самый простой способ не группировка, а просто добавить в конец (в последний SELECT) выражение
WHERE event_name = 'session_start'
(Спасибо Надежде Афоничевой за эту простую, но очень продуктивную идею).
Пояснения по запросу SQL
Этот запрос состоит из нескольких подзапросов:
- raw_data — просто формирует данные в первой нормальной форме и приводит их к нужному типу
- entry_points — определяет точки входа в сессию и формирует правильный идентификатор сессии для каждого пользователя
- hits — строит таблицу хитов и для каждого хита считает параметры канала, источника, кампании, объявления и ключевых слов по правилам, приведенным выше
- attribution — оконными функциями выполняет расчет модели “Последний клик” для сессии, “Первый клик” для пользователя и шесть путей, описанных выше
Поскольку этот запрос возвращает таблицу хитов, можно на основе полученных данных реализовать расчет любой другой модели атрибуции.
Буду благодарен за комментарии и уточнения к этой статье.