Русский
Русский
English
Статистика
Реклама

Продуктовая аналитика

Пошаговая инструкция как отправить данные в Amplitude с помощью Google Tag Manager

10.02.2021 12:04:06 | Автор: admin

Что это

Самая подробная инструкция по передаче событий с сайта в сервис продуктовой аналитики Amplitude с помощью Google Tag Manager.

Инструкция написана аналитиками Adventum, digital-агентства и эксклюзивного партнера Amplitude в России и СНГ.

Для кого

  1. Для продакт-менеджеров и аналитиков, которые хотят внедрить Amplitude на сайт,

  2. Для разработчиков, которые будут внедрять Amplitude на сайт,

  3. Для всех, кто слышал об Amplitude и хочет передавать данные о событиях на сайте в эту систему.

Зачем

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

Чтобы события с сайта быстро попадали в Amplitude, вам поможет Google Tag Manager (GTM) платформа, которая дает возможность управлять JavaScript и HTML-тегами сайта, настроить сбор данных путем добавления кода GTM.

Если вы ни разу не настраивали передачу событий через эти системы, придется потратить много времени, чтобы разобраться. Поэтому мы создали пошаговую инструкцию, которая поможет продакту и разработчику быстро настроить передачу данных в Amplitude с помощью Google Tag Manager.

Содержание

  1. Создайте аккаунты в Amplitude и GTM
    1.1 Создание проекта в Amplitude
    1.2 Создание проекта в GTM

  2. Добавьте код GTM на сайт

  3. Добавьте тег для инициализации Amplitude JavaScript SDK

  4. Создайте реестр событий и свойств пользователя

  5. Три способа настройки GTM для передачи событий в Amplitude
    5.1 Способ 1
    5.2 Способ 2
    5.3 Способ 3

  6. Настройте GTM для передачи свойств пользователя

  7. Настройте GTM для передачи User ID

  8. Проверьте отправку событий и свойств

  9. Файлы контейнеров из статьи

1. Создайте аккаунты в Amplitude и GTM

Создание проекта в Amplitude

Если вы до этого еще не передавали данные через эти системы, создайте аккаунты в Amplitude и GTM.

Чтобы создать аккаунт в Amplitude, на главной странице нажмите Explore Product:

Укажите почту для регистрации:

Выберите сферу вашей деятельности:

Укажите имя, название компании и нажмите Explore demo now:

Перейдите в настройки:

И нажмите Create Organization:

Укажите название компании, url и нажмите Create:

Далее можно указать сферу деятельности команды в компании или пропустить этот шаг, нажав на крестик в углу.

Если вы собираете данные от жителей Европейского союза (ЕС), то вам нужно будет заполнить Data Processing Agreement (DPA) Соглашение о защите персональных данных (в соответствии с правилами Общего регламента по защите данных (GDPR)).

Если не собираете нажмите We Dont Need a DPA:

Теперь добавьте проект. Рекомендуем создавать два проекта: тестовый и основной. Это делается для того, чтобы на тестовом проекте можно было проверить внедряемые события, а затем начать сбор корректных данных на основном аккаунте.

Чтобы создать проект в настройках, перейдите на вкладку Projects и нажмите Create Project:

Введите имя проекта и нажмите Create:

Готово вы создали проект. Теперь нужно создать проект в Google Tag Manager и приступить к настройке.

Создание проекта в GTM

Зайдите на главную страницу GTM и нажмите Start for free.

Для входа используйте аккаунт Google или создайте при регистрации, если ранее его не настраивали.

Нажмите Создать аккаунт:

Введите название аккаунта и выберите страну.

Затем укажите название контейнера, платформу, и нажмите Создать. После этого нужно согласиться с условиями использования Диспетчера тегов Google.

Предварительные действия завершены. Теперь у вас есть аккаунты в Amplitude и GTM. Приступим к настройке передачи событий.

2. Добавьте код GTM на сайт

ID вашего контейнера GTM находится в верхнем правом углу.

Контейнер GTM это небольшой фрагмент кода на JavaScript, который добавляется на страницы сайта.

Чтобы внедрить GTM на своем сайте, выполните следующие действия:

1. Скопируйте приведенный ниже код JavaScript, замените GTM-XXXX идентификатором своего контейнера и добавьте код на все страницы сайта как можно ближе к открывающему тегу <head>.

<!-- Google Tag Manager --><script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-XXXX');</script><!-- End Google Tag Manager -->

2. Скопируйте следующий фрагмент и вставьте его на все страницы сайта сразу после открывающего тега <body>, заменив GTM-XXXX идентификатором контейнера.

<!-- Google Tag Manager (noscript) --><noscript><iframe src="http://personeltest.ru/aways/www.googletagmanager.com/ns.html?id=GTM-XXXX"height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript><!-- End Google Tag Manager (noscript) -->

3. Добавьте тег для инициализации Amplitude JavaScript SDK

Для передачи событий будем использовать Amplitude JavaScript SDK. Добавим тег для инициализации SDK.

В Amplitude нужно зайти в Sources & Destinations:

И нажать Add Data Source:

Далее выберите JavaScript SDK и нажмите Next:

Целиком скопируйте код SDK в появившемся окне:

Перейдите в GTM и на главной странице аккаунта нажмите Добавить новый тег:

Сверху укажите название тега: tagAmplitudeSDK. Приписка в начале названия позволит легче находить тег в интерфейсе GTM.

Нажмите на иконку конфигурации тега:

В появившемся списке выберите Пользовательский HTML:

И вставьте скопированный в Amplitude код JS SDK:

После этого нажмите на иконку триггера:

И установите триггер All pages, чтобы код SDK инициализировался при загрузке каждой страницы сайта:

Нажмите Сохранить:

Amplitude периодически дорабатывает JS SDK и публикует новые версии. Не забывайте проверять наличие новых версий SDK и обновлять код в теге, это поможет предотвратить ошибки при передаче данных. Актуальную версию смотрите на странице JavaScript и узнавайте об изменениях в релизах.

Далее нужно добавить:

  • тег для отправки событий просмотра страниц,

  • теги для отправки событий и свойств пользователя,

  • триггеры (условия, при котором активируются теги),

  • переменные (объекты для формирования названий событий и значений свойств).

Как это сделать, рассказываем ниже.

Чтобы понимать, какие действия пользователя нужно отправлять, рекомендуем составить реестр событий и свойств пользователя.

4. Создайте реестр событий и свойств пользователя

Событие действие, которое пользователь выполняет в продукте (запускает игру, добавляет товар в корзину) или действие, связанное с пользователем (push-уведомления).

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

Нужно ли отправлять все действия пользователя? Чаще всего нет, по крайне мере, на первых этапах. Начните с воронок основных процессов в продукте: онбординг, регистрация, покупка.

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

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

Вариант реестра может выглядеть так:

События:

Категория события

Название события

Описание

Свойства события

1

conversions

conversionStep_[registration]_success

Успешная регистрация пользователя

registration_type = {{тип регистрации: вк или email}}

2

conversions

conversionStep_[purchase]_main_page_view

Просмотр главной страницы

3

conversions

conversionStep_[purchase]_item_view

Просмотр карточки товара

item_id = {{id товара}},

item_price = {{цена товара}}

4

conversions

conversionStep_[purchase]_add_to_cart

Товар добавлен в корзину

item_id = {{id товара}},

item_price = {{цена товара}}

5

conversions

conversionStep_[purchase]_success

Успешная покупка

purchase_sum = {{сумма покупки}},

item_amount = {{количество товаров в покупке}}

6

actions

makeActions_[favorite]_add

Товар добавлен в избранное

item_id = {{id товара}},

itemprice = {{цена товара}}

Свойства пользователя:

Название свойства

Описание

Пример значений

1

age

Возраст пользователя

23

2

gender

Пол

мужской

3

favoritesamount

Количество товаров в избранном

4

4

registrationdate

Дата регистрации в iso формате

2015-04-20

Реестр событий может быть оформлен в разном формате. Можно добавить столбцы, которые будут давать дополнительную информацию о событиях и свойствах пользователя. Главное, чтобы реестр был понятным и позволял любому сотруднику, который будет работать с данными, быстро сориентироваться в событиях, которые отправляются в Amplitude.

5. Три способа настройки GTM для передачи событий в Amplitude

Мы описываем 3 способа настройки GTM для передачи событий в Amplitude, выберите подходящий для вашего продукта:

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

Способ 2 позволяет быстро вносить изменение изменения в свойства отправляемых событий через GTM. Подойдет для продуктов с большим количеством свойств, если их значения не содержат массивов.

Способ 3 позволяет передавать значения свойств событий массивами. Подойдет для сложных продуктов.

Способ 1

Уровень данных (dataLayer) хранит и передает информацию с вашего сайта в Google Tag Manager.

Чтобы отправить данные в dataLayer, используют метод push.

В этом способе рассмотрим отправку пуша следующего вида:

<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e1',        'event_cat': 'registration',        'event_name': 'success',        'event_param': {'registration_type': '{{тип регистрации}}'}    });</script>

event:

addEvents_makeConversions действия в рамках определенной воронки,

addEvents_clickLinks клики по ссылкам, ведущим на внешний домен,

addEvents_viewErrors ошибки,

addEvents_useNavigations клики по навигационным элементам,

addEvents_makeActions все прочие действия.

Для простоты можете делить события на addEvents_makeConversions и addEvents_makeActions.

event_id идентификатор события согласно инструкции (в примере d-v1-e1: версия инструкции (v - version) 1, номер события (e - event) 1),

event_cat категория события,

event_name название события,

event_param параметр для передачи свойств.

Согласно реестру событий нам нужно отправлять следующие пуши:

<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e1',        'event_cat': 'registration',        'event_name': 'success',        'event_param': {'registration_type': '{{тип регистрации}}'}    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e2',        'event_cat': 'purchase',        'event_name': 'main_page_view',        'event_param': ''    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e3',        'event_cat': 'purchase',        'event_name': 'item_view',        'event_param': {            'item_id': '{{id товара}}',            'item_price': '{{цена товара}}'        }    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e4',        'event_cat': 'purchase',        'event_name': 'add_to_cart',        'event_param': {            'item_id': '{{id товара}}',            'item_price': '{{цена товара}}'        }    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e5',        'event_cat': 'purchase',        'event_name': 'success',        'event_param': {            'purchase_sum': '{{сумма покупки}}',            'item_amount': '{{количество товаров в покупке}}'        }    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'event_param': {            'item_id': '{{id товара}}',            'item_price': '{{цена товара}}'        }    });</script>

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

Если вы планируете отправлять одно и тоже событие как со свойствами, так и без них, тогда, в случае, если событие отправляется без какого-то свойства, нужно прописывать в пуше значение этого свойства null. Например:

<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'event_param': {            'item_id': null,            'item_price': '{{цена товара}}'        }    });</script>

Теперь создайте тег, который будет отправлять эти события в Amplitude. Для начала добавьте нужные для тега переменные и триггер.

Для создания триггера в раздел Триггеры и нажмите Создать:

В появившемся окне введите название DL_event_addEvents и нажмите на иконку настроек триггера:

В появившимся списке выберите Пользовательское событие:

Введите в поле Имя события addEvents и поставьте галочку напротив Использовать регулярные выражения. Это позволит GTM определить события, которые вы отправляете в dataLayer (если в параметре event будет содержаться addEvents, то триггер сработает).

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

Чтобы создать переменные, зайдите в раздел Переменные и нажмите Создать:

Кликните на иконку конфигурации переменной:

В появившемся окне нажмите Переменная уровня данных (datalayer variables), так как вы будете отправлять события и параметры через dataLayer.push.

Введите имя переменной, используя формат varDL_{{название переменной}}.

В поле Имя переменной уровня данных впишите {{название переменной}}.

Для параметра event_cat переменная будет выглядеть так:

Таким же образом добавляйте остальные параметры:

Имя переменной в GMT

Имя переменной в dataLayer

varDL_event_cat

event_cat

varDL_event_name

event_name

varDL_event_param

event_param

Добавьте таблицу поиска, которая будет формировать название события:

  • зайдите в раздел Переменные и нажмите Создать.

  • в появившемся окне введите название LT_Amplitude_event, нажмите на иконку конфигурации переменной и в появившемся списке выберите Таблица поиска.

Установите входную переменную {{event}} и добавьте 5 строк. Эта таблица поиска будет определять тип события и составлять название, используя переменную категории (varDL_event_cat) и названия (varDL_event_name). Вам нужно для каждого типа событий добавить строку в таблице поиска:

Входные данные

Результат

addEvents_viewErrors

viewErrors_[{{varDL_event_cat}}]_{{varDL_event_name}}

addEvents_makeActions

makeActions_[{{varDL_event_cat}}]_{{varDL_event_name}}

addEvents_makeConversions

conversionStep_[{{varDL_event_cat}}]_{{varDL_event_name}}

addEvents_useNavigations

useNavigations_[{{varDL_event_cat}}]_{{varDL_event_name}}

addEvents_clickLinks

clickLinks_[{{varDL_event_cat}}]_{{varDL_event_name}}

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

Нажмите Сохранить.

Триггер и переменные добавлены. Теперь переходим к созданию тегов.

На вкладке Обзор нажмите Добавить новый тег:

Назовите новый тег tagWA_Amplitude_Events, затем нажмите иконку конфигурации тега и выберите в списке Пользовательский HTML (так же, как делали при добавлении AMPLITUDE JS SDK).

Код состоит из двух функций:

Функция mainTagFunction отправляет события в Amplitude и обнуляет значение event_param в dataLayer. Обнуление нужно потому, что метод push только добавляет данные в dataLayer. Если не обнулить event_param после отправки пуша, в параметры следующего события попадут параметры из предыдущего. Такое еще может произойти, если вы случайно отправите событие без передачи event_param.

Функция amplitudeSdkSearch проверяет, инициализировался ли Amplitude JS SDK. Если SDK инициализирован, вызывается функция mainTagFunction. Если не добавить эту функцию, может произойти такая ситуация: код SDK еще не загрузился, а пуш события уже отправлен, тогда событие не отправится в Amplitude.

<script>(function(){  var amplitudeCounter = 0;  function mainTagFunction() {  try {    amplitude.getInstance().logEvent('{{LT_Amplitude_event}}', {{varDL_event_param}});    dataLayer.push({'event_param': ''});  } catch (e) {} }    function amplitudeSdkSearch() { if (window.amplitude) mainTagFunction();    else {      if (amplitudeCounter < 50) {         setTimeout(amplitudeSdkSearch, 100);         amplitudeCounter++;      }    }}   amplitudeSdkSearch();})();</script>  

Затем выберите для тега триггер DL_event_addEvents:

Нажмите Сохранить и тег добавится в контейнер.

Так же добавьте тег tag_Amplitude_SDK.

Amplitude периодически дорабатывает JS SDK и публикует новые версии. Не забывайте проверять наличие новых версий SDK и обновлять код в теге, это поможет предотвратить ошибки при передаче данных. Актуальную версию смотрите на странице JavaScript и узнавайте об изменениях в релизах.

Настройка GTM завершена.

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

Если вы хотите передавать свойства событий в другие системы, например, Google Analytics, то понадобится дополнительно настраивать GTM, либо отправлять свойства в dataLayer в другом виде.

Варианты отправки событий, лишенные этих недостатков, будут описаны в способе 2 и 3.

Cпособ 2

Используя данный способ, не получится передавать значения параметров в виде массивов.

Этот способ отправки событий основан на создании переменных уровня данных в GTM для каждого свойства события. Вам понадобится таблица поиска для сопоставления названий событий и их свойств, а код главного тега будет другим.

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

<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e0',        'event_cat': 'example',        'event_name': 'success',        'property1': 'value1',        'property2': 'value2'    });</script>

Этот пуш отличается от пуша из первого способа тем, что вы передаете свойства события как отдельные пары ключ-значение, а не в event_param.

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

<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e1',        'event_cat': 'registration',        'event_name': 'success',        'registration_type': '{{тип регистрации}}'    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e2',        'event_cat': 'purchase',        'event_name': 'main_page_view'    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e3',        'event_cat': 'purchase',        'event_name': 'item_view',        'item_id': '{{id товара}}',        'item_price': '{{цена товара}}'    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e4',        'event_cat': 'purchase',        'event_name': 'add_to_cart',        'item_id': '{{id товара}}',        'item_price': '{{цена товара}}'    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e5',        'event_cat': 'purchase',        'event_name': 'success',        'purchase_sum': '{{сумма покупки}}',        'item_amount': '{{количество товаров в покупке}}'    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'item_id': '{{id товара}}',        'item_price': '{{цена товара}}'    });</script>

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

Если вы планируете отправлять одно и тоже событие как со свойствами, так и без них, тогда, в случае если событие отправляется без какого-то свойства, нужно прописывать в пуше значение этого свойства null. Например:

<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'item_id': null,        'item_price': '{{цена товара}}'    });</script>

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

<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'item_price': '{{цена товара}}'    });</script>

Создайте переменные уровня данных.

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

Имя переменной в GMT

Имя переменной в dataLayer

varDL_event_cat

event_cat

varDL_event_name

event_name

varDL_registration_type

registration_type

varDL_item_id

item_id

varDL_item_price

item_price

varDL_purchase_sum

purchase_sum

varDL_item_amount

item_amount

После добавления этих переменных, переходим к созданию таблиц поиска.

Добавьте таблицу поиска LT_Amplitude_event.

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

В разделе Переменные нажмите Создать, введите название LT_Amplitude_params, выберите тип переменной Таблица поиска.

Важно изменить входную переменную на {{LT_Amplitude_event}}: таблица будет получать названия событий и передавать соответствующие им свойства.

Далее добавьте в таблицу строки:

  • в поле Входные данные будут названия событий,

  • в поле Результат будут свойства событий в формате {"название свойства 1": "{{название переменной 1 в gtm}}", "название свойства 2": "{{название переменной 2 в gtm}}"} . Важно использовать именно двойные кавычки (а не как в dataLayer.push одинарные), иначе свойства не будут отправляться.

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

Входные данные

Результат

conversionStep_[registration]_success

{"registration_type": "{{varDL_registration_type}}"}

conversionStep_[purchase]_item_view

{"item_id": "{{varDL_item_id}}", "item_price": "{{varDL_item_price}}"}

conversionStep_[purchase]_add_to_cart

{"item_id": "{{varDL_item_id}}", "item_price": "{{varDL_item_price}}"}

conversionStep_[purchase]_success

{"purchase_sum": "{{varDL_purchase_sum}}", "item_amount": "{{varDL_item_amount}}"}

makeActions_[favorite]_add

{"item_id": "{{varDL_item_id}}", "item_price": "{{varDL_item_price}}"}

Событие conversionStep_[purchase]_main_page_view не нужно добавлять в эту таблицу, потому что у него нет свойств.

Поставьте галочку напротив Установить значение по умолчанию и введите значение по умолчанию "" (две двойные кавычки). Так, если в пуше не будет свойств, ошибка при отправке события не возникнет.

Нажмите Сохранить, и таблица поиска добавится в контейнер.

Далее добавьте триггер DL_event_addEvents:

После этого добавьте тег tag_Amplitude_SDK.

Amplitude периодически дорабатывает JS SDK и публикует новые версии. Не забывайте проверять наличие новых версий SDK и обновлять код в теге, это поможет предотвратить ошибки при передаче данных. Актуальную версию смотрите на странице JavaScript и узнавайте об изменениях в релизах.

Тег для отправки событий и свойств будет отличаться от тега из первого способа: добавьте тег с названием tagWA_Amplitude_Events и триггером DL_event_addEvents, выберите тип тега Пользовательский HTML и добавьте этот код:

<script>(function(){  var amplitudeCounter = 0;function mainTagFunction() {  try {    var obj = JSON.parse('{{LT_Amplitude_params}}');    amplitude.getInstance().logEvent('{{LT_Amplitude_event}}', obj);  } catch (e) {} } function amplitudeSdkSearch() { if (window.amplitude) mainTagFunction();    else {      if (amplitudeCounter < 50) {         setTimeout(amplitudeSdkSearch, 100);         amplitudeCounter++;      }    }}   amplitudeSdkSearch();})();</script>

Он также содержит функцию amplitudeSdkSearch, которая проверяет, инициализировался ли Amplitude JS SDK.

Внутрь функцииmainTagFunction добавлена переменная obj, которая с помощью метода JSON.parse преобразовывает JSON обратно в объект. Это необходимо, потому что таблица поиска в GTM преобразовывает конечное значение в формат строки, а свойства события должны передаваться как объект. Именно из-за этой особенности таблицы поиска, используя второй способ, не получится передавать значения в виде массивов.

Нажмите Сохранить, и тег добавится в контейнер.

Способ 3

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

Пуши в dataLayer отличаются от пушей во втором способе тем, что в значениях свойств нужно будет добавить метод JSON.stringify:

<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e1',        'event_cat': 'registration',        'event_name': 'success',        'registration_type': JSON.stringify('{{тип регистрации}}')    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e2',        'event_cat': 'purchase',        'event_name': 'main_page_view'    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e3',        'event_cat': 'purchase',        'event_name': 'item_view',        'item_id': JSON.stringify('{{id товара}}'),        'item_price': JSON.stringify('{{цена товара}}')    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e4',        'event_cat': 'purchase',        'event_name': 'add_to_cart',        'item_id': JSON.stringify('{{id товара}}'),        'item_price': JSON.stringify('{{цена товара}}')    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e5',        'event_cat': 'purchase',        'event_name': 'success',        'purchase_sum': JSON.stringify('{{сумма покупки}}'),        'item_amount': JSON.stringify('{{количество товаров в покупке}}')    });</script>
<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'item_id': JSON.stringify('{{id товара}}'),        'item_price': JSON.stringify('{{цена товара}}')    });</script>

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

Если вы планируете отправлять одно и тоже событие как со свойствами, так и без них, тогда, в случае если событие отправляется без какого-то свойства, нужно прописывать в пуше значение этого свойства null. Например:

<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'item_id': null,        'item_price': JSON.stringify('{{цена товара}}')    });</script>

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

<script>    dataLayer.push({        'event': 'addEvents_makeActions',        'event_id': 'd-v1-e6',        'event_cat': 'favorite',        'event_name': 'add',        'item_price': JSON.stringify('{{цена товара}}')    });</script>

Добавьте следующие переменные:

Имя переменной в GMT

Имя переменной в dataLayer

varDL_event_cat

event_cat

varDL_event_name

event_name

varDL_registration_type

registration_type

varDL_item_id

item_id

varDL_item_price

item_price

varDL_purchase_sum

purchase_sum

varDL_item_amount

item_amount

Затем добавьте таблицу поиска LT_Amplitude_event:

Добавьте триггер DL_event_addEvents:

Добавьте тег tag_Amplitude_SDK.

Amplitude периодически дорабатывает JS SDK и публикует новые версии. Не забывайте проверять наличие новых версий SDK и обновлять код в теге, это поможет предотвратить ошибки при передаче данных. Актуальную версию смотрите на странице JavaScript и узнавайте об изменениях в релизах.

Затем добавьте тег для отправки событий tagWA_Amplitude_Events и триггер DL_event_addEvents, выберите тип тега Пользовательский HTML и добавьте код:

<script>(function(){  var amplitudeCounter = 0;function mainTagFunction() {  try {    var event_name = '{{LT_Amplitude_event}}';    if (event_name == 'conversionStep_[registration]_success') {      amplitude.getInstance().logEvent(event_name, {'registration_type': JSON.parse("{{varDL_registration_type}}")});        } else if (event_name == 'conversionStep_[purchase]_main_page_view') {       amplitude.getInstance().logEvent(event_name);           } else if (event_name == 'conversionStep_[purchase]_item_view') {       amplitude.getInstance().logEvent(event_name, {"item_id": JSON.parse("{{varDL_item_id}}"), "item_price": JSON.parse("{{varDL_item_price}}")});          } else if (event_name == 'conversionStep_[purchase]_add_to_cart') {       amplitude.getInstance().logEvent(event_name, {"item_id": JSON.parse("{{varDL_item_id}}"), "item_price": JSON.parse("{{varDL_item_price}}")});          } else if (event_name == 'conversionStep_[purchase]_success') {       amplitude.getInstance().logEvent(event_name, {"purchase_sum": JSON.parse("{{varDL_purchase_sum}}"), "item_amount": JSON.parse("{{varDL_item_amount}}")});          } else if (event_name == 'makeActions_[favorite]_add') {       amplitude.getInstance().logEvent(event_name, {"item_id": JSON.parse("{{varDL_item_id}}"), "item_price": JSON.parse("{{varDL_item_price}}")});    }  } catch (e) {} } function amplitudeSdkSearch() { if (window.amplitude) mainTagFunction();    else {      if (amplitudeCounter < 50) {         setTimeout(amplitudeSdkSearch, 100);         amplitudeCounter++;      }    }}   amplitudeSdkSearch();})();</script>

Нажмите Сохранить, и тег добавится в контейнер.

Код содержит функцию amplitudeSdkSearch, которая проверяет, инициализировался ли Amplitude JS SDK.

А функция mainTagFunction отправляет события со свойствами в Amplitude: он соотносит название события со свойствами, которые нужно передать вместе с этим событием.

Значения свойств передаются методом JSON.parse, это позволяет отправлять значения в виде массивов. Пример пуша с массивом в значении свойства:

<script>    dataLayer.push({        'event': 'addEvents_makeConversions',        'event_id': 'd-v1-e0',        'event_cat': 'purchase',        'event_name': 'example',        'property1': JSON.stringify(['value1', 'value2', 'value3']),        'property2': JSON.stringify('value4')    });</script>

При этом способе события передаются не через таблицу поиска, а указываются в коде тега tagWA_Amplitude_Events, поэтому необходимо перечислить все события в коде тега, используя else if. Это сделано потому, что если отправлять пуши из способа 3 с тегом из способа 2, то свойства события не будут отправляться, потому что из-за особенности работы таблицы поиска в ней не получиться использовать метод JSON.parse.

Для наглядности код в этом теге записан через else if. Но его можно записать в более лаконичной форме, используя объект JS.

6. Настройте GTM для передачи свойств пользователя

Отправка User properties

Для отправки свойств пользователя нужно добавить переменные уровня данных, таблицу поиска и тег.

dataLayer.push будет выглядеть так:

<script>    dataLayer.push({        'event': 'addUserProperties',        'user_property_id': 'd-v1-up1',        'user_property_name': 'user_property1',        'user_property1': JSON.stringify('value1')    });</script>

Обратите внимание, что значение userpropertyname должно быть таким же, как и название свойства. Метод JSON.stringify в значении свойства нужен, чтобы передавать значения массивами. Вариант пуша с массивом в значении свойства будет выглядеть так:

<script>    dataLayer.push({        'event': 'addUserProperties',        'user_property_id': 'd-v1-up1',        'user_property_name': 'user_property2',        'user_property2': JSON.stringify(['value2', 'value3', 'value4'])    });</script>

Для нашего реестра событий необходимо будет отправлять следующие пуши:

<script>    dataLayer.push({        'event': 'addUserProperties',        'user_property_id': 'd-v1-up1',        'user_property_name': 'age',        'age': JSON.stringify('{{возраст}}')    });</script>
<script>    dataLayer.push({        'event': 'addUserProperties',        'user_property_id': 'd-v1-up2',        'user_property_name': 'gender',        'gender': JSON.stringify('{{пол}}')    });</script>
<script>    dataLayer.push({        'event': 'addUserProperties',        'user_property_id': 'd-v1-up3',        'user_property_name': 'favorites_amount',        'favorites_amount': JSON.stringify('{{количество товаров в избранном}}')    });</script>
<script>    dataLayer.push({        'event': 'addUserProperties',        'user_property_id': 'd-v1-up4',        'user_property_name': 'registration_date',        'registration_date': JSON.stringify('{{дата регистрации в iso формате}}')    });</script>

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

Перейдем в GTM. Начнем с создания переменных принцип такой же, как у переменных для событий. Согласно реестру нужно добавить следующие переменные:

Имя переменной в GMT

Имя переменной в dataLayer

varDL_user_property_name

user_property_name

varDL_age

age

varDL_gender

gender

varDL_favorites_amount

favorites_amount

varDL_registration_date

registration_date

Добавьте таблицу поиска LT_user_properties_value, которая будет соотносить название свойства пользователя с его значением.

Установите входную переменную varDL_user_property_name, поставьте галочку напротив Установить значение по умолчанию и оставьте пустое поле.

Далее добавьте эти строки в таблицу:

Входные данные

Результат

age

{{varDL_age}}

gender

{{varDL_gender}}

favorites_amount

{{varDL_favorites_amount}}

registration_date

{{varDL_registration_date}}

Нажмите Сохранить, и переменная добавится.

Также нужно добавить триггер DL_event_addUserProperties, который будет активировать тег передачи свойств пользователя:

  • зайдите в раздел Триггеры и нажмите Создать,

  • в появившемся окне введите название DL_event_addUserProperties и нажмите на иконку настроек триггера,

  • в списке выберите Пользовательское событие,

  • введите в поле Имя события addUserProperties и поставьте галочку напротив Использовать регулярные выражения,

  • в условиях активации триггера оставьте Все специальные события и нажмите Сохранить.

Создайте новый тег с названием tagWA_Amplitude_UserProperties, выберите тип Пользовательский HTML, установите триггер DL_event_addUserProperties и добавьте код:

<script>(function(){  var amplitudeCounter = 0; function mainTagFunction() {    var properties_value = JSON.parse('{{LT_user_properties_value}}');    var identify = new amplitude.Identify().set('{{varDL_user_property_name}}', properties_value);    amplitude.getInstance().identify(identify);}   function amplitudeSdkSearch() { if (window.amplitude) mainTagFunction();    else {      if (amplitudeCounter < 50) {         setTimeout(amplitudeSdkSearch, 100);         amplitudeCounter++;      }    }}   amplitudeSdkSearch();})();</script>

Он также содержит функцию amplitudeSdkSearch, которая проверяет, инициализировался ли Amplitude JS SDK.

ФункцияmainTagFunction с помощью метода JSON.parse преобразует JSON обратно в объект, что позволяет передавать значение свойства пользователя в том числе в виде массива. Далее она подставляет название и значение свойства и отправляет его в Amplitude.

Нажмите Сохранить, и тег добавится в контейнер.

7. Настройте GTM для передачи User ID

Отправка User ID

User ID это уникальный идентификатор для каждого авторизованного пользователя, который не должен присваиваться анонимным пользователям. Это опциональное свойство пользователя: его не обязательно передавать, но его использование позволяет Amplitude определять переход пользователя между разными платформами (web, iOS, Android) и объединять пользователей (используя Amplitude ID и Device ID), в случае если события были совершены анонимным пользователем, а позже он авторизовался.

Подробнее о принципе объединения пользователей Amplitude.

User ID не должен изменяться: например, лучше не передавать в качестве user ID адрес электронной почты пользователя, потому что он может изменить его, и тогда в Amplitude он отобразится как новый пользователь.

Для отправки user ID в Amplitude необходимо на каждой странице сайта до основного кода контейнера передавать:

<script>    dataLayer.push({        'event': 'setUserId',        'user_id': 'user_id_value'    });</script><!-- Код контейнера GTM -->

Соответственно нужно добавить переменную GTM varDL_user_id с именем переменной уровня данных user_id :

Создайте триггер DL_event_setUserId, который будет активировать тег передачи user ID:

  • зайдите в раздел Триггеры и нажмите Создать,

  • в появившемся окне введите название DL_event_setUserId и нажмите на иконку настроек триггера,

  • в списке выберите Пользовательское событие,

  • введите в поле Имя события setUserId и поставьте галочку напротив Использовать регулярные выражения,

  • в условиях активации триггера оставьте Все специальные события и нажмите Сохранить.

Создайте новый тег с названием tagWA_Amplitude_User_Id, выберите тип Пользовательский HTML, установите триггер DL_event_setUserId и добавьте код:

<script>(function(){  var amplitudeCounter = 0;function mainTagFunction() {    amplitude.getInstance().setUserId('{{varDL_user_id}}');}   function amplitudeSdkSearch() { if (window.amplitude) mainTagFunction();    else {      if (amplitudeCounter < 50) {         setTimeout(amplitudeSdkSearch, 100);         amplitudeCounter++;      }    }}   amplitudeSdkSearch();})();</script>

Он также содержит функцию amplitudeSdkSearch, которая проверяет, инициализировался ли Amplitude JS SDK.

ФункцияmainTagFunction отправляет user ID в Amplitude.

Нажмите Сохранить, и тег добавится в контейнер.

8. Проверьте отправку событий и свойств

Чтобы проверить правильность отправки события, можно установить расширение для браузера либо посмотреть событие в интерфейсе Amplitude.

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

Рассмотрим работу расширения для браузера Instrumentation Explorer на примере отправки событий при просмотре блога Amplitude.

Когда пользователь пролистывает страницу статьи из блога, отправляется событие blog: page scrolled. Если нажать на кнопку расширения Instrumentation Explorer, то можно посмотреть название, свойства отправленного события и проверить правильность отправки.

Теперь проверим отправку событий в интерфейсе Amplitude.

В отчете User Look-Up нажмите на поиск:

Нажмите кнопку Advanced:

Установите режим поиска по User Property IP address:

Узнайте свой IP адрес (например, на сайте https://2ip.ru/), нажмите на кнопку Select value(s), введите свой IP адрес, отметьте его галочкой и нажмите Apply:

После этого в поиске отобразится пользователь это вы в Amplitude. Нажмите на него, и вы перейдете на карточку пользователя, где можно посмотреть пришедшие события.

В нижней части карточки пользователя можно посмотреть поток выполненных им событий.

Если выбрать одно из событий, вы увидите его параметры:

Здесь вы проверите, что событие отправилось с правильным названием и правильными параметрами.

Иногда при проверке собственных действий система не обнаруживает проблем с передачей событий и параметров. Поэтому после проверки собственных действий, зайдите в отчет Event Segmentation, выберите проверяемое действие и посмотрите его параметры.

Если вы выберите событие conversionStep_[login]_error и его параметр error, то увидите, что значение Password field is empty параметра error передается неправильно:

Если вы нашли ошибку, нужно ее описать, сделать скриншот и написать разработчикам.

9. Файлы контейнеров из статьи

Если вы захотите изучить контейнеры, на примере которых была написана эта статья, вы можете импортировать их в свой аккаунт GTM.

Чтобы это сделать, зайдите на вкладку Администрирование и нажмите Импортировать контейнер.

Выберите файл контейнера. Затем выберите Существующую рабочую область это пространство для работы с контейнером (если проводить аналогию с Google Analytics, можно сравнить ее с представлениями).

При создании аккаунта GTM по умолчанию добавляет рабочую область Default Workspace выберите ее для импорта файла контейнера:

Далее поставьте вариант импорта Переименовать конфликтующие теги, триггеры и переменные, чтобы в случае повтора переменных, триггеров или тегов они не удалились:

После этих действий GTM покажет, какие изменения будут внесены. Проверьте и нажмите Подтвердить:

Импорт завершен.

Файлы контейнеров:

Контейнер для первого способа (файл "Способ 1.json"):

- Тег с Amplitude JS SDK

- Тег и триггер для отправки событий

- Переменные

Контейнер для второго способа (файл "Способ 2.json"):

- Тег с Amplitude JS SDK

- Тег и триггер для отправки событий

- Тег и триггер для отправки свойств пользователя

- Тег и триггер для отправки user ID

- Переменные

Контейнер для третьего способа (файл "Способ 3.json"):

- Тег с Amplitude JS SDK

- Тег и триггер для отправки событий

- Переменные

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

Подробнее..

Получение данных Amplitude через API

28.06.2020 08:22:17 | Автор: admin

Введение


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


Пререквизит


1) Проект в Amplitude, в котором уже корректно настроены события, и по ним собирается статистика
2) Установлен python (работаю в версии 3.8.3), с которым потенциальный читатель уже умеет работать хотя бы на базовом уровне

Инструкция


Шаг 1. Получение API-key и secret-key.
Чтобы выгрузить данные, в первую очередь необходимо получить API-key и secret-key.
Найти их можно, перейдя по следующему пути:
1) Manage data (находиться в левой нижней части экрана)
2) Выбираем нужный проект, из которого будут выгружаться данные, и переходим в него
3) В открывшемся меню проекта выбираем Project settings
4) Находим строки API-key и secret-key, копируем и сохраняем их в надежном месте.
Без прокликивания можно перейти по ссылке, которая в общем виде выглядит так:
analytics.amplitude.com/$$$$$$$/manage/project/******/settings,
где $$$$$$ логин вашей организации в amplitude, ****** номер проекта

Шаг 2. Проверка наличия нужных библиотек.
Хорошая новость в том, что эти библиотеки почти наверняка уже у вас установлены по умолчанию или скачаны, но проверить необходимо. Полный список используемых мною библиотек на момент написания статьи (в скобках указаны версии, где уместно):
1) requests (2.10.0)- отправление запроса через api для получения данных
2) pandas (1.0.1) чтение json, создание dataframe и последующая запись в файл
3) zipfile извлечь файлы из архива, полученного через API
4) gzip распаковка json файлов из .gz
5) os получение списка файлов из распакованного архива
6) time необязательная, измерение времени работы скрипта
7) tqdm необязательная, для удобства наблюдения за прогрессом обработки файлов

Шаг 3. Написание скрипта загрузки данных.
Хинт: полный скрипт загрузки находится в конце статьи, при желании можно сразу взять его и обращаться к пошаговым пояснениям при необходимости.

Шаг 3.1. Импорт библиотек.
Производим импорт всех библиотек, перечисленных на втором шаге.
# Импорт библиотекimport requestsimport pandas as pdimport zipfileimport gzipimport osimport timeimport tqdmfrom tqdm import tqdm


Шаг 3.2. Отправление запроса в Amplitude.
Засечем начало выполнения скрипта и запишем в переменную a.
startdate и enddate отвечают за период для выгрузки данных и встраиваются в текст отправленного запроса на сервер Amplitude, помимо даты можно также указывать час, меняя значение после 'T' в запросе.
api_key и secret_key соответствуют значениям, полученным на первом шаге, в целях безопасности здесь указываю случайные последовательности вместо своих:)

a = time.time()# Параметры начальной и конечной датыstartdate = '20200627'enddate = '20200628'api_key = 'd988fddd7cfc0a8a'secret_key = 'da05cf1aeb3a361a61'# Отправление запроса в Amplituderesponse = requests.get('https://amplitude.com/api/2/export?start='+startdate+'T0&end='+enddate+'T0', auth = (api_key, secret_key))print('1. Запрос отправлен')


Шаг 3.3. Скачивание архива с данными.
Придумываем название для архива и записываем в переменную filename. Для своего удобства я указываю период + указываю, что это данные amplitude. Далее записываем полученный ответ от Amplitude в архив.
# Скачивание архива с даннымиfilename = 'period_since'+startdate+'to'+enddate+'_amplitude_data'with open(filename + '.zip', "wb") as code:    code.write(response.content)print('2. Архив с файлами успешно скачан')  


Шаг 3.4. Извлечение файлов в папке на компьютере.
В дело вступает библиотека zipfile, которая поможет осуществить извлечение файлов. В третьей строке будьте внимательны и пропишите свой путь, куда вам удобнее произвести извлечение.
# Извлечение файлов в папку на компьютереz = zipfile.ZipFile(filename + '.zip', 'r')z.extractall(path = 'C:\\Users\\...\\'+filename)print('3. Архив с файлами извлечен и записан в папку ' + filename)


Шаг 3.5. Преобразование json.
После извлечения файлов из архива нужно преобразовать json файлы, находящиеся в формате .gz и записать их в dataframe для дальнейшей работы.
Обращаю внимание на то, что здесь надо снова поменять путь на свой, а вместо 000000 написать свой номер проекта из Amplitude (либо открыть вручную путь, куда был извлечен архив, и посмотреть название папки внутри).
По порядку следования:
Запись директории в переменную, получение списка файлов из директории, создание пустого датафрейма, time.sleep(1) для корректной работы tqdm, внутри цикла открываем .gz файлы и сразу с помощью pandas читаем json и наполняем заданный dataframe.
# Преобразование json к обычному табличному форматуdirectory = 'C:\\Users\\...\\'+filename+'\\000000'files = os.listdir(directory)amplitude_dataframe = pd.DataFrame()print('Прогресс обработки файлов:')time.sleep(1)for i in tqdm(files):    with gzip.open(directory + '\\' + i) as f:        add = pd.read_json(f, lines = 'True')    amplitude_dataframe = pd.concat([amplitude_dataframe, add])time.sleep(1)    print('4. JSON файлы из архива успешно преобразованы и записаны в dataframe')


Шаг 3.6. Запись dataframe в excel.
Выгрузка в excel здесь просто как пример. Во многих случаях удобнее работать с полученным фреймом данных внутри python или сложить данные в хранилище.
Заменить путь выгрузки данных здесь также придется на свой.
# Записать полученной таблицы в Excel-файлamplitude_dataframe.to_excel('C:\\Users\\...\\'+filename+'.xlsx',index=False)print('5. Dataframe успешно записан в файл ' + filename)


Шаг 3.7. Считаем время работы скрипта.
Запись текущего времени в переменную b, расчет разницы и количества минут, вывод итого минут. Это последний шаг.
b = time.time()diff = b-aminutes = (diff%3600)//60print('Выполнение кода заняло: {:.0f} минут(ы)'.format( minutes))


Заключение


Вызвать таблицу и начать с ней работать можно, вызвав переменную amplitude_dataframe, в которую были записаны данные. В ней будет порядка 50 столбцов, из которых в 80% случаев вы будете использовать: event_type название ивента, event_properties параметры ивента, event_time время ивента, uuid id клиента, user_properties параметры клиента, стоит начать работу в первую очередь с них. И при сравнении цифр из собственных расчетов с показателями из дэшбордов Amplitude нельзя забывать о том, что система использует собственную методологию расчета уникальных клиентов/воронок и т.д., и перед этим надо обязательно ознакомиться с документацией Amplitude.
Спасибо за внимание! Теперь вы можете выгружать сырые данные Amplitude и полноценно использовать их в своей работе :)

Весь скрипт:
# Импорт библиотекimport requestsimport pandas as pdimport zipfileimport gzipimport osimport timeimport tqdmfrom tqdm import tqdma = time.time()# Параметры начальной и конечной датыstartdate = '20200627'enddate = '20200628'api_key = 'd988fddd7cfc0a8a'secret_key = 'da05cf1aeb3a361a61'# Отправление запроса в Amplituderesponse = requests.get('https://amplitude.com/api/2/export?start='+startdate+'T0&end='+enddate+'T0', auth = (api_key, secret_key))print('1. Запрос отправлен')# Скачивание архива с даннымиfilename = 'period_since'+startdate+'to'+enddate+'_amplitude_data'with open(filename + '.zip', "wb") as code:    code.write(response.content)print('2. Архив с файлами успешно скачан')  # Извлечение файлов в папку на компьютереz = zipfile.ZipFile(filename + '.zip', 'r')z.extractall(path = 'C:\\Users\\...\\'+filename)print('3. Архив с файлами извлечен и записан в папку ' + filename)# Преобразование json к обычному табличному форматуdirectory = 'C:\\Users\\...\\'+filename+'\\000000'files = os.listdir(directory)amplitude_dataframe = pd.DataFrame()print('Прогресс обработки файлов:')time.sleep(1)for i in tqdm(files):    with gzip.open(directory + '\\' + i) as f:        add = pd.read_json(f, lines = 'True')    amplitude_dataframe = pd.concat([amplitude_dataframe, add])time.sleep(1)    print('4. JSON файлы из архива успешно преобразованы и записаны в dataframe')# Записать полученной таблицы в Excel-файлamplitude_dataframe.to_excel('C:\\Users\\...\\'+filename+'.xlsx',index=False)print('5. Dataframe успешно записан в файл ' + filename)b = time.time()diff = b-aminutes = diff//60print('Выполнение кода заняло: {:.0f} минут(ы)'.format( minutes))
Подробнее..

Что и кому должен продуктовый аналитик? ч.1

29.11.2020 12:04:16 | Автор: admin


Последние несколько месяцев я проходила квест под названием уволиться с позиции маркетингового аналитика в FMCG и получить оффер на позицию продуктового аналитика в IT. Хочу поделиться опытом и систематизировать информацию, собранную за это время из разных источников. А для того, чтобы не быть голословной, рассказывая, о том, какими навыками должен обладать продуктовый аналитик, начну с обзора открытой информации о требованиях к соискателям в вакансиях hh.ru.

Я спарсила открытые данные о вакансиях, размещенных на сайте headhunter.ru на 28/10/2020 по запросу Аналитик и Продуктовый аналитик. Полный ноутбук и ссылки на данные выложены тут.

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

Список требований, необходимых для конкретной вакансии, я брала из раздела Ключевые навыки в описании позиции. Не все HR качественно заполняют это поле: кто-то опечатался (Прим: Phyton), кто-то вообще плохо понимает, что происходит ошибся (Прим: Массивы, Медицинское оборудование), кто-то не стал заполнять этот раздел. Однако, судя по тому, что для разных специальностей видны очевидные отличия в требованиях, большинство вакансий заполнены корректно, по крайней мере, критичные навыки упомянуты.

Возможно, для более точного учета навыков, особенно soft skills (Прим: обладаете хорошими коммуникативными навыками для общения с бизнес-, и с техническими командами) стоит выделять требования из полного описания вакансии и разбивать их на смысловые группы.

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

Всего для анализа было доступно 1178 объявлений, более 60% которых приходится на 5 специальностей: аналитик, бизнес аналитик, продуктовый аналитик, маркетолог аналитик и веб аналитик.

Как отличаются навыки, необходимые для каждой специальности?



Видно, что ключевые навыки для каждой специальности свои: для Продуктового аналитика важны технические навыки (SQL, Python), для Маркетингового аналитика чаще упоминают маркетинговый анализ и PowerPoint, а для Веб аналитика GA и Я.Метрику (вот за что я люблю аналитику. за такие инсайты!).

Если продолжить список топ навыков для Продуктового аналитика, окажется, что за техническими скиллами идут аналитические (анализ данных, аналитическое мышление, аналитические исследования) и знание статистики (математическая статистика, статистический анализ, a/b тесты, data mining). Полный список с интерпретацией частот навыков на самой первой картинке с облаком тегов.

В какую специальность легче всего зайти без релевантного опыта?



Легче всего искать работу на junior и intern позиции в области анализа данных по специальностям Маркетолог аналитик и Веб аналитик около 10% вакансий готовы нанять людей без опыта.

А на позицию Продуктового аналитика чаще всего ожидают более опытных людей: более чем в половине вакансий ищут человека с 3-6 годами релевантного опыта.

Как отличается заработная плата по специальностям?


Вакансий с открытой информацией о заработной плате мало всего 63. Тем не менее, невозможно удержаться и не посмотреть распределение. Для адекватного сравнения, рассмотрим медианы зарплат начального уровня (ожидаемый опыт работы от 1го до 3х лет).

Бизнес аналитик в среднем может рассчитывать на 140т, продуктовый аналитик на 100т, а меньше всего готовы платить маркетологу и веб аналитику: 60т. Маркетологи и веб аналитики, срочно учите BPMN или Python, SQL!

Напоследок дам ссылки на занимательные материалы на схожую тему:

  1. Аналитика для хантинга аналитиков обзор вакансий, навыков и зарплат от людей, которые в hr штучках понимают гораздо больше, чем я.
  2. Текст о необходимых навыках на разных грейдах в Яндексе.
  3. Статья о видах аналитиков в IT (читать голосом Дроздова).

Во второй части статьи я поделюсь полезными материалы, лайвхаками, курсами и задачниками, которыми пользовалась, когда наверстывала недостающие hard skills для позиции продуктового аналитика. Stay tuned!
Подробнее..

Продуктовая аналитика на практике при помощи R

26.02.2021 14:22:55 | Автор: admin

Проблематика

Современная мета управления продуктом подразумевает управление на основании данных.

Все хотят аналитического подхода и способности принимать решения на куче данных о продукте которые есть. Но в реальности есть недостаток информации о том, как именно это делать. Какие инструменты использовать, как принимать решения и как исследовать данные. Я бы хотел поделится именно практическим аспектом этого вопроса в формате одного кейса.

Вводная часть

Начало 2020 года, вы - обычный продукт-менеджер, которому предложили развивать кредитный продукт в другой стране. Оффер принят, документы оформлены, пора за работу.

Первое, что приходит в голову посмотреть, что там с экономикой продукта. И как вообще ведет себя продукт.

Еще через пару дней совещание, на котором вас просят ответить на несколько вопросов:

  1. Сейчас мы платим за привлечение клиента 695494. Какая стоимость привлечения для нас является приемлемой? Имеет ли смысл повысить стоимость привлечения на клиента, чтобы получить больший объем?

  2. Насколько здоровой выглядит экономика портфеля и какая динамика здесь и сейчас?

  3. Мы недавно изменили подход к размеру выдачи и стали в первые кредиты выдавать меньшие чеки. Как это сказалось на продукте?

В общем-то понятные вопросы, на которые стоит уметь отвечать любому владельцу продукта.

Но есть проблема. Метрики, конечно, все посчитаны: LTV, CAC и прочее. Есть финансовая отчетность, где видны все расходы и доходы, и видно, что продукт в небольшом операционном плюсе.

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

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

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

Анализируй это

Пример взят из реальной практики. То есть вот прям все взаправду, данные подшумлены, но это реальный портфель. И анализируются данные примерно таким образом.

Для того, чтобы анализировать, понадобятся следующие инструменты: R ,Rstudio,dbeaver(или аналог). Далее рассматривается реальный пример, как могут выглядеть эти данные и что с ними можно сделать.

Итак, транзакции. Напишем в базу нечто вроде select * from transactions t и посмотрим результат.

Rows: 2,226,532Columns: 10$ borrower_id          2, 2, 2, 6, 6, 12, 12, 12, 12, 16, 20, 20, 20, 20, 22, 23, 23, 33, 33, 39, 39, 36, 36$ con_id               1, 1, 1, 2, 2, 4, 4, 4, 4, 5, 7, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13$ disbursement_date    2017-11-23, 2017-11-23, 2017-11-23, 2017-11-24, 2017-11-24, 2017-11-27, 2017-11-27, $ prolongations_count  1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1$ loan_type            "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "$ date                 2017-12-15, 2017-11-23, 2017-12-06, 2017-11-24, 2017-12-25, 2017-12-29, 2017-11-27, $ type                 "Payments::Transaction::ContractAddTransaction", "Payments::Transaction::DisburseTran$ amount               250000, 1000000, 1200000, 2500000, 3500000, 1040000, 1500000, 10000, 1470000, 1500000$ id                   325, 2, 127, 5, 587, 557500, 557499, 557504, 557507, 17, 182865, 182874, 182869, 1828$ deleted_at           NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N

Видим всю историю транзакций по всем клиентами. Проанализируем состав полей(обычно можно посмотреть в документации, но кое-что понять можно и так).

У нас есть идентификаторы заемщика, контракта, и транзакции. Дата выдачи кредита (disbursment_date). Отметка о пролонгации кредита (prolongations_count) - перенос даты выплаты за отдельный платеж. Размер и дата транзакции. Отметка о "мягком" удалении.

Главное - тип транзакции(ContractAdd- возврат денег заемщиком, DisburseTransaction - выплата денег заемщику).

Теперь поговорим об экономике продукта. Принципиально кредитный бизнес работает просто(как и большинство бизнесов): заплатили, чтобы привести пользователя -> выдали кредит -> собрали деньги -> выдали еще кредит.

Выручку по продукту формируют два финансовых потока: от нас к пользователю и от пользователя к нам.

И в нашей табличке есть все данные, чтобы это посчитать.

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

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

  • для любителей R, данных в табличке прилично, больше 2 млн. Для таких датасетов предпочитаю data table. Но можно и на tidyverse(по запросу покажу как)

Теперь посмотрим на структуру данных и займемся обработкой.

Чуть обработаем их и приведем данные в нужный нам формат. Назовем транзакции удобно и сделаем отрицательными транзакции выдачи кредитов (поля z_type и am):

lk %>% data.table()->lk1 #Создаем отдельный объект вида data tablelk1[,':='(z_type=z_type<-fifelse(#  создаем отдельную переменную - которая отвечает за тип транзакций type=='Payments::Transaction::ContractAddTransaction','add','disb'),am=amount*fifelse(z_type=='add',1,-1))][1:20,c(-1,-11,-8,-6)] #убираем ненужные колонкиlk1[disbursement_date<'2020-01-01' & date<='2020-03-01',c(-1,-11,-8,-6)]->lk1glimpse(lk1) #посмотрим на получившийся фаил
$ borrower_id          2, 2, 2, 6, 6, 12, 12, 12, 12, 16, 20, 20, 20, 20, 22, 23, 23, 33, 33, 39, 39, 36, 36$ con_id               1, 1, 1, 2, 2, 4, 4, 4, 4, 5, 7, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13$ disbursement_date    2017-11-23, 2017-11-23, 2017-11-23, 2017-11-24, 2017-11-24, 2017-11-27, 2017-11-27, $ prolongations_count  1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1$ date                 2017-12-15, 2017-11-23, 2017-12-06, 2017-11-24, 2017-12-25, 2017-12-29, 2017-11-27, $ amount               250000, 1000000, 1200000, 2500000, 3500000, 1040000, 1500000, 10000, 1470000, 1500000$ id                   325, 2, 127, 5, 587, 557500, 557499, 557504, 557507, 17, 182865, 182874, 182869, 1828$ z_type               "add", "disb", "add", "disb", "add", "add", "disb", "add", "add", "disb", "disb", "ad$ am                   250000, -1000000, 1200000, -2500000, 3500000, 1040000, -1500000, 10000, 1470000, -150

Давайте посмотрим на главное и посчитаем, прибыльный ли портфель.

Для этого посмотрим транзакции за все время. И так как цифры крупные, посмотрим сразу в миллионах:

lk1[,.(total_in_mln=sum(am*for_ex)/1e6),.(z_type)]

total_in_mln

add

58.33176

disb

-45.49114

Видим ясный и понятный результат: транзакции от заемщиков (add) значительно превышают транзакции от выдачи кредита (disb). А ведь часть кредитов наверняка только выдана и по ним даже не подошла дата выплаты.

И здесь же разберемся с темпом, с которым выдавали кредиты:

lk1[!is.na(disbursement_date)&z_type=='disb',.(sum=sum(am*for_ex*-1,na.rm = T)/1e6),.(date=floor_date(disbursement_date,'month',))][,ggplot(.SD,aes(date,sum,label=round(sum,2)))+geom_col(fill=polar_night[2])+ff+tt+  geom_text(aes(y=sum+0.1),col=aurora[1])+  labs(x='месяц выдачи',y='выдача в миллионах долларов',  title='Выдача кредитов в месяц')]

Аналитически видно, что масштабирование продукта в последние несколько месяцев остановилось. Выдаем столько же сколько и выдавали.

Вернемся к Юнит экономике

Глобально мы занимаемся масштабированием пользователей, ведь кредит без человека выдать сложно.

А юнит экономика в таком контексте- это сколько мы зарабатываем или теряем на одном пользователе, заплатив за его привлечение.

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

Удобнее работать с относительным временем, так сможем сравнивать пользователей, пришедших в разное время:

lk1[][,':='( min_date=min(disbursement_date)),.(borrower_id)][,#найдем дату начала работы каждого заемщикаc("gen",'dif'):=.(floor_date(min_date,'1 month'),as.numeric(date-min_date))][, #месяц в котором появился заемщик и растояние в днях между датой его рождения и датой транзакцииn:=uniqueN(borrower_id),][, #количество уникальных клиентов  .(sum=sum(am*for_ex),n=unique(n)),  .(dif)][order(dif)][,":="(bal=bal<-sum/n,cum=cumsum(bal))][,    ggplot(.SD,aes(dif,cum))+ #строим график   geom_line()+   tt+ff+        labs(x='дни жизни заемщика',y='доход в USD',col='поколение',        title='LTV клиента по поколениям и по годам')+   scale_y_continuous(breaks = seq(-200,200,20),   labels =paste0('$',seq(-200,200,20),'k' ))+   scale_x_continuous(breaks = seq(0,1000,20))  ]

Получившаяся кривая показывает его LTV c динамикой по времени. И это уже близко к ответу на первый вопрос о стоимости привлечения пользователя.

Но перед тем как пустится в эти рассуждения, стоит вспомнить, кто такой, этот средний пользователь. Мало ли, может, лучшие времена продукта давно позади.

Поэтому стоит посмотреть этот же график, но разбив по поколениям и посмотрев последние 24 поколения. Сразу же наложим сюда стоимость привлечения и разобьем по годам(чисто для удобства сравнения):

lk1[,':='( min_date=min(disbursement_date)),.(borrower_id)][,c("gen",'dif'):=.(floor_date(min_date,'1 month'),as.numeric(date-min_date))][,n:=uniqueN(borrower_id),.(gen)][,  .(sum=sum(am*0.00003),n=unique(n)),  .(gen,dif)# сгруппируем данные  по поколениями][order(gen,dif)][,":="(bal=bal<-sum/n)][,':='(cum=cumsum(bal),m_dif=max(dif)),.(gen)][dif<=m_dif-30 & gen %between% c('2018-01-01','2021-03-31')][, ggplot(.SD,aes(dif,cum,col=factor(gen)))+ geom_line()+ facet_wrap(~factor(year(gen),levels = c(2019,2018)),nrow=2)+tt+ff+ labs(x='дни жизни заемщика',y='доход в USD',col='поколение', title = 'LTV клиента по поколениям и по годам')+ scale_y_continuous(breaks = seq(-200,200,20), labels =paste0('$',seq(-200,200,20),'k' ))+ scale_x_continuous(breaks = seq(0,1000,20))+ geom_hline(yintercept = 695494*for_ex,color='red',size=1)+ geom_hline(yintercept = 0,color='dark red',linetype='dashed')+ geom_text(inherit.aes = F,aes(x=as.Date(600), y=695494*for_ex+3,group=1),label='Стоимость привлечения клиента = $20.86', col='red',size=6)+tt+ff+ theme(legend.text = element_text(size=20),       legend.title = element_text(size=25))+  guides(colour = guide_legend(override.aes = list(size=10)))]

Если каждая траектория - это поколение пользователей (месяц в котором выдан первый кредит), то мы можем сделать несколько важных выводов исходя из следующего:

  1. Точка, из которой начинаются линии - размер первого кредита.

  2. Чем выше траектория, тем больше денег на одного пользователя мы заработали в этом поколении.

  3. Чем раньше траектория поколения пересекает 0, тем быстрее мы начинаем зарабатывать на привлеченном пользователе.

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

Выводы

  1. Продукт становится лучше. Траектории более поздних поколений пересекают ноль и стоимость привлечения раньше, чем траектории старых поколений (150 дней против 180).

  2. Разница в темпах окупаемости объясняется скорее стартовой точкой и первой парой кредитов. Например, поколения конца 2018 года окупались лучше, имея меньшую сумму первого кредита и большую сумму второго. Это можно понять по провалу на траектории на 10-40 днях. Так же в 2019 году: поколения с меньшей средней суммой первого кредита окупали себя намного быстрее.

  3. На дистанции в один год мы заработаем на одном пользователе от 40 до 60 долларов. Максимальный размер заработка неизвестен. Ни одно поколение пока не вышло на плато, но экстраполируя, оценка в 120-150 долларов на дистанции в 3 года выглядит разумной

  4. Наиболее существенное влияние на траекторию оказывают события первых 90 дней, после этого темп роста плюс минус одинаковый.

Мы тут ради ответов, а не картинки смотреть

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

1. Сейчас мы платим 695494. Какая стоимость привлечения для нас является приемлемой? Имеет ли смысл повысить стоимость привлечения на клиента, чтобы получить больший объем?

Ситуация выглядит следующим образом - у нас есть успешный продукт, который мы не масштабируем - и это проблема. Ожидания по доходности от клиента на дистанции год - 40-60 долларов. Текущие затраты на привлечение $20.8 (695494* 0.00003)

В принципе - увеличение стоимости привлечения наша экономика выдержит, портфель не станет убыточным. Но в продукте - длинный срок окупаемости клиента.

Если мы можем позволить себе тратить сейчас - чтобы заработать потом - эксперименты оправданы.

2. Насколько здоровой выглядит экономика портфеля и какая динамика здесь и сейчас?

Портфель устойчиво положительный, с динамикой к росту за счет практик развития клиентов в первые дни жизни. Главное пространство для идей - механика работы с клиентами возраста 100+ дней.

Там существенных прорывов пока не было.

3. Мы недавно изменили подход к размеру выдачи и стали в первые кредиты выдавать меньшие чеки. Как это сказалось на продукте?

Вообще - видно что поколения в которых первый кредит был меньшим по размерам окупали себя чуть лучше. Четкий ответ - только через АБ тест.

Итог

Работа продукт менеджера это принимать решения. 2.2 млн строк рассказали о том, что происходит. Такой анализ занимает от 30 минут до пары часов, в зависимости от знания предметной области и грязи в данных. Для такого анализа не нужно ничего, кроме сырых данных и открытого ПО.

Несколько десятков строк кода, чуть здравых размышлений и экономика продукта ясна, посчитана и выводы сделаны.

Из этих же данных несложно собирается еще несколько важных оценок и выводов , но о них в дуговой раз.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru