Datalytics

Блог Алексея Макарова о веб-аналитике, анализе данных и не только.
Группа в Фейсбук
Канал в Телеграм

Большая подборка полезных ссылок про Pandas

Я достаточно долго веду группу в Facebook и канал в Telegram, посвященные анализу данных на Python и за это время накопилось множество полезных ссылок на материалы о библиотеке Pandas. Решил, что будет здорово, если все они будут в одном месте, а не разбросаны по каналу/группе. Поэтому завёл табличку в Notion, где все ссылки протегированы и разбиты по языку (ru, eng). Также в документе прикреплена ссылка, с помощью которой можно порекомендовать материал, будь то ваша авторская статья или просто понравившийся материал с просторов интернета. Материал по pandas появится в подборке и с высокой вероятностью может быть опубликован в канале и группе.

Встречайте: подборка полезных материалов про библиотеку Pandas

Лайк, шер, пускай больше людей приобщится к использованию Pandas!

14 февраля   pandas   python   полезности

Как в Pandas разбить одну колонку на несколько

Введение в задачу

Решил начать рассматривать нетривиальные кейсы в Pandas, с которыми иногда сталкиваюсь при работе с данными. Опять же, нетривиальные они только на мой взгляд, потому что какие-то вещи я решаю впервые и они заставляют немного подумать :) Возможно, такие небольшие кейсы помогут аналитикам, если они увидят в своих «затыках» что-то похожее. Также, я не претендую на абсолютную правильность или универсальность решения. Таким образом, у подобной задачи может быть несколько правильных решений. Я буду рад, если в комментариях вы будете предлагать свои решения. Касательно универсальности решения, тут я имею в виду, что решение может быть применимо к конкретному датасету, но при этом может не работать, если датасет будет иметь какие-то существенные видоизменения.

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

Итак, у меня есть вот такой dataframe:

Скачать данные для датафрейма в csv можно тут.

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

Как видно, значения разделены знаком переноса строки (\n), а также каждое значение представлено в виде ключ=значение (например, ключом выступает date_start, а значением 2018-12-04).

Задача состоит в том, чтобы привести датафрейм к вот такому виду:

Решение №1

Итак, первое решение будет простым и достаточно коротким.

Сначала, нам понадобится пандосовская функция str.split (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.split.html). Она просто разбивает столбец на список на основании разделителя

df['new_values'].str.split('\n')

Вот как выглядит отдельное значение:

df['new_values'].str.split('\n')[0]

Но у функции str.split есть замечательный параметр expand=True, позволяющий сразу сделать разбиение на колонки и получить датафрейм

new_df = df['new_values'].str.split('\n',expand=True)
new_df

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

new_df.columns=['date_start','component','customer']
new_df

Затем нам нужно избавиться в колонках от названия ключа и знака «равно». Сделаем это простым циклом с функцией str.replace (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.str.replace.html):

for column in new_df.columns:
    new_df[column] = new_df[column].str.replace(column+'=','')
new_df

Осталось только соединить два датафрейма (исходный df и new_df) с помощью функции pd.concat (https://pandas.pydata.org/pandas-docs/version/0.23.4/generated/pandas.concat.html). Обратите внимание на параметр axis=1, который позволяет соединить датафреймы по столбцам, а не по строкам

final_df = pd.concat([df,new_df],axis=1)
final_df

Ну и выкинем из получившегося датафрейма ненужный нам теперь столбец new_values с помощью drop (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop.html):

final_df = final_df.drop('new_values',axis=1)
final_df

Итоговый код для решения задачи выглядит так:

df = pd.read_csv('sample_data_1.csv')
new_df = df['new_values'].str.split('\n',expand=True)
new_df.columns=['date_start','component','customer']
for column in new_df.columns:
    new_df[column] = new_df[column].str.replace(column+'=','')
final_df = pd.concat([df,new_df],axis=1).drop('new_values',axis=1)

Решение №2

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

Представим, что исходный датафрейм отличается от того, что я показывал выше.

Скачать данные для датафрейма в csv можно тут.

Этот датафрейм отличается тем, что в некоторых строках колонки new_values отсутствует параметр date_start. Из-за этого при попытке сделать str.split с параметром expand=True мы получим вот такой датафрейм:

df['new_values'].str.split('\n',expand=True)

В этом случае, мы не сможем просто пройтись циклом по колонкам и получить нужные значения, так как значения по ключу не всегда однозначно находятся в одной колонке, а могут быть разбросаны по нескольким. Такая же ситуация могла бы быть, если бы перечисление значений шло не в одном порядке, а по-разному. Например в одной строке date_start=2018-12-28\ncomponent=abc\ncustomer=59352, а в другой component=abc\ncustomer=22080\ndate_start=2018-12-18

Чтобы обойти эту проблему нам нужно преобразовать эти данные в такую структуру, которую было бы удобно запихнуть в датафрейм и pandas сам бы смог разделить данные по нужным колонкам, опираясь на структуру данных. Одной из таких структур может быть список словарей (list of dicts). В виде списка словарей данные должны выглядеть вот так:

Когда мы получим такую структуру, то потом сможем преобразовать её в датафрейм, а полученный датафрейм соединить с исходным (как мы уже это делали выше с помощью функции pd.concat).

Итак, первое что мы сделаем, это разобъем колонку с помощью уже знакомой функции str.split, но без параметра expand=True. Это позволит нам сделать отдельный series, содержащий списки:

s = df['new_values'].str.split('\n')
s

Затем каждый из списков нам нужно преобразовать к словарю. То есть совершить вот такое преобразование:

Чтобы лучше понять суть преобразования покажу на примере одного списка s[0], а затем сделаем функцию, которую применим к каждому элементу в series.

Сначала сделаем внутри списка s[0] вложенные списки, сделав split каждого из элемента списка по знаку «равно» через list comprehension (подробнее про list comprehension можно прочитать в статье, скажу только, что это очень удобно):

splited_items = [a.split('=') for a in s[0]]
splited_items

Затем полученный список splited_items надо переделать в словарь. Для этого сделаем несложное преобразование — создадим словарь, после чего пройдемся циклом по каждому вложенному списку и назначим нулевой элемент вложенного списка ключом словаря, а первый элемент — значением по ключу:

dictionary = {}
for item in splited_items:
    key = item[0]
    value = item[1]
    dictionary[key] = value
dictionary

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

def convertToDict(x):
    splited_items = [a.split('=') for a in x]
    dictionary = {}
    for item in splited_items:
        key = item[0]
        value = item[1]
        dictionary[key] = value
    return dictionary

После этого можно применить функцию к серии s с помощью apply (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.apply.html):

s = s.apply(lambda x: convertToDict(x))
s

Затем применим функцию pd.Series к каждому из словарей, таким образом преобразовав каждый словарь в series, а вместе последовательность series образует датафрейм:

new_df = s.apply(pd.Series)
new_df

Следующим шагом соединим датафрейм new_df с исходным df и уберем столбец с помощью функции drop:

final_df = pd.concat([df,new_df],axis=1).drop('new_values',axis=1)
final_df

Итоговый код для обработки данных выглядит вот так:

def convertToDict(x):
    splited_items = [a.split('=') for a in x]
    dictionary = {}
    for item in splited_items:
        key = item[0]
        value = item[1]
        dictionary[key] = value
    return dictionary

df = pd.read_csv('sample_data_2.csv')
s = df['new_values'].str.split('\n')
s = s.apply(lambda x: convertToDict(x))
new_df = s.apply(pd.Series)
final_df = pd.concat([df,new_df],axis=1).drop('new_values',axis=1)

И в качестве бонуса, можно сделать код более удобочитаемым с помощью method chaining:

df = pd.read_csv('sample_data_2.csv')
new_df = df['new_values'].str.split('\n').\
    apply(lambda x: convertToDict(x)).\
    apply(pd.Series)
final_df = pd.concat([df,new_df],axis=1).drop('new_values',axis=1)

Решение Романа Шапкова

UPD от 30.01.2019:
Читатель Роман решил эту задачу с помощью регулярных выражений

import pandas as pd
import re

df = pd.read_csv('sample_data_2.csv')

pattern_date = '(\d{4}-\d{2}-\d{2})'
pattern_ncomp = 'component=(\w*)'
pattern_ncust = 'customer=(\d*)'

def find_pattern(string, pattern):

    """
    аргументы: string - текстовая строка для поиска
    pattern - шаблон регулярного выражения

    функция осуществляет поиск шаблона "pattern" в строке "string" используя правила регулярных выражений(RegExp).
    Если шаблон найден - возвращает значение, иначе - возвращает None
    """
    if re.search(pattern, string):
        return re.search(pattern, string).group(1)

df['start_date'] = df['new_values'].apply(lambda x: find_pattern(x,pattern_date))
df['component'] = df['new_values'].apply(lambda x: find_pattern(x,pattern_ncomp))
df['customer'] = df['new_values'].apply(lambda x: find_pattern(x,pattern_ncust))

df

Заключение

На этом всё. Надеюсь, этот пример решения задачи кто-то найдет интересным и научится из него каким-то новым приёмам в своей работе. Это причина, по который я решил выкладывать такие примеры — не просто рассказывать про то, как работают функции в pandas или предлагать готовые решения, а показать последовательность действий и методологию решения задачи, чтобы читатели могли перенести части этого решения в свои проекты.

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

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

29 января   pandas   python   задачки

Как использовать Google BigQuery с помощью Python

Что такое Google BigQuery

Google BigQuery — это безсерверное масштабируемое хранилище данных. Использование безсерверного (облачного) решения — хорошая идея, если у вас нет серьезного бэкграунда в администрировании баз данных. Такой подход позволяет сосредоточиться только на анализе данных, и не думать об инфраструктуре хранения данных (шардировании, индексации, компрессии). BigQuery поддерживает стандартный диалект SQL, так что любой, кто когда-либо пользовался SQLными СУБД, с легкостью может начать им пользоваться.

Начало работы с Google BigQuery и создание ключа для сервисного аккаунта

Я не буду подробно объяснять о том как начать работу с Google Cloud Platform и завести первый проект, об этом хорошо написано в статье Алексея Селезнева в блоге Netpeak. Когда у нас уже есть проект в Cloud Platform с подключенным API BigQuery, следующим шагом нужно добавить учетные данные.

  1. Переходим в раздел «API и сервисы > Учетные данные»:
  1. Нажимаем «Создать учетные данные > Ключ сервисного аккаунта»
  1. Заполняем параметры: пишем название сервисного аккаунта; выбираем роль (как показано на скриншоте ниже, но роль может зависеть от уровня доступов, которые вы хотите предоставить сервисному аккаунту); выбираем тип ключа JSON; нажимаем «Создать»
  1. Переходим в раздел «IAM и администрирование > Сервисные аккаунты»
  1. В колонке «Действия» для созданного нами сервисного аккаунта выбираем «Создать ключ»
  1. Выбираем формат ключа «JSON» и нажимаем создать, после чего будет скачан JSON-файл, содержащий авторизационные данные для аккаунта

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

Использование pandas-gbq для импорта данных из Google BiqQuery

Первый способ, с помощью которого можно загружать данные из BigQuery в Pandas-датафрейм — библиотека pandas-gbq. Эта библиотека представляет собой обертку над API Google BigQuery, упрощающая работу с данными BigQuery через датафреймы.
Сначала нужно поставить библиотку pandas-gbq. Это можно сделать через pip или conda:

pip install pandas-gbq
conda install pandas-gbq -c conda-forge

Я решил рассмотреть основы работы с Google BigQuery с помощью Python на примере публичных датасетов. В качестве интересного примера возьмем датасет с данными о вопросах на сервисе Stackoverflow.

import pandas as pd
from google.oauth2 import service_account

# Прописываем адрес к файлу с данными по сервисному аккаунту и получаем credentials для доступа к данным
credentials = service_account.Credentials.from_service_account_file(
    'my-bq-project-225910-6e534ba48078.json')

# Формируем запрос и получаем количество вопросов с тегом "pandas", сгруппированные по дате создания
query = '''
SELECT DATE(creation_date) as date, COUNT(id) as questions
FROM
  [bigquery-public-data:stackoverflow.posts_questions]
WHERE tags LIKE '%pandas%'
GROUP BY
  date
'''

# Указываем идентификатор проекта
project_id = 'my-bq-project-225910'

# Выполняем запрос, записывая результат в dataframe
df = pd.read_gbq(query, project_id=project_id, credentials=credentials)

display(df.head(5))

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

df['month'] =  df['date'].values.astype('datetime64[M]') # Создаем новый столбец с месяцем
df['year'] =  df['date'].values.astype('datetime64[Y]') # Создаем новый столбец с годом

# Отображаем один день с максимальным количеством вопросов
display(df.sort_values('questions',ascending=False).head(1))

Cгруппируем данные по годам и месяцам и запишем полученные данные в датафрейм stats

stats = df.groupby(['year','month'],as_index=False).agg({'questions':'sum'}) # Группируем данные по году и месяцу, используя в качестве агрегирующей функции сумму количества вопросов

display(stats.sort_values('questions',ascending=False).head(5))

Посчитаем суммарное количество вопросов в год, а также среднее количество запросов в месяц для каждого года, начиная с января 2013 и по август 2018 (последний полный месяц, который был в датасете на момент написания статьи). Запишем полученные данные в новый датафрейм year_stats

year_stats = stats[(stats.month >= '2013-01-01') & (stats.month < '2018-09-01')].groupby(['year'],as_index=False).agg({'questions':['mean','sum']})

display(year_stats)

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

year_stats['estimate'] = year_stats[('questions','mean')]*12

display(year_stats)

На основе данных от StackOverflow, можно сказать, что популярность pandas из года в год растёт хорошими темпами :)

Запись данных из dataframe в Google BigQuery

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

В датафрейме year_stats получился multiindex из-за того, что мы применили две агрегирующие функции (mean и sum). Чтобы нормально записать такой датафрейм в BQ надо убрать multiindex. Для этого просто присвоим dataframe новые колонки.

year_stats.columns = ['year','mean_questions','sum_questions','estimate']

После этого применим к датафрейму year_stats функцию to_gbq. Параметр if_exists = ’fail’ означает, что при существовании таблицы с таким именем передача не выполнится. Также в значении этого параметра можно указать append и тогда к существующим данным в таблице будут добавлены новые. В параметре private_key указываем путь к ключу сервисного аккаунта.

year_stats.to_gbq('my_dataset.my_table', project_id=project_id, if_exists='fail', private_key='my-bq-project-225910-6e534ba48078.json')

После выполнения функции в BigQuery появятся наши данные:

Итак, мы рассмотрели импорт и экспорт данных в BiqQuery из Pandas’овского датафрейма с помощью pandas-gbq. Но pandas-gbq разрабатывается сообществом энтузиастов, в то время как существует официальная библиотека для работы с Google BigQuery с помощью Python. Основные сравнения pandas-gbq и официальной библиотеки можно посмотреть тут.

Использование официальной библиотеки для импорта данных из Google BiqQuery

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

from google.cloud import bigquery

client = bigquery.Client.from_service_account_json(
    '/home/makarov/notebooks/my-bq-project-225910-6e534ba48078.json')

sql = '''
    SELECT DATE(creation_date) as date, DATE_TRUNC(DATE(creation_date), MONTH) as month, DATE_TRUNC(DATE(creation_date), YEAR) as year, COUNT(id) as questions
FROM
  `bigquery-public-data.stackoverflow.posts_questions`
WHERE tags LIKE '%pandas%'
GROUP BY
  date, month, year
    '''

project_id = 'my-bq-project-225910'

df2 = client.query(sql, project=project_id).to_dataframe()

display(df2.head(5))

Как видно, по простоте синтаксиса, официальная библиотека мало чем отличается от использования pandas-gbq. При этом я заметил, что некоторые функции, (например, date_trunc), не работают через pandas-gbq. Так что я предпочитаю использовать официальное Python SDK для Google BigQuery.

Чтобы импортировать данные из датафрейма в BigQuery нужно установить pyarrow. Эта библиотека обеспечит унификацию данных в памяти, чтобы dataframe соответствовал структуре данных, нужных для загрузки в BigQuery.

# Создаем тестовый dataframe
df = pd.DataFrame(
    {
        'my_string': ['a', 'b', 'c'],
        'my_int64': [1, 2, 3],
        'my_float64': [4.0, 5.0, 6.0],
    }
)
dataset_ref = client.dataset('my_dataset_2') # Определяем датасет
dataset = bigquery.Dataset(dataset_ref)
dataset = client.create_dataset(dataset) # Создаем датасет

table_ref = dataset_ref.table('new_table') # Определяем таблицу (при этом не создавая её)

result = client.load_table_from_dataframe(df, table_ref).result() # Тут данные из датафрейма передаются в таблицу BQ, при этом таблица создается автоматически из определенной в предыдущей строке

Проверим, что наш датафрейм загрузился в BigQuery:

Прелесть использования нативного SDK вместо pandas_gbq в том, что можно управлять сущностями в BigQuery, например, создавать датасеты, редактировать таблицы (схемы, описания), создавать новые view и т. д. В общем, если pandas_gbq — это скорее про чтение и запись dataframe, то нативное SDK позволяет управлять всей внутренней кухней

Ниже привожу простой пример как можно изменить описание таблицы

table = client.get_table(table_ref) # Получаем данные о таблице

table.description = 'Моя таблица' # Задаем новый дескрипшн для таблицы

table = client.update_table(table, ['description'])  # Обновляем таблицу, передав новый дескрипшн через API

print(table.description)

Также с помощью нативного Python-SDK можно вывести все поля из схеме таблицы, отобразить количество строк в таблице

for schema_field in table.schema: # Для каждого поля в схеме
    print (schema_field) # Печатаем поле схемы

print(table.num_rows) # Отображаем количество строк в таблице

Если таблица уже создана, то в результате новой передачи датафрейма в существующую таблицу будут добавлены строки

result = client.load_table_from_dataframe(df, table_ref).result() # Передаем данные из dataframe в BQ-table
table = client.get_table(table_ref) # Заново получаем данные о таблице
print(table.num_rows) # Отображаем новое количество строк

Заключение

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

Успехов!

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

18 декабря   api   bigquery   pandas   python   анализ данных

С чего аналитику начать изучение Python

Оригинал поста подготовлен для Телеграм-канала «Интернет-аналитика»

В первую очередь, тем кто не знаком с Python я бы рекомендовал установить дистрибутив Anaconda. Это удобнее, чем устанавливать чистый Python, т. к. Anaconda содержит большинство пакетов, необходимых для анализа данных.

Следующий шаг — выбор среды разработки. Для анализа данных лучше всего подойдет Jupyter Notebook. Эта среда разработки устанавливается вместе с Anaconda. Вот простой туториал по работе с Jupyter Notebook.

Многие аналитики начинают учить Python, но быстро бросают. Чаще всего это происходит потому, что люди начинают изучение с синтаксиса и простых абстрактных примеров. По началу это может быть интересным, но потом надоедает. Лучше всего начинать с решения легких практических задач, автоматизируя рутину и сразу же ощущая как Python улучшает вашу жизнь?

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

На мой взгляд, самая главная библиотека для аналитика — Pandas. Если вы хотите быстро очищать, трансформировать, агрегировать, объединять и вообще всячески манипулировать табличными данными, то Pandas будет в этом надежным помощником. Про Pandas есть хорошая статья в блоге khashtamov.com (и весь блог годный!). Также советую почитать более хардкорную статью ребят из ODS.

Начните использовать Python с решения какой-то простой практической задачи, например, выгрузки данных через API Яндекс.Метрики и сохранения полученных данных в Excel. Узнать как начать работать с API Яндекс.Метрики можно из моей статьи.

Данные из Яндекс.Метрики в Python можно получить с помощью вот такого простого сниппета. Начните с получения токена для API Яндекс.Метрики и выполните этот код в Jupyter Notebook. Вы удивитесь как это просто!

Дальше можно усложнять скрипт, например, сделать несколько различных запросов и выгрузить данные на несколько вкладок в одном Excel-файле. Или выгрузить из Метрики данные с множеством dimensions и попробовать на их основе сделать в Pandas несколько таблиц с группировкой с помощью функции groupby, а также сводные таблицы с помощью функции pivot_table.

Успехов в автоматизированной борьбе с рутиной!

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

Группа в Facebook про анализ данных с помощью Python

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

https://www.facebook.com/groups/pydata/

Делаем сессии из лога событий с помощью Pandas

Предыстория

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

Структура данных лога представляет собой:

  • id — порядковый номер события в логе
  • user_id — уникальный идентификатор пользователя, совершившего событие (при решении реальной задачи анализа лога в качестве user_id может выступать IP-адрес пользователя или, например, уникальный идентификатор cookie-файла)
  • date_time — время совершения события
  • page — страница, на которую перешел пользователь (для решения задачи эта колонка не несет никакой пользы, я привожу её для наглядности)

Задача состоит в том, чтобы разбить последовательность событий (просмотров страниц) на вот такие блоки, которые будут сессиями:

Говоря «разбить», я не имею в виду разделить и сохранить в виде разных массивов данных или ещё что-то подобное. Тут речь идёт о том, чтобы каждому событию сопоставить номер сессии, в которую это событие входит.

Критерий сессии в моем случае — она живет полчаса после предыдущего совершенного события. Например, в строке 6 пользователь перешел на страницу /catalog в 8:21, а следующую страницу /index (строка 7) посмотрел в 9:22. Разница между просмотром страниц составляет 1 час 1 минуту, а значит эти просмотры относятся к разным сессиям этого пользователя.

Все это дело я буду делать на Питоне при помощи Pandas в Jupyter Notebook. Вот ссылка на ноутбук.

Алгоритм

Итак, у нас есть ’event_df’ — это датафрейм, в котором содержатся данные о событиях в привязке к пользователям:

1 События сгенерированные разными пользователями идут в хронологическом порядке. Для удобства отсортируем их по user_id, тогда события каждого пользователя будут идти последовательно:

event_df = event_df.sort_values('user_id')

2 В колонке ’diff’ для каждого события отдельного пользователя посчитаем разницу между временем посещения страницы и временем посещения предыдущей страницы. Если страница была первой для пользователя, то значение в колонке ’diff’ будет NaT, т. к. нет предыдущего значения

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

event_df['diff'] = event_df.groupby('user_id')['date_time'].diff(1)

Кое-что уже проклевывается. Мы нашли такие события, которые будут начальными точками для сессий:

3 Из основного датафрейма ’event_df’ создадим вспомогательный датафрейм ’session_start_df’. Этот датафрейм будет содержать события, которые будут считаться первыми событиями сессий. К таким событиям относятся все события, которые произошли спустя более чем 30 минут после предыдущего, либо события, которые были первыми для пользователя (NaT в колонке ’diff’).

Также создадим во вспомогательном датафрейме колонку ’session_id’, которая будет содержать в себе id первого события сессии. Она пригодится, чтобы корректно отобразить идентификатор сессии, когда будем объединять данные из основного и вспомогательного датафреймов.

sessions_start_df = event_df[(event_df['diff'].isnull()) | (event_df['diff'] > '1800 seconds')]
sessions_start_df['session_id'] = sessions_start_df['id']

Вспомогательный датафрейм ’session_start_df’ выглядит так:

4 С помощью функции merge_asof объединим между собой данные основного и вспомогательного датафреймов. Эта функция позволяет объединить данные двух датафреймов схожим образом с левым join’ом, но не по точному соответствию ключей, а по ближайшему. Примеры и подробности в документации.

Для корректной работы функции merge_asof оба датафрейма должны быть отсортированы по ключу, на основе которого будет происходить объединение. В нашем случае это колонка ’id’.

Обратите внимание, что из датафрейма ’session_start_df’ я выбираю только колонки ’id’, ’user_id’ и ’session_id’, так как остальные колонки особо не нужны.

event_df = event_df.sort_values('id')
sessions_start_df = sessions_start_df.sort_values('id')
event_df = pd.merge_asof(event_df,sessions_start_df[['id','user_id','session_id']],on='id',by='user_id')

В итоге получаем вот такой распрекрасный объединенный датафрейм, в котором в колонке ’session_id’ указан уникальный идентификатор сессии:

Дополнительные манипуляции

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

Обнаружить эти события предельно просто: их идентификаторы будут равны идентификаторам сессии. Для этого создадим колонку ’is_first_event_in_session’, в которой сравним между собой значения колонок ’id’ и ’session_id’.

event_df['is_first_event_in_session'] = event_df['id'] == event_df['session_id']

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

Для этого сначала считаем разницу между предыдущей и следующей страницей внутри сессии. Мы уже делали такое вычисление, когда считали разницу между временем посещения страниц пользователем. Только тогда мы группировали по ’user_id’, а теперь будем по ’session_id’.

event_df['time_on_page'] = event_df.groupby(['session_id'])['date_time'].diff(1)

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

Нам нужно сдвинуть значение столбца ’time_on_page’ на одну строку вверх внутри отдельно взятой сессии. Для этого нам пригодится функция shift.

event_df['time_on_page'] = event_df.groupby(['session_id'])['time_on_page'].shift(-1)

Получили то, что нужно:

Значения в столбце ’time_on_page’ имеют специфический тип datetime64, который не всегда удобен для арифметических операций, поэтому преобразуем ’time_on_page’ в секунды.

event_df['time_on_page'] = event_df['time_on_page'] / np.timedelta64(1, 's')

Вуаля:

3 На основе полученных данных очень просто посчитать различные агрегаты

event_df['user_id'].nunique() # Количество пользователей
event_df['session_id'].nunique() # Количество сессий
event_df['id'].count() # Количество просмотров страниц (событий)
event_df['time_on_page'].mean() # Среднее время просмотра страниц

Заключение

Таким образом, используя несколько не самых очевидных функций в Pandas (например, merge_asof мне довелось применять впервые), можно формировать сессии на основе лога событий. Логом событий могут выступать логи сервера, какой-нибудь клик-стрим в SaaS-сервисах, сырые данные систем веб-аналитики.

Удачи и новых аналитических достижений!

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

Становясь падаваном Logs API Яндекс.Метрики

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

Но Метрика не остановилась в своем развитии на обычной API, которая позволяет вытащить данные только по заданному списку группировок, ограниченному 10 группировками в запросе. Разработчики Яндекса предоставили возможность получать «сырые» данные из хранилища данных Яндекс.Метрики — Logs API.

Агрегированные, или обобщённые данные, которые вы видите в интерфейсе Метрики или выгружаете через АПИ отчетов, рассчитываются для определённой группы визитов. Например, метрика «время на сайте» вычисляется для всех переходов из какого-либо источника трафика, всех визитов от посетителей мужского пола или всех визитов с планшетов.

А основой для этих расчётов служат сырые данные — записи об отдельных визитах или просмотрах. Таблица с этими записями и передаётся через Logs API, при этом каждая запись дополнена полезными сведениями из Метрики. Это подробные данные по Директу и по электронной коммерции, страна и город посетителя, а ещё — различная техническая информация о визите: например, браузер и модель мобильного телефона.

Если вы новичок в работе с АПИ Метрики, то я рекомендую сначала ознакомиться со статьей «Становясь гуру API Яндекс.Метрики». Она даст понимание того как работает API и как выгружать данные из Метрики с помощью API отчетов. Информация в ней нам ещё будет полезна, чтобы получить авторизационный токен.

В этой статье я хочу поделиться своим рецептом получения данных из Logs API Яндекс.Метрики, а также о нескольких приемах обработки этих данных.

Приступим!

Первое: Получить авторизационный токен

Процедура получения токена подробно описывается в пункте 4 предыдущей статьи. Для доступа к Logs API понадобится тот же токен, что и для доступа к API отчетов. Без авторизационного токена у нас не получится сделать запросы, это своего рода ключ доступа к данным вашего счетчика.

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

Второе: Запрос на создание лога

После того как мы получили авторизационный токен, например, AQAAAAAHrQEBAADn-FX3DPJUn04fkptrzvFv8nE, мы должны сформировать запрос к Logs API, который создает лог. Лог формируется на стороне Яндекс.Метрики в течении определенного времени, которое зависит от того сколько параметров визита или просмотра вы хотите получить, а также от диапазона времени, за который нужен лог.

Метод «Создание лога запросов» создает запрос на подготовку отчета, в котором будут нужные нам данные.

Это POST-запрос, следующей структуры:

POST https://api-metrika.yandex.ru/management/v1/counter/{counterId}/logrequests ? 
date1=<string>
 & date2=<string>
 & fields=<string>
 & source=<log_request_source>

Параметры запроса на создание лога

Рассмотрим все параметры, которые нужно передать в запросе:
{counterId} — идентификатор счетчика Метрики
date1 — дата начала отчетного периода в формате YYYY-MM-DD (например, 2015-08-31).
date2 — дата конца отчетного периода в формате YYYY-MM-DD (не может быть текущим днем)
fields — список полей, которые надо получить. Поля разделяются запятыми.

Давайте параметр fields подробнее.

Поля — это те параметры визитов или просмотров, которые Яндекс выгрузит из своей базы данных и предоставит нам в виде файла в формате CSV. Существует две категории полей, которые можно использовать в Logs API:

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

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

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

  • ym:s:visitID — идентификатор визита;
  • ym:s:date — дата визита;
  • ym:s:clientID — идентификатор пользователя на сайте;
  • ym:s:pageViews — глубина просмотра;
  • ym:s:ym:s:startURL — страница входа.

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

Следующий параметр source, который задает источник логов. Тут всё просто: если вы хотите получить данные по визитам, то нужно указать visits; если нужны данные по просмотрам — указываем hits.

Последний параметр oauth_token — это авторизационный токен, который мы получили в предыдущем пункте.

Сформируем тестовый запрос:
https://api-metrika.yandex.ru/management/v1/counter/30177909/logrequests?date1=2017-03-01&date2=2017-03-06&fields=ym:s:visitID,ym:s:date,ym:s:clientID,ym:s:pageViews,ym:s:startURL&source=visits&oauth_token=AQAAAAAHrQEBAADn-FX3DPJUn04fkptrzvFv8nE

Дальше нужно сделать POST-запрос. Один из самых простых способов сделать POST-запрос, не прибегая к программированию — воспользоваться расширением Postman для Chrome.

Как сделать POST-запрос к API Яндекс.Метрики?

1 — Устанавливаем и запускаем расширение Postman:

2 — Выбираем тип HTTP-запроса «POST», а в поле ввода запроса вставляем сформированный выше запрос:

3 — Нажимаем синюю кнопку «Send»

4 — Получаем ответ от API:

5 — В ответе нас интересует идентификатор request_id. Это идентификатор, созданного запроса на получение данных из Logs API:

Этот идентификатор копируем, он понадобится нам на следующем шаге.

Третье: Получение информации о запросе логов

После того как мы отправили в АПИ заявку на формирование лога, нужно получить статус лога: узнать готов ли он для скачивания. Для этой цели существует метод «Информация о запросе логов».

Вызывается этот метод с помощью следующего GET-запроса:

GET https://api-metrika.yandex.ru/management/v1/counter/{counterId}/logrequest/{requestId}

Вместо counterId подставляем идентификатор счетчика, для которого мы делали запрос на создание, а вместо requestId ставим идентификатор request_id, полученный в ответе на предыдущий запрос. После этого через знак «?» указываем параметр oauth_token.

Таким образом, наша сформированная ссылка для получения информации о запросе лога выглядит так:
https://api-metrika.yandex.ru/management/v1/counter/30177909/logrequest/45264?oauth_token=AQAAAAAHrQEBAADn-FX3DPJUn04fkptrzvFv8nE

По сути, это обычный GET-запрос, поэтому выполнить его можно и в обычном браузере, но удобнее будет в Postman, потому что в него встроен pretty-вывод JSON и смотреть на результат будет приятнее.

Вставляем запрос в Postman, выбираем тип HTTP-запроса «GET» и нажимаем «Send»:

В ответе нас интересует параметр status. Этот параметр может принимать несколько значений, которые описаны тут. В нашем случае он принимает значение «processed», которое говорит о том, что запрос лога обработан и лог готов к скачиванию. Это то, что нужно!

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

Перейдем к скачиванию лога.

Четвертое: Загрузка лога

Чтобы скачать подготовленный лог нам понадобится метод «Загрузка части подготовленных логов обработанного запроса».

Этот метод, также как и предыдущий, вызывается с помощью GET-запроса:

GET https://api-metrika.yandex.ru/management/v1/counter/{counterId}/logrequest/{requestId}/part/{partNumber}/download

Аналогично тому как мы делали это в предыдущем запросе, вместо counterId подставляем идентификатор счетчика, вместо requestId указываем уже известный нам идентификатор request_id, а на место partNumber ставим порядковый номер той части лога, которую мы хотим скачать. В нашем примере всего одна часть, поэтому ставим 0. После этого через знак «?» указываем параметр oauth_token.

Сформированная ссылка для скачивания лога будет такой:
https://api-metrika.yandex.ru/management/v1/counter/30177909/logrequest/45264/part/0/download?oauth_token=AQAAAAAHrQEBAADn-FX3DPJUn04fkptrzvFv8nE

Этот GET-запрос можно выполнять как в браузере, так и через Postman, но в случае с Postman’ом вместо «Send» надо выбрать вариант «Send and Download»:

А затем сохранить полученный файл в формате CSV:

Всё! Мы получили лог и сохранили его себе на диск, поэтому можно избавить Яндекс от хранения лишней информации и очистить лог с помощью метода «Очистка подготовленных для загрузки логов обработанного запроса». Делается это с помощью уже привычного нам POST-запроса. Расписывать не буду, надеюсь разберетесь сами :)

Заключительное: Обработка лога

Честно говоря, обработка логов с помощью Excel — это то ещё извращение, я бы рекомендовал использовать для таких задач что-то более подходящее, например, Pandas или R. Но Excel — базовый инструмент анализа данных, поэтому рассмотрю на его примере.

Открываем csv-файл в Excel. Для этого создаем новую книгу, открываем вкладку «Данные» и выбираем «Из текста»:

Выбираем файл и указываем в мастере текстов, что наш файл содержит разделители и записан в кодировке UTF-8:

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

Итак, мы загрузили наши данные в таблицу:

Дальше давайте решим две простые задачи:

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

Построим простейшую сводную таблицу:

В строки вынесем ID пользователей (ym:s:clientID), а в значения — ID визита (ym:s:visitID) и глубину просмотра (ym:s:pageViews). Из-за того, что для поля ym:s:visitID мы указали как текстовый столбец, сводная таблица автоматически посчитает не сумму айдишников, а их количество. Это и будет количеством визитов на пользователя.

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

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

Видно, что первый пользователь заходил на протяжении 3 дней с одной и той же страницы, скорее всего, он добавил её в закладки и это были прямые переходы, чтобы подтвердить эту гипотезу нужно бы сделать ещё одну выгрузку из Logs API с указанием параметра ym:s:referer (реферер) или ym:s:lastTrafficSource (источник трафика):

Второй пользователь вел себя по-другому. Он совершил в ходе одного из визитов 16 просмотров, благодаря чему и стал рекордсменом по числу просмотров. Для этого пользователя было бы интересно посмотреть какие страницы он просматривал. Это можно сделать, добавив в параметры выгрузки параметр ym:s:watchIDs (идентификаторы просмотров, которые были в визите):

Естественно, применение Logs API не ограничено этими простенькими ситуациями, можно делать намного более сложные вещи, вроде когортного анализа, сложных моделей аттрибуции, анализа посещаемости страниц (например, определить на какую страницу чаще всего переходят после просмотра определенной страницы, или даже построить вероятностную модель переходов). Но все эти задачи в Excel не так просто решаются. Здесь на помощь приходят более мощные инструменты анализа данных: Python и Pandas, R, SQL. Об этом как-нибудь в другой раз :)

Да и получать данные из Logs API с помощью запросов в Postman’е — это не совсем удобный способ. Если вам захочется делать эти выгрузки постоянно, всё складировать у себя и периодически анализировать — лучший вариант складывать данные в БД ClickHouse. Благо, что для интеграции Logs API с ClickHouse в Яндексе уже разработали простой в использовании Python-скрипт. Конечно, это сложнее, придется поднять ClickHouse, научиться запускать скрипты на Python’е, а еще лучше не ручками, а по расписанию, но, поверьте, всё это намного интереснее и открывает кучу новых возможностей!

Успехов!

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

Как получить список станций Московского метрополитена по API

Станции Кольцевой линии Московского метрополитена

Существует большое количество способов получить список станций Московского метро. Их можно разделить на 2 категории: ручные и автоматизированные.

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

В этой статье я собрал несколько автоматизированных способов получения всех станций Московского метро. Некоторые из них предельно простые и используют один запрос к API, другие чуть посложнее и предполагают парсинг веб—страниц.

API Superjob

Сервис поиска работы Superjob предлагает метод API для получения списка станций:
https://api.superjob.ru/2.0/suggest/town/4/metro/all/

Цифра 4 после town в запросе — это идентификатор города. Так что этот метод годится не только для метро в Москве, но и для других городов, где есть метро. Узнать идентификатор города можно с помощью запроса:
https://api.superjob.ru/2.0/towns/?all=1&genitive=1

Ответ приходит вот в JSON с вот такой простой структурой:

Хозяйке на заметку: если нужны склонения городов, то можно взять из этого JSON.

Запрос возвращает станции метро в JSON:

На момент написания статьи (январь 2017) метод отдавал 220 станций. В списке отсутствуют станции Московского центрального кольца. Еще один очевидный минус этого метода — отсутствие координат станций.
Полная документация API Superjob доступна по ссылке.

API HeadHunter

Еще один сервис для поиска работы HeadHunter предоставляет API, в котором есть удобный метод для получения справочника по станциям метро:
https://api.hh.ru/metro/1

Цифра 1 указывает на идентификатор города в HeadHunter. В документации можно ознакомиться с методом, позволяющим получить дерево всех регионов, если вдруг понадобится найти станции метро для другого города.

Метод возвращает структуру в JSON—формате, в которой на верхнем уровне иерархии находится список линий метро lines:

Отдельный плюс — это указание цвета ветки в атрибуте hex_color.
Внутри каждой ветки в массиве station лежат станции, принадлежащие этой ветке:
И здесь несказанно радует наличие географических координат, а также атрибут order, содержащий порядковый номер станции в линии.

По состоянию на январь 2017 метод отдаёт 240 станций метро, включая станции Московского центрального кольца.

Еще одна интересная особенность метода — можно не указывать город и сделать запрос https://api.hh.ru/metro/. В этом случае ответом будет список всех городов, которые добавлены в HeadHunter и в которых есть метрополитен. А уже внутри каждого города будет массив lines. Удобно!

Портал открытых данных Правительства Москвы

На портале открытых данных Москвы есть справочник под названием «Станции Московского метрополитена»:
https://data.mos.ru/classifier/7704786030-stantsii-moskovskogo-metropolitena

Эти же данные можно получить, обратившись к API портала data.mos.ru:
http://api.data.mos.ru/v1/datasets/1488/rows

В данных отсутствуют географические координаты станции, но эту проблему можно решить подружив данные с геокодером Яндекса (рассказ о его использовании чуть ниже). В результатах, которые отдает data.mos.ru есть указание административных округов и районов, в которых расположены станции. Еще одна любопытная деталь, которой нет в других источниках — наличие строящихся станций Московского метрополитена.

Всего список насчитывает 296 станций, из которых 236 — действующие станции метро.

API 2Гис

API 2Гис не доступно публично. Официально, чтобы им пользоваться необходим ключ доступа, который предоставляется по запросу. Но дело в том, что веб—интерфейс 2Гис сам обращается к тому же API, поэтому в запросах от браузера можно найти GET-запросы вот такого вида:
https://catalog.api.2gis.ru/2.0/suggest/list?key=ruczoy1743®ion_id=36&lang=ru&q=foobar
Здесь key — ключ доступа к API, который судя по всему имеет достаточно длительный срок жизни. Поэтому пользуйтесь на здоровье.

Я не нашел в документации API 2Гис метода, который позволял бы найти все станции метро одним запросом. Но есть замечательный метод, позволяющий найти транспортный маршрут:
https://catalog.api.2gis.ru/2.0/transport/route/search?key=ruczoy1743&q=Сокольническая&subtype=metro,monorail®ion_id=32
В параметре subtype задается тип маршрута, который нужно выводить в результатах, а именно метро и монорельс; в region_id указываем идентификатор города.

В ответ приходит результат поиска по маршрутам:

Зная ID маршрута, можно получить список остановок, выполнив запрос:
https://catalog.api.2gis.ru/2.0/transport/route/get?id=4504205217760068&fields=items.region_id&key=ruczoy1743

Собственно, это то, что нужно:

Замечу, что станции тут располагаются в массиве platforms в порядке их следования.

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

В базе 2Гис на январь 2017 года содержится 236 станции Московского метро.

Сайт mosmetro + геокодер Яндекса

Этот способ самый «костыльный» из всех способов и его я рассмотрю только для того, чтобы показать как можно работать с геокодером Яндекса для поиска станций метро.

Во—первых, нам понадобится список станций метро, который можно взять со страницы «Расписание поездов» на официальном сайте Московского метрополитена.

Названия всех станций — это тексты ссылок в блоке с классом «schedulestations». Их можно достать, написав простейший парсер HTML—страницы.

Вторым шагом работы нашего «костыля» будет обращение с названием каждой из станций к геокодеру Яндекса:
https://geocode-maps.yandex.ru/1.x/?geocode=метро%20Третьяковская&results=100&format=json

(Обратите внимание на приписку «метро» к названию станции, это нужно, чтобы отсеять лишние результаты, например, «улица Третьяковская»)

Результаты поиска — объекты GeoObject, в которых содержатся данные о найденных по запросу станциях метро. Атрибут kind указывает на тип объекта. Станции метро имеют тип metro, кроме станций монорельса, у них тип station. Нужно помнить, что для одной станции может быть несколько линий, поэтому объектов GeoObject может быть больше одного.

XML от Циан

Классифайд по недвижимости Циан предоставляет список в виде XML:
https://www.cian.ru/metros.php

К сожалению, не указаны ветки.

XML от Авито

Сайт объявлений Авито использует вот такой XML-справочник с местоположениями:
http://autoload.avito.ru/format/Locations.xml

Этот справочник включает сущности типа subway — это станции метро.

API Яндекс.Метро

У Яндекс.Метро есть слабодокументированное API — https://metro.yandex.ru/api/. Подозреваю, что API скорее для внутреннего пользования. Оно выгодно отличается от других способов получения станций тем, что можно получить не просто станции, но всю схему Московского метро в SVG с помощью метода get-scheme-geometry: https://metro.yandex.ru/api/get-scheme-geometry?id=1&lang=ru. Отдельно станции можно получить с помощью метода get-stations: https://metro.yandex.ru/api/get-stations?id=1&lang=ru. Еще можно получить метаданные методом get-scheme-metadata: https://metro.yandex.ru/api/get-scheme-metadata?id=1&lang=ru. В метаданных можно найти информацию о пересадках и различные примечания, например, информацию о вестибюлях, закрытых на ремонт.

Файлы

Если вам не нужно API, а достаточно JSON, CSV или XLSX — их есть у меня:

Файлы актуальны на январь 2017 года, данные получены с помощью API HeadHunter. Постараюсь обновлять регулярно. Об ошибках, недочетах, или если вдруг файлы станут недоступны сообщайте в комментах к посту.

Знаете еще какие—то способы получения станций Московского (и не только Московского) метро? Делитесь в комментариях.

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

Новые пользовательские группировки в API Яндекс.Метрики

30 мая 2016 года Яндекс.Метрика выкатила новый тип сегментации: сегментацию по пользователям. Теперь аналитика в Метрике стала человекоцентрированной (вернее пользователецентрированной), а интерфейс сегментации значительно упростился. С внедрением сегментации по пользователям в API Метрики появились новые группировки и метрики. Пока что они не документированы, но, надеюсь, в скором времени описание новых возможностей появится в документации API Яндекс.Метрики.

Одна из самых интересных новых группировок — ym:s:specialUser. При добавлении этой группировки в dimensions мы сможем детализировать данные по пользователям. Например, можно получить отчет по поисковым системам, с помощью которого будет видно пользователей, приходивших на сайт несколько раз, но по разным запросам или поисковым системам. Запрос выглядит так:

(Вместо «token» вставить авторизационный OAuth-токен, вместо «id» — номер счетчика. Чтобы получить данные в CSV нужно добавить .csv после data в запросе. Подробнее о работе с API Яндекс.Метрики можно прочитать в моей статье)

Вот как выглядит такой отчет в Excel:

На отчете видно, что один и тот же пользователь посещал сайт несколько раз с 26 по 31 мая, при этом каждый визит был зафиксирован с разных поисковых фраз.
Использование группировки ym:s:specialUser — это интересная возможность, позволяющая выявить ассоциированные запросы, или построить отчет схожий с многоканальными последовательностями (MCF) в Google Analytics.

Еще один запрос, в котором можно использовать ID пользователя — получение данных о кампаниях и ключевых фразах Яндекс.Директа:

Получившийся отчет выглядит так:

С помощью этих данных мы можем понять сколько пользователей заходят с двух или более кампаний Яндекс.Директа (определить % пересечения аудитории между кампаниями). Также эти данные позволяют нам понять какие пользователи совершают посещения, перейдя с разных запросов Яндекс.Директа.

Еще одно нововведение: появились новые типы группировок и метрик. Раньше в Метрике было два типа группировок и метрики: уровня хита (они начинались с ym:pv) и уровня визита (они начинались с ym:s), теперь появился уровень пользователя. Группировки и метрики этого типа начинаются с ym:u. Именно по этим группировкам производится сегментация пользователей.

Одна из самых полезных группировок пользовательского типа — ym:u:userFirstVisitDate. Эта группировка содержит в себе дату первого визита пользователя. Её удобно использовать в filters для того, чтобы получить данные о посещениях пользователей с конкретной датой первого визита. Пример запроса:

Обратите внимание, что при фильтрации используется специальный оператор EXIST.

Такой отчет будет содержать данные только тех пользователей, которые совершили первое посещение сайта в определенную дату (в моем примере — 13 мая 2015):

Приведу еще пару интересных примеров использования группировок ym:u.

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

Такой запрос к API возвратит отчет по всем пользователям, которые были на сайте в отчетный период, с указанием различных параметров (например, пол):

Обратите внимание, содержимое параметра ym:u:userID совпадает с ym:s:specialUser. Это означает, что мы можем сопоставить данные из любого отчета, в котором используется ym:s:specialUser, с данными пользователей, например, с датой первого визита. Это открывает возможности для проведения когортного анализа в Яндекс.Метрике.
Еще один важный момент: в запросе нельзя использовать dimensions разных типов, то есть мы не можем в одном запросе получить группировки с ym:u и ym:s одновременно, например, ym:u:userID и ym:s:date. Поэтому придется делать два запроса и по ym:u:userID джоинить данные между собой.

Во-вторых, с помощью filters мы можем получать данные о специфичных пользователях, выполнивших какое-либо условие. К примеру, вот запрос, позволяющий получить пользователей, которые хотя бы раз заходили с источника трафика Google:

Напоследок, привожу полный список новых группировок, которые мне удалось раскопать:

  • ym:s:specialUser — Идентификатор посетителя (uid) уровня визита
  • ym:u:userID — Идентификатор посетителя (uid) уровня посетителя
  • ym:u:userVisits — Общее число визитов посетителя
  • ym:u:totalVisitsDuration — Суммарная длительность визитов посетителя
  • ym:u:pageviews — Суммарное количество просмотров страниц пользователем
  • ym:u:userFirstVisitDate — Дата первого визита посетителя
  • ym:u:firstSourceEngine — Первый источник трафика (детально) посетителя
  • ym:u:firstTrafficSource — Тип первого источника трафика посетителя
  • ym:u:gender — Пол посетителя
  • ym:u:ageInterval — Возрастной диапазон посетителя
  • ym:u:interest — Интерес пользователя
  • ym:u:daysSinceFirstVisitOneBased — Количество дней с первого визита пользователя

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

Список полезностей

Рейтинг сайтов по количеству ссылающихся IP от Мегаиндекса
Социальные кнопки Лайкли от Ильи Бирмана
Извлечение контента со страницы
Сервис для быстрого создания веб-страничек. Позволяет создать вот такую страничку. Полезно, когда надо максимально быстро поделиться текстовым контентом, без регистраций и мусора
Максим Ильяхов о процессе согласования
План обучения для джедаев в IT-Agency
Как быстро перейти к Pandas (Python), если знаешь SQL
Расчет средневзвешенного в Pandas
Data Flow Diagrams
Книга «Автоматизируем рутинную фигню с Python»
Дистрибутивные семантические модели для русского языка
GitBook: создание книг на основе документов в GitHub
Электронный учебник по типографике и верстке от Горбунова
37 сайтов, чтобы научиться новому
Гайд по работе с Microsoft PowerBI
Зачем вести блог
Кого читать в FB

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

2015  
Ранее Ctrl + ↓