Визуализация путей пользователей на данных Google Analytics 4 в BigQuery

20 ноября, 2025

В этой статье я покажу вам, как создать диаграмму Санкей (Sankey diagram), используя данные Google Analytics 4, Google BigQuery, SQL-запросы и язык программирования Python.

Введение

Материал частично основан на решении Джошуа Кима (Joshua Kim), но дополнен моими комментариями, скриншотами и новыми приёмами.

Диаграмма Санкей (Sankey diagram)

Диаграмма Санкей, Санки, Сэнки и другие вариации (Sankey diagram) - это тип визуализации, в котором потоки (связи) между категориями отображаются с помощью направленных линий (лент), толщина которых соответствует количественному значению потока. Узлы (категории) располагаются по уровням слева направо, а ленты показывают, как значения распределяются между ними.

Диаграмма Sankey особенно полезна для:

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

Если вы успели поработать с Universal Analytics (Google Analytics 3), то наверняка хотя бы раз открывали карту поведения:

Карта поведения в Google Analytics (устар. Universal Analytics)

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

Или отчет Карта событий, который показывал, в каком порядке посетители вашего сайта запускали события на сайте:

Карта событий в Google Analytics (устар. Universal Analytics)

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

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

Пути пользователей в Google Analytics (устар. Universal Analytics)

В Google Analytics 4 нет элемента интерфейса, который официально называется Sankey diagram, однако концепция Санки используется внутри некоторых визуализаций. Наиболее близкая концепция - это Исследование пути (Path Exploration).

Исследование пути в Google Analytics 4

Данная методика исследования позволяет визуализировать пути перемещения пользователей по вашему сайту или мобильному приложения с целью:

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

Используя диаграмму Сэнки, мы можем реализовывать свои стратегии следующим образом (пример):

  • большинство покупателей на сайте обычно проходят путь, начиная со страницы A, затем переходят на страницу Б, далее на страницу В и в итоге совершают покупку. Поэтому мы можем добавить кнопку с призывом к действию, направляющую других посетителей по этому пути, что потенциально позволит нам повысить доход;
  • страницы A и Б - это места, откуда наши посетители чаще всего уходят. Скорее всего, у этих страниц есть проблемы с точки зрения удобства использования (юзабилити) или технической реализации. Стоит перейти к проведению интервью и исследованиям пользователей, чтобы глубже понять причины высокого показателя отказов и ухода с этих страниц.

Хотя в GA4 есть поддержка построения воронок и анализа пути пользователей для понимания их перемещений по сайту, на практике этот процесс очень сложен и неудобен, если вы хотите получить какие-либо полезные данные из Исследований.

Поэтому в качестве альтернативного решения рассмотрим пример построения диаграммы Санкей, используя данные Google Analytics 4, Google BigQuery, SQL-запросы и язык программирования Python.

Однако перед тем, как это сделать, вы должны установить связь Google Analytics 4 с BigQuery.

Ниже будут использоваться подзапросы WITH (Common Table Expressions, CTE) - это конструкция SQL, которая позволяет создавать временные именованные результирующие наборы (подзапросы), которые можно использовать внутри основного запроса. Она делает код более читаемым и структурированным, особенно при сложных запросах.

1. Преобразование данных

Хотя интеграция GA4 с BigQuery очень удобна, существует непростая задача - некоторые поля в схеме данных могут иметь тип STRUCT. Это такой составной тип данных, который может содержать произвольное количество полей с различными типами данных.

Тип поля RECORD (STRUCT)

Другими словами, это контейнер упорядоченных полей (группа полей, следующая в определенном порядке), каждое из которых имеет тип и имя поля. Имена полей являются необязательными, то есть структуру можно определить как STRUCT <INT64, STRING> или STRUCT <id INT64, name STRING>

, где:

  • INT64 и STRING – тип поля (обязательно);
  • id и name – имена полей (необязательно).

В схеме данных, где присутствует вложенность, там всегда тип RECORD. Внутри вложенности могут быть разные типы данных. Например, для параметров событий event_params установлены несколько разных типов:

Разные типы данных внутри "RECORD"

  • string_value - STRING
  • int_value - INTEGER
  • float_value - FLOAT
  • double_value - FLOAT

Это и есть группа полей, следующих в определенном порядке, у которых есть названия и типы (STRUCT / RECORD).

Для "расплющивания" (flatten) такой сложной структуры данных Google Analytics 4 в BigQuery используется функция UNNEST. Она позволяет вам взять массив, и развернуть его элементы в отдельные строки.

Первый SQL-запрос, который вы должны выполнить, выглядит так:

, где вместо your_table.events_* задайте свой путь к таблице с данными Google Analytics 4 в BigQuery.

Он:

  • преобразует (flatten) события GA4 в более удобный «плоский» формат;
  • извлекает ключевые параметры события (session_id, utm, page_location и др.);
  • фильтрует события за последние 90 дней;
  • выбирает только первую сессию пользователя (session_number = 1);
  • убирает записи без user_pseudo_id и session_id.

Подробная расшифровка:

LEFT JOIN UNNEST(event_params) AS ga_session_id ON ga_session_id.key = 'ga_session_id'

Из массива параметров события извлекается параметр ga_session_id. Он нужен, чтобы связать все события в одну сессию и отслеживать путь пользователя внутри нее.

LEFT JOIN UNNEST(event_params) AS ga_session_number ON ga_session_number.key = 'ga_session_number'

Извлекает номер сессии пользователя. Это позволяет отличить первый визит от повторного и анализировать поведение новых пользователей.

LEFT JOIN UNNEST(event_params) AS page_location ON page_location.key = 'page_location'

Извлекает URL текущей страницы. Это нужно, чтобы строить путь пользователя по страницам сайта.

LEFT JOIN UNNEST(event_params) AS utm_campaign ON utm_campaign.key = 'campaign'

Извлекает рекламную кампанию из UTM_метки. Она позволяет понять, из какой кампании (campaign) пришел пользователь.

LEFT JOIN UNNEST(event_params) AS utm_medium ON utm_medium.key = 'medium'

Извлекает канал трафика из UTM_метки (например, cpc, organic, referral). Помогает классифицировать канал посещения (medium).

LEFT JOIN UNNEST(event_params) AS utm_source ON utm_source.key = 'source'

Извлекает источник трафика (source) из UTM_метки.

LEFT JOIN UNNEST(event_params) AS page_referrer ON page_referrer.key = 'page_referrer'

Извлекает реферер (предыдущий URL). Это позволяет понять, с какой страницы пользователь пришел на текущую.

Фильтры в WHERE:

AND ga_session_number.value.int_value = 1
AND user_pseudo_id IS NOT NULL
AND ga_session_id IS NOT NULL

1. ga_session_number = 1. Оставляются только события из первой сессии пользователя, то есть анализируются только новые пользователи.

2. user_pseudo_id IS NOT NULL. Остаются только события, у которых есть пользователь. Без user_pseudo_id нельзя связать события между собой.

3. ga_session_id IS NOT NULL. Остаются только события, у которых есть идентификатор сессии. Без него невозможно отслеживать путь пользователя внутри сеанса.

Пример выполнения SQL:

Результат выполнения SQL

2. Убедитесь, что каждый пользователь и сеанс имеют одинаковые свойства (или параметры).

Иногда, даже если события привязаны к одному и тому же идентификатору сеанса, некоторые события имеют все параметры, а другие - нет. Поэтому при выполнении SQL-запроса создаются дубликаты параметров для одного и того же идентификатора пользователя и идентификатора сеанса.

Только одно событие в таблице имеет значение в medium

В таком случае можно воспользоваться функциями FIRST_VALUE и LAST_VALUE для корректного анализа входных источников и путей пользователей.

Полная расшифровка запроса:

REGEXP_REPLACE(REGEXP_REPLACE(LOWER(page_location), r'(\?.*)$', ''), r'/$', '')

Это:

  • переводит URL в нижний регистр
  • убирает GET-параметры (?utm=...)
  • убирает слэш на конце (/page/ → /page)

Чтобы одинаковые страницы не считались разными из-за параметров. Зачастую URL-адрес страницы имеет разную форму, хотя перенаправляет посетителей на одну и ту же страницу.

Четыре URL-адреса ниже имеют разные значения, но в любом случае направляют вас на одну и ту же страницу Google.)

  1. https://google.com
  2. https://google.com/
  3. https://google.com?utm_source=medium
  4. https://google.com/?utm_source=medium

Вот почему используется функция REGEXP_REPLACE (чтобы отбросить ненужные буквы и символы).

FIRST_VALUE(country) OVER(PARTITION BY user_pseudo_id, session_id ORDER BY event_timestamp)
FIRST_VALUE(device) OVER(PARTITION BY user_pseudo_id, session_id ORDER BY event_timestamp)

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

Предположим, посетитель находится на борту самолета, и на момент начала сеанса самолет находится в США. Но при посещении следующей страницы он уже был в Канаде. В этом случае мы бы предпочли считать, что посетитель вылетает из США.

LAST_VALUE(utm_campaign) ...
LAST_VALUE(utm_medium) ...
LAST_VALUE(utm_source) ...

Берет последнюю не-NULL UTM-метку внутри сессии.

LAST_VALUE(REGEXP_REPLACE(...page_referrer...)) OVER(...)

То же, что и с URL:

  • строчные буквы;
  • убраны параметры;
  • убран слэш на конце;
  • выбираем последнее ненулевое значение в сессии.

Нужно для корректного анализа входных источников и путей пользователей.

Результат выполнения второго SQL-запроса:

Результат выполнения SQL

3. Удалите последовательные дубликаты URL-адресов одной и той же страницы в одном и том же идентификаторе сеанса

В большинстве случаев, когда посетитель попадает на страницу А, могут происходить различные события, такие как session_start, page_view, user_engagement, scroll и другие. Нам нужно оставить только уникальные URL-адреса страниц в пределах одного сеанса, удаляя повторяющиеся последовательные посещения одного и того же URL.

Для этого выполните следующий запрос:

Этот SQL-запрос делает фильтрацию последовательных повторов одного и того же URL в рамках одного сеанса, оставляя только уникальные последовательные страницы для каждого пользователя.

В запросе используются функции смещения LAG и LEAD.

  • LAG(page_location) - берет предыдущий URL в рамках того же пользователя и сеанса;
  • LEAD(page_location) - берет следующий URL.

Средний подзапрос с CASE WHEN END не менее важен:

CASE
WHEN previous_page_location IS NULL THEN 'remain'
WHEN previous_page_location <> page_location THEN 'remain'
WHEN previous_page_location = page_location AND page_location <> next_page_location THEN 'del'
END AS remain_or_del

Условия:

  • если предыдущего URL нет (IS NULL) - оставляем (remain);
  • если предыдущий URL отличается от текущего - оставляем (remain);
  • если текущий URL такой же, как предыдущий, но следующий отличается - помечаем как удаляемый (del).

По сути, это удаляет неуникальные последовательные дубликаты одного и того же URL в сеансе, но оставляет первый и последний в блоке повторов.

WHERE remain_or_del = 'remain'
ORDER BY user_pseudo_id, session_id, event_timestamp

Фильтрует только те события, которые помечены как remain. В результате мы получаем цепочку событий, где одинаковые последовательные страницы сокращены до одной записи.

Результат выполнения SQL, включая предыдущие запросы:

Результат выполнения SQL

4. Отметьте порядковый номер посещения каждой страницы для каждого сеанса

Мы можем пометить visit_order для каждого URL-адреса страницы на основе event_timestamp в порядке возрастания. Для этого можно использовать функцию ROW_NUMBER.

Таким образом:

  • для каждого пользователя и каждой сессии создается последовательный список посещенных страниц;
  • каждое посещение имеет уникальный порядковый номер (visit_order) и дополнительные параметры сессии;

ROW_NUMBER() присваивает порядковый номер каждой записи внутри группы. PARTITION BY user_pseudo_id, session_id группирует данные по пользователю и сессии. ORDER BY event_timestamp - нумерация происходит по времени события, то есть по последовательности посещения страниц.

Результат: visit_order показывает, какая страница была посещена первой, второй и т.д. в рамках одной сессии.

Результат выполнения SQL, включая предыдущие запросы:

Результат выполнения SQL

5. Категоризируйте все страницы

Сам URL страницы выглядит неудобочитаемым, поэтому на этом этапе можно классифицировать все страницы, чтобы сделать их понятными и легко читаемыми.

Автор в своем решении предлагает следующую команду:

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

  • точные совпадения (=) - конкретные страницы (Google Home, Joshua Intro и т.д.).
  • частичные совпадения (CONTAINS_SUBSTR) - категории страниц (Policy Pages, Event Pages).
  • все остальное - ETC (прочие страницы).

У каждого проекта будет своя категоризация. В качестве примера я буду использовать такой вариант SQL, где категория выбраны исходя из URL-адресов страниц:

Если кратко, то здесь идет перечисление разделов моего блога (Google Tag Manager, Google Analytics 4, Яндекс Метрика, Яндекс Тег Менеджер, VK Реклама, Facebook *, Looker Studio и др.) на основе ссылки.

* Деятельность американской компании Meta (бывшая Facebook) запрещена в России, организация признана экстремистской.

Результат выполнения SQL, включая предыдущие запросы:

Результат выполнения SQL

6. Возвращайте каждую сессию к шагу 10 (step 10)

Скопируйте нижеприведенный SQL и вставьте его в BigQuery вместе с предыдущими частями:

Этот SQL создает таблицу для построения диаграммы Санкей по шагам сессии.

Подробная расшифровка:

MAX((SELECT page_group FROM CTE_user_session_visit_pagegroups SUB
WHERE MAIN.user_pseudo_id = SUB.user_pseudo_id
AND MAIN.session_id = SUB.session_id
AND visit_order = 1)) AS step01

Для каждого пользователя и сессии берется page_group для конкретного visit_order (первый визит - step01). Используется функция MAX, чтобы агрегировать подзапрос в один результат на сессию. Аналогично для step02, step03 и так далее.

GROUP BY user_pseudo_id, session_id, country, device, utm_campaign, utm_medium, utm_source, page_referrer

Группировка по пользователю, сессии и всем атрибутам сессии. Благодаря этому каждая строка = одна сессия пользователя со всеми параметрами и упорядоченными шагами страниц.

ORDER BY step01, step02, ...

Сортирует сессии по последовательности посещtнных страниц (step01 → step10).

В запросе создаются step01 … step10 - это максимально отслеживаемые шаги сеанса.

Причины:

  • обычно пользователи просматривают не более 10 страниц за сессию для анализа;
  • если сессия длиннее 10 шагов - дальнейшие шаги игнорируются, чтобы не перегружать таблицу;
  • это стандартный прием при построении Sankey-диаграмм (фиксированное число шагов удобно для визуализации);
  • такое количество шагов используется в Google Analytics.

Итоговый SQL-запрос в BigQuery на данных Google Analytics 4 будет выглядеть так:

И результат его выполнения:

Результат выполнения всего SQL-запроса

Визуализация данных

В оригинальной статье автор приводит пример, где он использует для построения диаграммы Санкей сервис Redash. Это платформа для визуализации данных и бизнес-аналитики, которая предоставляет простой интерфейс для создания дашбордов, запросов и отчетов.

Конечный результат у Джошуа получился таким:

Конечный результат

Мы же будем использовать другой подход. На основе нашей итоговой таблицы из BigQuery можно построить диаграмму Sankey в Python с помощью библиотеки Plotly, которая отлично поддерживает Sankey-диаграммы.

В правом углу результатов запроса нажмите на Open in и выберите Notebook:

Open in - Notebook

В открывшемся окне в меню программы нажмите Выполнить все, чтобы запустить выполнение кода:

Выполнить все ячейки программы

Вас могу попросить дать доступ к приложению Colab Enterprise. Сделайте это и дождитесь завершения работы программы.

Как только это произойдет, вы увидите, как последняя ячейка pandas_df = bq_df.to_pandas() преобразует табличные данные BigQuery в DataFrame.

Преобразование данных в DataFrame

В конце программы вам необходимо создать еще ячейки и вставить туда фрагменты кода для создания диаграммы Санкей. Я для своего примера попросил ChatGPT сгенерировать мне код, учитывая все шаги последовательности (от step01 до step10). Пример моего варианта - у вас может быть другой:

Подробное пояснение:

1. Берем только нужные шаги

Это создает список ["step01", "step02", ..., "step09", "step10"] и исключает шаги, которых нет в таблице.

2. Создаем список узлов (nodes)

Каждое возможное значение на каждом шаге - это узел.

3. Убираем дубли и создаем индекс узлов

Plotly требует, чтобы каждый узел был просто номером: 0, 1, 2….
Эта таблица делает:

  • "step01::Visit" → 0
  • "step02::View" → 1
  • "step02::Cart" → 2

4. Строим переходы между шагами

Проходим:

  • step01 → step02
  • step02 → step03
  • ...
  • step09 → step10

И считаем сколько пользователей так переходили:

Дальше мы превращаем это в номера узлов:

5. Чистые подписи узлов

Убираем stepXX::.

6. Цвета узлов - по шагам

Каждый шаг получает свой цвет:

То есть все узлы на step01 - одного цвета, все на step02 - другого, и т.д.

7. Рисуем диаграмму Sankey

Узлы:

  • label - подписи
  • color - назначенные цвета
  • pad - отступы
  • thickness - толщина блоков

Связи:

  • source - узлы «откуда»
  • target - узлы «куда»
  • value - сколько переходов
  • color - цвет линий

8. Настройки вида

Размер, шрифт, заголовок - чисто визуальные настройки:

Вот так выглядит итоговый результат визуализации (диаграмма Санкей / Sankey diagram):

Визуализация путей пользователей на данных Google Analytics 4 в BigQuery

ChatGPT предложил мне улучшить мою диаграмму:

  • перерисовать диаграмму под твой стиль;
  • сгруппировать редкие значения в «Other»;
  • уменьшить количество узлов;
  • сделать горизонтальную/вертикальную версию;
  • вынести код в функцию или класс.

Я согласился и получил вот такую обновленную диаграмму:

Обновленная диаграмма ("улучшения" от ChatGPT)

Далее начинается творческая работа с настройками диаграммы: вы можете группировать узлы, менять стили и цвета, скрывать лишние элементы, изменять количество шагов последовательности и в целом адаптировать визуализацию так, чтобы она стала максимально понятной и удобной для анализа, а также была похожа на отчеты и Исследования Google Analytics 4!

Получайте бесплатные уроки и фишки

По контекстной, таргетированной рекламе и аналитике