Отчет Яндекс Метрики "Посетители и клиенты" с помощью Logs API, Python и чат-бота
Экспериментальный материал по воспроизведению отчета Яндекс Метрики "Посетители и клиенты" путем выгрузки данных по Logs API и написанию кода на Python с помощью чат-бота.
Начало
С помощью этого руководства я хотел бы продемонстрировать вам уникальные возможности искусственного интеллекта и технологий, которые просочились во все сферы нашей деятельности. Из него вы узнаете, как интернет-маркетологу и аналитику данных с любым опытом работы (даже новичку, никогда не изучавшему языки программирования) всего за каких-то 60 минут написать полноценную программу, которая воссоздаст интерфейсный отчет Яндекс Метрики. И при этом вы не напишите ни одной строчки кода самостоятельно!
На этот раз без прелюдий. Я не планирую в этой статье описывать историю возникновения ИИ и давать определение нейросетям, рекомендовать вам какие-то конкретные решения или давать советы по написанию промптов. Все это вы можете найти самостоятельно в Интернете. Тем более, что таких материалов в сети за последние несколько лет появилось больше, чем всех публикаций по интернет-маркетингу за все время существования индустрии.
Дополнительно к прочтению:
- Искусственный интеллект: краткая история
- Глубокий интеллект. Как зародились нейросети и почему они изменят всё
- История нейронных сетей в СССР
- Нейросети: начало или что было до ChatGPT
- Использование нейросетей с Google Tag Manager
Хотя я периодически решаю задачи на Python и создаю материалы по выгрузке данных через API, не могу сказать, что чувствую себя достаточно уверенно и способен написать любой код. Как и многие из вас, я постоянно совершенствуюсь, изучаю справочные руководства, пробую различные подходы, использую готовые библиотеки и примеры скриптов, когда это возможно. В своей работе я использую те же самые инструменты, что и большинство специалистов. А обучаюсь, выполняя конкретные поставленные задачи. Никаких специальных курсов или основ программирования на Python я не проходил. И, как показывает практика, в последнее время это уже и не нужно. Используя возможности различных нейросетей и чат-ботов, мы можем получать вполне пригодные для использования решения по выгрузке данных и автоматизации различных ETL-процессов.
Например, можно извлечь данные из Яндекс Метрики, преобразовать их и передать в другое место, например, в базу данных, с целью выполнения различных дополнительных операций (анализировать данные в реальном времени, анализировать логи, выполнять мониторинг системы, машинное обучение и прогнозирование спроса на товары и услуги, объединять данные из разных источников и т.д. и т.п.). Все это можно сделать с помощью программирования, SQL, базы данных и Logs API.
Но как решать такие задачи в 2025 году, не зная основ работы с API и не умея писать код? Это и есть основная цель данного руководства - показать вам, что даже не имея каких-то глубоких знаний в программировании, но примерно представляя, что желаете получить на выходе (в конце), какой результат должен быть достигнут, вы все равно можете решать уникальные аналитические задачи за пределами интерфейса Яндекс Метрики. И для этой демонстрации я выбрал очень интересный отчет "Посетители и клиенты", который собирает обезличенную информацию о визитах посетителей вашего сайта и предоставляет общее сведения о каждом пользователей.
Отчет чем-то напоминает карточки клиентов в CRM-системе. Он примечателен тем, что все действия о визитах привязаны к конкретным пользователям. Метрика учитывает посетителей по анонимным идентификаторам браузеров, которые сохраняются в cookie (он же ClientID).
А это означает, что использовать API отчетов Яндекс Метрики для решения этой задачи не получится, так как в этом API нет поля с идентификатором пользователя. А вот в Logs API (неагрегированные данные) он есть. И учитывая то, что группировка в отчете выполняется по идентификатору пользователя, все другие метрики тоже рассчитываются исходя из выполненных визитов и событий конкретного посетителя сайта - Первый визит, Последний визит, Всего визитов, Общее время на сайте, Цели, Количество покупок и Общая стоимость покупок.
Я не буду вдаваться в теоретический экскурс и еще раз писать о том, что такое Logs API и сырые данные Яндекс Метрики, чем они отличаются от агрегированной статистики, которую вы видите в стандартных отчетах в интерфейсе, и какие задачи можно решать с помощью такой выгрузки. Все это подробно описано других в материалах, ссылки на которые я привожу ниже. Обязательно прочитайте их:
- Введение в API Яндекс Метрики
- Математический расчет поведенческих факторов в Яндекс Метрике
- Математический расчет целевых показателей в Яндекс Метрике
- Загрузка собственных данных Яндекс Метрики в ClickHouse
- Воронки и когортный анализ на данных Яндекс Метрики
- Ассоциированные конверсии в Яндекс Метрике
- Подключение Яндекс Метрики к Yandex DataLens
Я же хочу сосредоточиться на других деталях... Итак, мы определились с отчетом - это "Посетители и клиенты". Мы поняли, что для группировки данных по идентификатору пользователя необходимо использовать Logs API. Результат, который вы получите на выходе с помощью Python, должен совпадать или быть очень схожим с интерфейсом:
Как же построить такой отчет за пределами Яндекс Метрики? Давайте сделаем это!
Запрос к Logs API
Для использования API Яндекс Метрики вам необходимо создать приложение и пройти авторизацию с помощью OAuth-токена. Для этого обязательно прочтите оба эти материала в таком порядке и выполните все шаги, описанные в них:
Примечание: сохраните свой OAuth-токен и никому не показывайте и не передавайте его.
Получив токен, вы сможете использовать его для выгрузки статистики своего счетчика программным способом. Сделайте это, воспользовавшись шаблоном моего проекта в Google Colab. Авторизовавшись под своей учетной записью Gmail, откройте его по ссылке. Сохраните проект к себе на Диск с помощью команды Файл - Сохранить копию на Диске:
Перейдите ко второй ячейке и добавьте свои данные в поля ACCESS_TOKEN и COUNTER_ID.
- ACCESS_TOKEN - ваш токен доступа;
- COUNTER_ID - идентификатор вашего счетчика Яндекс Метрики.
Запрос, который мы будем использовать, написан на языке программирования Python с использованием библиотеки Павла Максимова. Она расположена по адресу https://github.com/pavelmaksimov/tapi-yandex-metrika
Поскольку библиотека является сторонней разработкой, ее необходимо установить. Она называется tapi-yandex-metrika. Для этого теперь уже в вашем проекте Colab рядом с первой ячейкой нажмите иконку запуска (значок play), чтобы код в ячейке сработал. В Colab это будет выглядеть так:
После установки библиотеки перейдите ко второй ячейке программы, где вы задали токен и указали идентификатор вашего счетчика Яндекс Метрики. Все, что останется вам изменить - это диапазон дат в переменных date1 и date2. Я буду воспроизводить отчет "Посетители и клиенты" за небольшой период, чтобы количество выгружаемых данных было невелико. Например, с 3 по 5 марта 2025 года. Поэтому в поле date1 указано значение "2025-03-03", а в date2 "2025-03-05".
Для расчета нужных полей отчета (Первый визит, Последний визит, Всего визитов, Общее время на сайте, Цели, Количество покупок и Общая стоимость покупок) нам необходимо использовать следующие поля на уровне визита (visits):
1 |
"fields": "ym:s:clientID,ym:s:visitID,ym:s:dateTime,ym:s:visitDuration,ym:s:goalsID,ym:s:purchaseID,ym:s:purchaseRevenue" |
В запросе я использую классический идентификатор пользователя ym:s:clientID. Однако Яндекс в своих расчетах использует и другой идентификатор посетителя (в рамках одного браузера), с помощью которого производится подсчет уникальных посетителей сайта в интерфейсе Метрики. Это поле ym:s:counterUserIDHash. При необходимости вы можете самостоятельно изменить в запросе поле ym:s:clientID на ym:s:counterUserIDHash.
Примечание: статистика по количеству покупок и общей стоимости покупок будет отображаться только в том случае, если вы настроите электронную коммерцию. А сведения по достигнутым целям будут доступны только после того, как вы настроите их.
Запустите вторую ячейку аналогичным образом. С помощью него мы проверяем, возможно ли вообще создание такого запроса. Если создание запроса возможно (метод evaluate), то мы получим соответствующий ответ:
- log_request_evaluation – параметр оценки возможности создания запросов логов;
- possible – возможность создания запроса логов (true – да, false – нет);
- max_possible_day_quantity - максимальное количество дней периода, который можно указать в запросах логов, с учетом текущей квоты. В данном примере – 78092 дня.
Подробнее об оценки возможности создания запроса читайте в официальной документации разработчиков Яндекса.
Теперь перейдите к третьей ячейке программы и запустите ее. На этом этапе происходит создание запроса с помощью метода created. Как только он будет создан, вам будет возвращен идентификатор запроса requestId (в моем примере - 41646032):
На скриншоте выше статус запроса – created (создан). В этом статусе запрос еще не готов, на его подготовку может уйти какое-то время (несколько секунд и более, если количество выгружаемых данных велико).
Скопируйте идентификатор запроса и перейдите к следующей ячейке, вставив ваш идентификатор в поле requestId= и запустив ее:
Посмотрите на статус запроса. Он должен быть processed (подготовлен). Если это так, то теперь вы можете перейти к следующей ячейке. В противном случае запустите ее столько раз, пока статус запроса не изменится на processed.
В следующей ячейке снова измените идентификатор запроса на свой и запустите ее:
Отчет можно будет скачать, когда он станет доступен на сервере Яндекса. Он может состоять из нескольких частей. Параметр part_number указывает номер части отчета, которую вы хотите загрузить. Чтобы узнать номер части, взгляните выше на запрос с командой info. В part_number (номер части) будет указано данное значение. Если отображается число 0, то отчет можно скачать за один раз. В параметре size указан размер файла в байтах.
Воспользуемся функцией part().to_dicts() для преобразования словаря в датафрейм. Выполните команду list = part().to_dicts() в следующей ячейке:
И следующей командой создайте датафрейм, импортировав библиотеку pandas и задав столбцам названия (для всего того большого списка полей, что были заданы в теле запроса):
Выведите статистику на экран с помощью простой команды df с названием самого датафрейма, запустив следующую ячейку программы:
Отлично! Вы только что получили неагрегированные данные Яндекс Метрики. На скриншоте выше - это статистика моего счетчика за 3-5 марта 2025 года с заданным набором полей, необходимых для воспроизведения отчета "Посетители и клиенты". Теперь начинается самое интересное - преобразование сырых данных!
Программа на Python
Как преобразовать полученные данные? Давайте предположим, что мы не знаем и не умеем писать самостоятельно код на Python, который позволил бы получить нам красивый интерфейсный отчет Яндекс Метрики. Мы ничего не знаем ни о функциях, ни о синтаксисе запроса, ни о том, какие библиотеки необходимы для преобразования статистики и т.д. и т.п.
Вы можете ничего не знать о вышеперечисленном, но вы должны обладать базовыми знаниями работы с Яндекс Метрикой и понимать природу параметров и показателей, заложенных в счетчик аналитики. Об этом я напоминаю слушателям каждый раз, когда провожу свои онлайн-обучения. Не зная, что такое ClientID, как рассчитываются визиты, что такое идентификаторы визитов и целей, количество уникальных достигнутых целей, откуда берется дата первого визита и источника у каждого пользователя, что такое реферер и прочее, вы не сможете в полной мере оценить возможности Яндекс Метрики и выполнять какие-то глубокие и детализированные анализы своей статистики. И речь не о сырых данных...
Нам же все это потребуется для построения текущего отчета. Примерно понимая, какой результат мы хотим получить на выходе, давайте начнем преобразования полученных данных. И поможет нам в этом чат-бот.
Я буду использовать Gemini, хотя для выполнения следующих шагов вы можете выбрать любой другой чат-бот (ChatGPT, Gemini, YandexGPT, Grok, GigaChat, Copilot, DeepSeek), или даже выполнять запросы в нескольких нейросетях одновременно, чтобы сравнивать полученный результат и по возможности добиваться "лучшего кода", более чистого и простого. Или можно сначала прогнать код по одной нейросети (более простой, бесплатной), а затем использовать платную версию или аналог другой, чтобы она скорректировала скрипты и упростила финальное решение. Хотя для нашей задачи это не требуется. Главное, чтобы программа удовлетворяла минимальным базовым потребностям.
По возможности, для каждого следующего этапа я буду подробно описывать логику преобразований, а за написанием самого кода обращаться к Gemini.
Первый визит пользователя
Вы уже знаете, что группировка всех данных в отчете "Посетители и клиенты" выполняется по пользователям, а если быть точнее - по их идентификаторам (см. выше). В нашей программе выгрузка данных была по полю ym:s:clientID. Это и есть первый столбец отчета, он нам понятен.
Далее идет столбец Первый визит. И вот с него начинается самое интересное.
Все дело в том, что все данные вашего счетчика Яндекс Метрики хранятся в базе данных ClickHouse на серверах Яндекса в больших таблицах. И когда Яндекс предоставляет нам в интерфейсе этот отчет, он может для каждого пользователя найти поле ym:s:isNewUser (первый визит посетителя) и извлечь дату из этого визита. Но мы так сделать не можем, поскольку для нахождения первого визита посетителя нам бы пришлось выгружать много статистики. А у нас - заданный интервал, причем всего за несколько дней. И в этот интервал первый визит пользователя может не попасть.
Как быть в таком случае? Для этого нужно обратиться к ClientID. Подробнее об анонимном идентификаторе вы можете почитать в этой статье. Идентификатор пользователя в Яндекс Метрике создается случайным образом и определяет браузер, в котором посетитель просматривает сайт. Если вы заходили на один и тот же сайт, например, с Google Chrome и Opera, в Яндекс Метрике будет зафиксировано два разных ClientID.
Найти его очень просто. Перейдите на сайт, откройте консоль разработчика (F12 в Google Chrome) и вкладку Console. Введите команду document.cookie и нажмите Enter. Там вы должны увидеть файл куки с именем _ym_uid и значением определенного идентификатора. Например, _ym_uid=1720092427287057359:
Чтобы найти это значение в Cookies, перейдите в консоли разработчика на вкладку Application – Cookies:
В этом идентификаторе "зашита" дата первого посещения пользователя. Вы можете скопировать всё значение идентификатора пользователя или же взять первые 10 цифр. Перейдите на сайт конвертера The Current Epoch Unix Timestamp и в поле Enter a Timestamp вставьте значение _ym_uid=. Вы получите удобочитаемую дату первого посещения пользователем сайта:
Отфильтровав в отчете информацию по ClientID, вы должны увидеть точно такую же дату первого визита:
Правда, такое может быть не всегда. И в других своих публикациях я привожу примеры такого поведения:
- 1 Client ID - 2 разных профиля посетителя в Яндекс.Метрике. Как?
- Почему дата первого визита не равна метке ClientID в Яндекс Метрике?
Таким образом, мы выяснили, что из значения уникального идентификатора пользователя можно извлечь дату первого посещения. А чтобы реализовать это практически, воспользуемся чат-ботом Gemini. Я спросил у него так:
У меня есть столбец ym:s:clientID в датафрейм, в котором содержится анонимный идентификатор пользователя в браузере 1737030877992681689 в формате UInt64. Напиши функцию на Python для этого столбца, чтобы он конвертировал идентификатор такого типа Unix TimeStamp в обычную дату ЧЧ.ММ.ГГГГ.
За несколько итераций и корректировок первоначального запроса Gemini выдал мне желаемый результат функции с ее подробным описанием:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# Нахождение даты первого визита пользователя import pandas as pd import datetime def convert_client_id_to_date(df, column_name='ym:s:clientID'): """ Преобразует первые 10 цифр столбца с идентификаторами клиентов в формат даты ДД.ММ.ГГГГ, учитывая, что это миллисекунды с 1 января 1970 года. Аргументы: df: DataFrame, содержащий столбец с идентификаторами клиентов. column_name: Название столбца с идентификаторами клиентов. Возвращает: DataFrame с добавленным столбцом 'first_date'. """ def convert_to_date(client_id): try: client_id_str = str(client_id) if len(client_id_str) >= 10: timestamp = int(client_id_str[:10]) # Берем первые 10 цифр и преобразуем в int return datetime.datetime.fromtimestamp(timestamp).strftime('%d.%m.%Y') # Изменено форматирование else: return None # Возвращаем None, если длина идентификатора меньше 10 except (TypeError, ValueError): return None # Возвращаем None для некорректных значений df['first_date'] = df[column_name].apply(convert_to_date) return df df = convert_client_id_to_date(df) |
Выполнив функцию и попросив результат в виде датафрейма, вы должны получить новый столбец с названием first_date:
Ручная перепроверка выборочных ClientID подтверждает преобразование идентификатора пользователя в дату первого посещения. Например, для ym:s:clientID - 1729251398305679312:
Последний визит пользователя
Следующий столбец и преобразование - это дата последнего визита пользователя за выбранный диапазон дат.
Как видите, в столбец Последний визит совсем другой формат даты - ГГГГ-ММ-ДД ЧЧ:ММ:СС. Но найти его тоже не составляет никакой проблемы. Мой запрос к Gemini:
У меня есть столбцы ym:s:clientID и ym:s:dateTime. Мне необходимо взять комбинацию этих двух столбцов и для каждого ym:s:clientID найти по последней дате ym:s:dateTime и вывести эту дату в отдельном столбце last_date. Напиши функцию на Python. Преобразуй вывод в формат ДД.ММ.ГГГГ ЧЧ:ММ:СС
Сама функция Gemini:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# Нахождение последнего визита пользователя def find_last_date_by_client(df, client_id_col='ym:s:clientID', datetime_col='ym:s:dateTime', last_date_col='last_date'): """ Находит последнюю дату для каждого клиента на основе комбинации столбцов client_id и datetime. Преобразует вывод в формат ДД.ММ.ГГГГ ЧЧ:ММ:СС. Аргументы: df: DataFrame, содержащий столбцы client_id и datetime. client_id_col: Название столбца с идентификаторами клиентов. datetime_col: Название столбца с датами и временем. last_date_col: Название столбца для сохранения последних дат. Возвращает: DataFrame с добавленным столбцом last_date в формате ДД.ММ.ГГГГ ЧЧ:ММ:СС. """ # Преобразуем столбец datetime в datetime тип, указывая формат df[datetime_col] = pd.to_datetime(df[datetime_col], format='%Y-%m-%d %H:%M:%S', errors='coerce') # Группируем DataFrame по client_id и находим максимальную дату для каждой группы last_dates = df.groupby(client_id_col)[datetime_col].max() # Объединяем DataFrame с найденными последними датами df = df.merge(last_dates, on=client_id_col, suffixes=('', '_max')) # Преобразуем максимальные даты в формат ДД.ММ.ГГГГ ЧЧ:ММ:СС df[last_date_col] = df[datetime_col + '_max'].dt.strftime('%d.%m.%Y %H:%M:%S') # Удаляем дублирующий столбец datetime df = df.drop(columns=[datetime_col, datetime_col + '_max']) return df df = find_last_date_by_client(df) |
И результат ее выполнения:
И снова ручная проверка некоторых ClientID с целью подтверждения или опровержения написанной Gemini функции:
Все сходится.
Всего визитов
Следующий столбец - это Всего визитов.
Для расчета этой метрики чат-боту требуется написать следующее: У меня есть столбцы ym:s:clientID и ym:s:visitID. Мне необходимо взять комбинацию этих двух столбцов и посчитать общее количество визитов ym:s:visitID и вывести итоговое значение в отдельном столбце sum_visits. Напиши функцию на Python
Полученный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Подсчет общего количества визитов для каждого пользователя def calculate_visit_counts(df, client_id_col='ym:s:clientID', visit_id_col='ym:s:visitID', sum_visits_col='sum_visits'): """ Вычисляет общее количество визитов для каждого clientID. Аргументы: df: DataFrame, содержащий столбцы client_id и visit_id. client_id_col: Название столбца с идентификаторами клиентов. visit_id_col: Название столбца с идентификаторами визитов. sum_visits_col: Название столбца для сохранения общего количества визитов. Возвращает: DataFrame с добавленным столбцом sum_visits. """ # Группируем DataFrame по client_id и подсчитываем количество visit_id в каждой группе visit_counts = df.groupby(client_id_col)[visit_id_col].transform('count') # Добавляем столбец с количеством визитов df[sum_visits_col] = visit_counts return df df = calculate_visit_counts(df) |
И результат в датафрейме:
Проверка по пользователю с наибольшим количеством визитов за выбранный период:
Общее время на сайте
Столбец Общее время на сайте в интерфейсе Яндекс Метрики представлен в формате ЧЧ:ММ:СС, а Logs API выгружает эту информацию в секундах.
Нам снова потребуется помощь Gemini: У меня есть столбцы ym:s:clientID и ym:s:visitDuration с типом UInt32. Мне необходимо взять комбинацию этих двух столбцов и для каждого ym:s:clientID просуммировать все числа ym:s:visitDuration и вывести итоговое значение в отдельном столбце total_time. Напиши функцию на Python, чтобы она переводила итоговое число в минуты и секунды в формате ЧЧ:ММ:СС
Функция от чат-бота:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# Расчет общего времени на сайте для каждого пользователя import pandas as pd def calculate_total_visit_duration_formatted(df, client_id_col='ym:s:clientID', visit_duration_col='ym:s:visitDuration', total_time_col='total_time'): """ Вычисляет суммарное время посещений для каждого клиента и форматирует результат в ЧЧ:ММ:СС. Обрабатывает строковые значения в столбце visit_duration. Аргументы: df: DataFrame, содержащий столбцы client_id и visit_duration. client_id_col: Название столбца с идентификаторами клиентов. visit_duration_col: Название столбца с продолжительностью посещений. total_time_col: Название столбца для сохранения суммарного времени. Возвращает: DataFrame с добавленным столбцом total_time в формате ЧЧ:ММ:СС. """ # Преобразуем значения в числовой тип, некорректные значения заменяем на NaN df[visit_duration_col] = pd.to_numeric(df[visit_duration_col], errors='coerce') # Удаляем строки с NaN в столбце visit_duration df = df.dropna(subset=[visit_duration_col]) # Группируем DataFrame по client_id и суммируем visit_duration для каждой группы total_visit_durations = df.groupby(client_id_col)[visit_duration_col].sum() # Функция для преобразования секунд в формат ЧЧ:ММ:СС def format_duration(seconds): hours = seconds // 3600 minutes = (seconds % 3600) // 60 secs = seconds % 60 return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, secs) # Применяем функцию форматирования к суммарному времени formatted_durations = total_visit_durations.apply(format_duration) # Объединяем DataFrame с отформатированными суммарными временами df = df.merge(formatted_durations.rename(total_time_col), on=client_id_col) return df df = calculate_total_visit_duration_formatted(df) |
И результат ее выполнения с новым столбцом total_time:
Проверим результат для того же ClientID в интерфейсе Яндекс Метрики:
Общее время на сайте с группировкой по каждому пользователю в Logs API и интерфейсе сходится.
Цели
Еще один интересный столбец, отображающий количество уникальных достигнутых целей за выбранный диапазон дат:
Примечателен он тем, что при выгрузке данных по Logs API поле ym:s:goalsID (номера целей, достигнутых за данный визит) имеет тип Array(UInt32), то есть данные отображаются в виде массива:
Но это нам нисколько не помешает, поскольку Gemini легко справится с написанием функции и для такого типа поля: У меня есть столбцы ym:s:clientID, ym:s:visitID и ym:s:goalsID с типом Array(UInt32). Он имеет такой формат [194421079,238914648,194421079,238914648,238914648,3033437348], то есть в нем отображаются идентификаторы целей, и они разделяются запятой. Мне необходимо посчитать уникальное количество достижений целей по всем визитам, то есть взять сумму всех целей по всем визитам для этого пользователя и убрать дубли, а затем вывести их в отдельном столбце total_goals. Напиши функцию на Python
Вот какую функцию для меня написал Gemini всего за несколько секунд:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# Подсчет уникального количества достижений целей для каждого пользователя import ast def calculate_total_unique_goals_by_client(df, client_id_col='ym:s:clientID', goals_id_col='ym:s:goalsID', total_goals_col='total_goals'): """ Вычисляет общее уникальное количество достижений целей по всем визитам для каждого клиента. Аргументы: df: DataFrame, содержащий столбцы client_id, visit_id и goals_id. client_id_col: Название столбца с идентификаторами клиентов. goals_id_col: Название столбца с идентификаторами целей (Array(UInt32)). total_goals_col: Название столбца для сохранения общего уникального количества достижений целей. Возвращает: DataFrame с добавленным столбцом total_goals. """ def get_unique_goals(goals_list_str): try: # Преобразуем строку в список goals_list = ast.literal_eval(goals_list_str) return set(goals_list) except (ValueError, SyntaxError, TypeError): return set() # Возвращаем пустое множество, если список не может быть разобран # Преобразуем строки в множества уникальных целей df['unique_goals_set'] = df[goals_id_col].apply(get_unique_goals) # Группируем по client_id и объединяем множества уникальных целей unique_goals_by_client = df.groupby(client_id_col)['unique_goals_set'].apply(lambda x: set.union(*x)) # Добавляем столбец с общим уникальным количеством целей df = df.merge(unique_goals_by_client.rename(total_goals_col), on=client_id_col, how='left') # Применяем функцию len к множествам в столбце total_goals df[total_goals_col] = df[total_goals_col].apply(lambda x: len(x)) # Удаляем временный столбец unique_goals_set df = df.drop(columns=['unique_goals_set']) return df df = calculate_total_unique_goals_by_client(df) |
И сам датафрейм выглядит так:
Я выполнил фильтрацию по тем идентификаторам пользователей, кто совершил цели (>0). И в результате данные в программе и интерфейсе Метрики сошлись:
Количество покупок
Количество покупок для каждого пользователя можно определить, посчитав идентификаторы транзакций по полю ym:s:purchaseID.
Однако и это поле имеет свои особенности. Список всех идентификаторов транзакций ym:s:purchaseID для визита имеет тип Array(String), что затрудняет итоговый подсчет уникальных значений.
Нужны преобразования... Но не беда! Код мы писать не умеем, но обратившись к Gemini, он выдаст нам его в два счета: У меня есть столбцы ym:s:clientID, ym:s:visitID и ym:s:purchaseID с типом Array(String). Посчитай общее количество ym:s:purchaseID для каждого ym:s:clientID, а затем выведи их в отдельном столбце total_purchases
Функция на Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# Расчет общего количества покупок import ast def calculate_total_purchases_by_client(df, client_id_col='ym:s:clientID', purchase_id_col='ym:s:purchaseID', total_purchases_col='total_purchases'): """ Вычисляет общее количество purchaseID для каждого clientID. Аргументы: df: DataFrame, содержащий столбцы client_id и purchase_id. client_id_col: Название столбца с идентификаторами клиентов. purchase_id_col: Название столбца с идентификаторами покупок (Array(String)). total_purchases_col: Название столбца для сохранения общего количества покупок. Возвращает: DataFrame с добавленным столбцом total_purchases. """ def count_purchases(purchase_list_str): try: # Преобразуем строку в список purchase_list = ast.literal_eval(purchase_list_str) return len(purchase_list) except (ValueError, SyntaxError, TypeError): return 0 # Возвращаем 0, если список не может быть разобран # Применяем функцию count_purchases к столбцу purchase_id df['purchase_counts'] = df[purchase_id_col].apply(count_purchases) # Группируем по client_id и суммируем purchase_counts total_purchases = df.groupby(client_id_col)['purchase_counts'].sum() # Добавляем столбец с общим количеством покупок df = df.merge(total_purchases.rename(total_purchases_col), on=client_id_col, how='left') # Удаляем временный столбец purchase_counts df = df.drop(columns=['purchase_counts']) return df df = calculate_total_purchases_by_client(df) |
И результат выполнения скрипта:
Максимальное количество покупок (2) по данным Logs API за выбранный диапазон дат совершил пользователь с ClientID = 1741067920721024911. Сравним эти данные с интерфейсным отчетом Яндекс Метрики:
Как видите, они сходятся!
Общая стоимость покупок
Последний столбец в отчете "Посетители и клиенты" - это Общая стоимость покупок.
Общая стоимость покупок в Logs API содержится в поле ym:s:purchaseRevenue. Это поле имеет тип Array(Float64) и оно тоже является массивом:
Мы уже спрашивали Gemini о чем-то похожем. Поэтому сам промпт не сильно отличается от предыдущих: У меня есть столбцы ym:s:clientID, ym:s:visitID и ym:s:purchaseRevenue с типом Array(Float64). Посчитай сумму всех покупок для каждого ym:s:clientID, а затем выведи их в отдельном столбце total_revenue
Функция для общей стоимости покупок по каждому пользователю выглядит так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# Расчет общей стоимости покупок import ast def calculate_total_revenue_by_client(df, client_id_col='ym:s:clientID', revenue_col='ym:s:purchaseRevenue', total_revenue_col='total_revenue'): """ Вычисляет общую сумму покупок для каждого clientID. Аргументы: df: DataFrame, содержащий столбцы client_id и purchaseRevenue. client_id_col: Название столбца с идентификаторами клиентов. revenue_col: Название столбца с суммами покупок (Array(Float64)). total_revenue_col: Название столбца для сохранения общей суммы покупок. Возвращает: DataFrame с добавленным столбцом total_revenue. """ def sum_revenue(revenue_list_str): try: # Преобразуем строку в список revenue_list = ast.literal_eval(revenue_list_str) return sum(revenue_list) except (ValueError, SyntaxError, TypeError): return 0.0 # Возвращаем 0.0, если список не может быть разобран # Применяем функцию sum_revenue к столбцу purchaseRevenue df['revenue_sums'] = df[revenue_col].apply(sum_revenue) # Группируем по client_id и суммируем revenue_sums total_revenue = df.groupby(client_id_col)['revenue_sums'].sum() # Добавляем столбец с общей суммой покупок df = df.merge(total_revenue.rename(total_revenue_col), on=client_id_col, how='left') # Удаляем временный столбец revenue_sums df = df.drop(columns=['revenue_sums']) return df df = calculate_total_revenue_by_client(df) |
А результат ее выполнения так:
Проверим транзакции для того же пользователя с ClientID = 1741067920721024911. С 3 по 5 марта 2025 года он совершил 2 транзакции на сумму 1400 рублей.
Это подтверждается простой проверкой в интерфейсе Метрики.
Финальный запрос
Поскольку расчет данных осуществлялся для каждого пользователя по его ClientID, все, что остается - удалить лишние (промежуточные) поля из датафрейма, которые нам были необходимы для расчета остальных метрик. Другими словами, нам нужно оставить только исходное поле ym:s:clientID, но удалить все остальные (ym:s:visitID, ym:s:dateTime, ym:s:visitDuration, ym:s:goalsID, ym:s:purchaseID, ym:s:purchaseRevenue), кроме расчетных.
Последний запрос к Gemini выглядит следующим образом: У меня есть столбцы в датафрейме ym:s:clientID, ym:s:visitID, ym:s:visitDuration, ym:s:goalsID, ym:s:purchaseID, ym:s:purchaseRevenue, first_date, last_date, sum_visits, total_time, total_goals, total_purchases. Оставь только ym:s:clientID first_date, last_date, sum_visits, total_time, total_goals, total_purchases и выведи его в финальном датафрейме
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# Финальный отчет "Посетители и клиенты" import pandas as pd def select_and_create_final_dataframe(df): """ Отбирает нужные столбцы и создает финальный DataFrame. Аргументы: df: Исходный DataFrame. Возвращает: Финальный DataFrame с выбранными столбцами. """ selected_columns = [ 'ym:s:clientID', 'first_date', 'last_date', 'sum_visits', 'total_time', 'total_goals', 'total_purchases' ] final_df = df[selected_columns].copy() # Используем copy(), чтобы избежать SettingWithCopyWarning return final_df final_df = select_and_create_final_dataframe(df) |
И переименовка столбцов на схожие названия в интерфейсе Метрики: В моем датафрейм final_df есть столбцы с названиями ym:s:clientID, first_date, last_date, sum_visits, total_time, total_goals, total_purchases. Переименуй их в Посетитель, Первый визит, Последний визит, Всего визитов, Общее время на сайте, Цели, Количество покупок, Общая стоимость покупок
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# Изменение названия столбцов def rename_columns_inplace(df): """ Переименовывает столбцы в DataFrame на месте (без создания новых столбцов). Аргументы: df: Исходный DataFrame. Возвращает: DataFrame с переименованными столбцами. """ rename_dict = { 'ym:s:clientID': 'Посетитель', 'first_date': 'Первый визит', 'last_date': 'Последний визит', 'sum_visits': 'Всего визитов', 'total_time': 'Общее время на сайте', 'total_goals': 'Цели', 'total_purchases': 'Количество покупок', 'total_revenue': 'Общая стоимость покупок' } df.rename(columns=rename_dict, inplace=True) return df final_df = rename_columns_inplace(final_df) |
Окончательный результат выполнения программы:
Итоги
На написание этого руководства мне потребовалось 5 часов. А на написание самой программы на Python - 1 час.
Вот вам и разница! Не обладая навыками программирования, мало что представляя о функциях, переменных, циклах, датафреймах, но зная примерно результат, который вы хотите получить, а также специфику работы самого счетчика Яндекс Метрики, принципы работы и то, какие параметры и показатели заложены в его систему, с помощью чат-ботов можно добиться поразительных результатов! Эта и была основная цель данной статьи.
Надеюсь, что после прочитанного у вас не останется сомнений в том, что новые технологии необходимо внедрять в свои проекты и бизнес-решения, а также стараться передавать ИИ ту работу, которую можно автоматизировать. Тем более что от интернет-маркетолога и аналитика данных не требуется оптимизировать код и делать его производительным и быстродействующим. Если же это необходимо, вам снова могут помочь те же чат-боты или инженеры данных/разработчики, которые разбираются в этом гораздо лучше.
Как и обещал, прикладываю ссылку на мой чат с Gemini для этого руководства. В России он не работает, поэтому для открытия воспользуйтесь VPN. И еще раз ссылка на итоговую программу в Colab. Успехов!