В этот раз разберёмся, как с помощью Python передавать в Clickhouse данные по рекламным кампаниям и построим агрегат, используя материализованное представление.
Для чего нам материализованные представления? Часто Clickhouse используется для работы с огромными объемами данных, а время получения ответа на запрос к таблице с сырыми данными постоянно растёт. Стандартно, чтобы решить такую задачу эффективным способом, чаще всего используют ETL-процессы или создают таблицы агрегатов, что не очень удобно, ведь их необходимо регулярно пересчитывать. Clickhouse обладает встроенной и эффективной возможностью для решения задачи — материализованными представлениями.
Материализованные представления физически хранят и обновляют данные на диске в соответствии с запросом SELECT, на основе которого представление было создано. При вставке данных в искомую таблицу SELECT преобразовывает данные и вставляет их в представление.
Настройка машины
Наш скрипт на Python из предыдущих материалов необходимо подключить к Clickhouse — он будет отправлять запросы, поэтому нужно открыть несколько портов. В Dashboard AWS переходим в Network & Security — Security Groups. Наша машина входит в группу launch-wizard-1. Переходим в неё и смотрим на Inbound rules: нам нужно добавить правила как на скриншоте.
Настройка Clickhouse
Теперь настроим Clickhouse. Отредактируем файл config.xml в редакторе nano:
чтобы доступ к базе данных был с любого IP-адреса:
Создание таблицы и материализованного представления
Зайдём в клиент и создадим нашу базу данных, в которой впоследствии создадим таблицы:
CREATE DATABASE db1
USE db1
Мы проиллюстрируем всё тот же пример сбора данных с Facebook. Информация по кампаниям может часто обновляться, и мы, в целях упражнения, хотим создать материализованное представление, которое будет автоматически пересчитывать агрегаты на основе собранных данных по затратам. Таблица в Clickhouse будет практически такой же, как DataFrame из прошлого материала. В качестве движка таблицы используем CollapsingMergeTree: он будет удалять дубликаты по ключу сортировки:
CREATE MATERIALIZED VIEW fb_aggregated
ENGINE = SummingMergeTree()
ORDER BY date_start
AS
SELECT campaign_id,
date_start,
sum(spend * sign) as spent,
sum(impressions * sign) as impressions,
sum(clicks * sign) as clicks
FROM facebook_insights
GROUP BY date_start, campaign_id
К сожалению, в Clickhouse UPDATE отсутствует, поэтому необходимо придумывать некоторые ухищрения. Мы воспользовались рецептом от команды Яндекса для обходного пути команды UPDATE. Идея состоит в том, чтобы в начале вставить строки, которые уже были в таблице с отрицательным Sign, а затем использовать Sign для сторнирования. Следуя этому рецепту старые данные не будут учитываться при суммировании.
Скрипт
Начнём писать скрипт. Понадобится новая библиотека — clickhouse_driver, позволяющая отправлять запросы к Clickhouse из скрипта на Python:
В материале приведена только доработка скрипта, описанного в статье «Собираем данные по рекламным кампаниям в Facebook». Всё будет работать, если вы просто вставите код из текущего материала в скрипт предыдущего.
from datetime import datetime, timedelta
from clickhouse_driver import Client
from clickhouse_driver import errors
Объект класса Client позволит отправлять запросы методом execute(). В host вводим свой public dns, user ставим default, port — 9000 и в database базу данных для подключения.
Пусть, например, мы хотим рассматривать данные за последние три дня. Получим эти даты библиотекой datetime и переведём в нужный формат методом strftime():
Наконец, напишем наш алгоритм: вставляем те же самые данные с sign = −1, оптимизируем для удаления дубликатов движком CollapsingMergeTree и выполняем INSERT новых данных со знаком sign = 1.
SQL_query = 'INSERT INTO facebook_insights VALUES'
client.execute(SQL_query, new_data_list)
SQL_optimize = "OPTIMIZE TABLE facebook_insights"
client.execute(SQL_optimize)
for i in range(len(insight_campaign_id_list)):
client.execute(SQL_query, [[insight_campaign_id_list[i],
insight_clicks_list[i],
insight_spend_list[i],
insight_impressions_list[i],
datetime.strptime(insight_date_start_list[i], '%Y-%m-%d').date(),
datetime.strptime(insight_date_start_list[i], '%Y-%m-%d').date(),
1]])
client.execute(SQL_optimize)
Вернёмся в Clickhouse. Выполним SELECT * FROM facebook_insights LIMIT 20, чтобы посмотреть первые 20 строк таблицы:
И SELECT * FROM fb_aggregated LIMIT 20, чтобы проверить наше представление:
Отлично! Мы сделали материализованное представление — теперь новые данные, поступающие в таблицу facebook_insights будут поступать и в материализованное представление fb_aggregated и каждый раз пересчитываться благодаря SummingMergeTree. При этом трюк с sign позволяет отлавливать уже обработанные записи и не допускать их суммирования, а CollapsingMergeTree — чистить дубликаты.
В прошлый раз мы узнали, как получить полную информацию по рекламным кампаниям Facebook. А для анализа этой информации можно загрузить данные в Redash. Чтобы наш скрипт передавал данные в Redash, загрузим его в облако AWS и поднимем сервер на aiohttp. Но сперва необходимо немного улучшить наш скрипт.
from facebook_business.api import FacebookAdsApi
from facebook_business.adobjects.adaccount import AdAccount
from facebook_business.adobjects.adreportrun import AdReportRun
from facebook_business.adobjects.adsinsights import AdsInsights
from facebook_business.adobjects.adaccountuser import AdAccountUser as AdUser
from facebook_business.exceptions import FacebookRequestError
import time
Redash, к примеру, принимает данные в формате json. Ранее мы не разбирали этот источник данных, сегодня изучим и его. Файл формата json выглядит как список словарей — его наш новый скрипт и будет передавать в Redash, предварительно переведя в json. Из прошлого скрипта нам понадобятся переменная с полями, access token, app id, app secret и две функции: wait_for_async_job() и get_insights(). Вторая функция принимает все параметры аккаунта и асинхронно собирает информацию по рекламным кампаниям — из них мы выбираем поля клики, просмотры, затраты и даты.
Получение данных обернём в функцию return_json() — она будет вызывать get_insights(), заполняя список insights_lists. Так как рекламных кампаний в аккаунте может быть несколько, наш результат окажется не списком словарей, а списком списков словарей. Для этого используем простое лямбда-выражение, «сглаживающее» список списков в список. Затем возвращаем наш insights_lists_flatten.
def return_json():
insights_lists = []
date_preset = 'last_year'
for elem in my_accounts:
elem_insights = get_insights(elem, date_preset)
insights_lists.append(elem_insights)
flatten = lambda lst: [item for sublist in lst for item in sublist]
insights_lists_flatten = flatten(insights_lists)
return insights_lists_flatten
Теперь наш скрипт возвращает список словарей с информацией. Но ещё нам нужен сервер на aiohttp, который будет собирать список и возвращать его в формате json. Создадим новый файл, импортируем библиотеку aiohttp и функцию get_json() из нашего прошлого скрипта. Напишем простой обработчик запросов — данные из Facebook скрипт получает асинхронно, так что асинхронная функция handler будет «спать», пока вся информация не будет собрана и передана. Возвращает функция данные через json_response, чтобы информация передавалась в формате json.
from aiohttp import web
from get_json import return_json
async def handler(request):
data = return_json()
return web.json_response(data)
Теперь идём на AWS, создаём папку и через sftp кидаем туда оба скрипта. Проверим, что в ACL на AWS нужный порт открыт. Заходим в console — network & security — security groups — default.
Запускаем файл с сервером. После запуска можно проверить, работает ли скрипт, обратившись к нему по ip сервера с портом 0880 на route, указанный в скрипте с сервером — там мы указали /json.
Теперь обратимся через Redash к URL и получим ту самую таблицу, которую вернул скрипт:
url: ip сервера
Сформировав query results, можем написать запрос следующего вида:
select date_start, sum(clicks) as clicks, sum(impressions) as impressions, sum(spend) as spend from query_45
group by date_start
Получаем такую таблицу, сгруппированную по колонке date_start:
Теперь по этой таблице можно отобразить данные на графике — посмотрим, как количество затрат на рекламу влияет на количество кликов по ней:
Готово! В следующий раз поработаем с ВКонтакте — получим информацию по рекламным кампаниям уже оттуда.
Давайте узнаем, как получить информацию о затратах, кликах и показах рекламных кампаний из Facebook.
from facebook_business.api import FacebookAdsApi
from facebook_business.exceptions import FacebookRequestError
from facebook_business.adobjects.adaccount import AdAccount
from facebook_business.adobjects.adreportrun import AdReportRun
from facebook_business.adobjects.adsinsights import AdsInsights
from facebook_business.adobjects.campaign import Campaign
from facebook_business.adobjects.adset import AdSet
from facebook_business.adobjects.adaccountuser import AdAccountUser as AdUser
from facebook_business import adobjects
from matplotlib import pyplot as plt
from pandas import DataFrame
import time
Получение ключа
Первое, что необходимо сделать для работы с Facebook API — создать приложение. Для этого заходим на https://developers.facebook.com — My Apps — Create App. Заполняем название приложения, контактный Email и жмём на «Create App ID»
Перед нами открывается Dashboard приложения. Переходим в Settings — Basic и записываем себе App ID и App Secret. Эти данные понадобятся для авторизации.
Теперь переходим в Tools — Graph API Explorer и оказываемся в меню для создания нового access token.
Токены выдаются под разные нужды, и нам нужно задать определенные права для нашего. Понадобится только на ads_managment — это право позволяет получать информацию о рекламных кампаниях вашего аккаунта Facebook. Добавляем его и нажимаем на «Generate Access Token».
Осторожно — user access token изначально выдается как short-lived, уже через 1-2 часа он перестанет действовать. Для получения long-lived Token можно нажать на синюю кнопку, чтобы открыть Access Token Info, а затем на «Open in Access Token Tool». Откроется новая страница, внизу которой появится синяя кнопка «Extend Access Token». После этого сгенерируется новый long-lived Access Token, действующий 60 дней.
Написание скрипта
Создадим три переменных, в которые запишем access token, App ID и App Secret. Авторизуемся через метод init() класса FacebooksAdsApi и добавляем пользователя. Метод get_ad_accounts() вернёт нам данные по всем нашим рекламным аккаунтам в виде словаря. По этим же данным можем получить информацию о кампаниях методом get_campaigns().
Попробуем получить amount spent через my_account. Для этого воспользуемся методом api_get(), передав в параметр fields поле AdAccount.Field.amount_spent. Теперь, чтобы получить желаемые данные, выведем поле у переменной my_account, поделив на 100, чтобы обрубить копейки. Расходы получаем в валюте аккаунта, в нашем случае это RUB. То, ради чего мы всё это затеваем — получить данные о расходах на рекламные кампании для последующего анализа.
Объявим переменную fields — в этом списке будут храниться поля, которые мы хотим получать: id кампании, количество кликов, затрат и просмотров. Теперь опишем две функции. Первая будет асинхронно отправлять запросы к Facebook и возвращать результаты. Вторая — формирует эти запросы и передает в первую функцию. В результате будем получать список словарей.
Следующий шаг — получение искомых данных о затратах. Будем собирать данные за всё время, поэтому заведём переменную date_preset, значение которой поставим lifetime. И для каждого аккаунта вызовем функцию get_insights(), а список, который она возвращает, положим в insights_lists.
Создадим DataFrame и вытащим из insights_lists интересующие данные — это id кампании, количество кликов, затраты и просмотры.
Давайте подытожим эти данные — сгруппируем по кампаниям и посчитаем суммы каждой колонки для групп. Для этого в pandas реализованы методы groupby() и sum() — достаточно указать, по какой колонке проводить группировку.
df.groupby(['campaign_id']).sum()
Теперь построим два графика — по количеству кликов и просмотров относительно дат. Воспользуемся атрибутом rcParams, чтобы задать размеры графиков.
Вот и всё! Получили всю информацию о затратах, просмотрах и кликах. В следующем материале посмотрим, как выгрузить её в формате json и передать в Redash для последующего анализа и визуализации.
Пост написан при участии команды Valiotti Analytics: Елисей Рыков (младший аналитик), Георгий Ушаков (технический консультант)
Сегодня у нас в выпуске лонгрид при поддержке телеграмм-канала Русский маркетинг на тему аналитических метрик в маркетинге. В рамках статьи обсудим для чего нужна маркетинговая аналитика, какими метриками следует оперировать при расчете эффективности маркетинга, как можно структурировать работу по построению маркетинговой отчетности. Коснемся ключевых верхнеуровневых KPI, обсудим популярный фреймворк и разберемся как считать важные аналитические показатели. Статья получилась довольно объемная и в ее содержании используется множество сокращений, поэтому не обошлось и без глоссария.
Глоссарий
Revenue / Income / Sales — выручка, доход (руб. / $ / евро)
GMV (Margin) — маржа (% / руб. )
MAU (monthly active users) — уникальное число активных пользователей за месяц (шт.)
WAU (weekly active users) — уникальное число активных пользователей за неделю (шт.)
DAU (daily active users) — уникальное число активных пользователей за день (шт.)
CARC (customer acquision and retention cost) — стоимость привлечения и удержания клиента (руб. / $ / евро)
ДРР — доля рекламных расходов (%)
Для чего нужна маркетинговая аналитика?
Чтобы разобраться с аналитическими метриками, для начала следует разобраться зачем вообще нужна маркетинговая аналитика и на какие вопросы она может дать ответ. В целом, маркетинговая аналитика — это изучение и измерение в количественных показателях маркетинговой деятельности. При этом чаще всего цель данных действий — оценить эффективность маркетинга, посчитать окупаемость маркетинговых инвестиций в компанию.
Маркетинговая аналитика помогает найти ответы на следующие вопросы:
Какая целевая аудитория наиболее эффективно конвертируется?
Какие каналы коммуникаций наиболее / наименее прибыльны?
Что является наибольшим источником дохода компании?
Маркетинговый анализ следует начинать с определения ключевых показателей бизнеса и связей между ними, об этом мы поговорим чуть позже. В целом, работа над построением маркетинговой аналитики больше похожа на создание системы правильных метрик, их планирования, замеров и реагирования на изменения метрик. Более подробно цикл PDCA описан в книжке У. Деминга "Выход из кризиса", рекомендую для ознакомления.
Ключевые принципы построения правильной аналитики в маркетинге
Имея системный подход к анализу данных, влияющих на маркетинговую деятельность, можно помочь маркетологам решить проблему, устранить боль, предоставить рекомендации для дальнейших маркетинговых стратегических шагов. Системный подход подразумевает соблюдение ряда ключевых принципов, без которых аналитика окажется неполноценной.
Компетентность
Задачей маркетингового анализа данных должен заниматься профессионал, разбирающийся в основах математической статистики, эконометрики, разумеется, умеющий считать, интерпретировать результаты и делать выводы актуальные для конкретного бизнеса (понимающий предметную область). Только в таком случае, аналитика сможет дать плоды, в противном случае некорректные выводы из данных могут только усугубить ситуацию, вследствие, чего проийзодет не оптимизация бюджета, а его разорение.
Объективность
Необходимо, решая задачу, рассматривать данные, которые влияют на проблему, с разных сторон. Разные показатели, разная агрегация данных позволит взглянуть на проблему объективно, желательно, чтобы один и тот же вывод из данных, повторялся как минимум дважды.
Актуальность
Изучая сегодняшние проблемы не следует оперировать устарелыми ретроспективными данными, мир очень быстро меняется, равно как и ситуация на рынке / в компании. Анализ, произведенный год назад, может дать сегодня совершенно иные результаты, поэтому необходимо регулярно освежать отчеты и данные, содержащиеся в них.
Интерпретируемость
Результаты анализа должны быть понятны человеку из бизнеса, не знакомому с техническими терминами. В идеале — каждый отчет помогает легко разобраться в проблеме и подталкивает читателя на очевидные выводы. Ситуация, при которой не аналитик вынужден копаться в огромной кучей графиков, непонятных диаграмм и страниц с цифрами без выводов недопустима.
Подобные принципы однозначно помогут нанять компетентных аналитиков для построения корректной отчетности.
Как структурировать показатели?
Систему метрик, помогающих оценивать эффективность маркетинга, можно построить исходя из нескольких соображений. Один из ключевых подходов к структуризации — жизненный цикл клиента. Постараемся разобраться в нем и поговорим об одном из интересных фреймворков для работы над такой системой метрик. В жизненном цикле клиента можно выделить основные этапы:
1) Охват аудитории — работа маркетолога начинается еще до того момента, как потенциальная аудитория становится клиентами компании
2) Вовлечение — этап конверсии зашедших пользователей на сайт / в мобильное приложение в зарегистрированных клиентов
3) Монетизация — этап формирования платящих пользователей (из зарегистрированных)
4) Удержание / Отток — мероприятия направленные на развитие и удержание привлеченной аудитории, снижение уровня оттока
Метод AARRR / Pirate Metrics
В 2007-ом году Дейвом МакКлюром был разработан и предложен метод AARRR — система метрик, помогающая стартапам разобраться в бизнес-показателях. Другое название метода, которое также можно встретить, — "пиратские метрики" из-за того, что название произносится на пиратский лад: "ааррр!".
Итак, разберемся в подходе и поговорим о метриках, соответствующих каждому этапу "воронки". Аббревиатура состоит из 5-ти ключевых маркетинговых этапов:
Аcquisition — привлечение (соответствует п. 1 выше)
Аctivation — активация (соответствует п. 2 выше)
Retention — удержание (соответствует п. 4 выше)
Revenue — доход / монетизация (соответствует п. 3 выше)
Referral — рекомендации (нововведенный этап)
На входе в воронку располагается целевая аудитория, которую мы хотим привлечь. Затем, всеми силами мы стараемся зарегистрировать потенциального покупателя и превратить его в зарегистрированного клиента (к этому моменту человек, зашедший к нам на сайт / в приложение должен осознавать ценность нашего продукта). После, клиент совершает покупки и возвращается к нам снова и снова. В конечном итоге, если ему очень нравится наш продукт, то он порекомендует его своим друзьям / знакомым.
На каждом уровне воронки необходимо выбрать метрики, описывающие переход из одного состояния в другое, которые мы можем подсчитать и проанализировать. Разберемся последовательно с каждым из этапов и соответствующим ему метриками. Будем изучать каждый этап на примере живых организаций, чтобы расчет показателей был максимально понятен в прикладном смысле.
Привлечение
Охват потенциальных покупателей — ключевой этап формирования новой аудитории. Изучим этот важный этап на примере мобильного приложения Grow Food и каналов привлечения трафика. Зачастую, аудитория попадают в приложение Grow Food из нескольких разных источников:
Органический трафик: поиск в Google, Yandex, Bing, etc
Органический мобильный трафик: поиск в Apple Store / Google Play
Обсудим на примере рекламы в Facebook. Каждое рекламное объявление таргетируется на потенциальную рекламную аудиторию, которая в терминах Facebook называется "Охват". При этом мы можем оптимизировать показы рекламного объявления по кликам / конверсиям / etc. Наша задача получить максимально эффективную аудиторию за минимальные деньги. Следовательно, нужно выбрать метрики, которые помогут нам оценить эффективность, изучим их:
Impressions — количество показов рекламного объявления, сам показатель мало о чем скажет и очень тесно связан с объемом потенциальной аудитории, однако потребуется нам для понимания остальных метрик.
Clicks — число кликов на рекламное объявление, в абсолютном выражении, опять же зависит от числа показов.
Installs — количество клиентов, установивших мобильное приложение
CTR — кликабельность, рассчитывается как отношение Clicks / Impressions и показывает насколько эффективно наше объявление с точки зрения заинтересованности аудитории, другими словами, какова кликабельность нашего объявления
CR (conversion rate) (= Installs / Clicks) — уровень конверсии, показывает какой процент пользователей установили приложение из тех, кто кликнул на рекламное объявление
Spend — число денег, которые мы потратили на данное рекламное объявление
CPC (= Spend / Clicks) — показывает нам стоимость одного клика, оперировать показателем следует в сравнении с другими объявлениями / рыночными бенчмарками
CPM (= Spend / Impressions * 1000 ) — показывает нам стоимость тысячи показов рекламного объявления, используется для сравненияи с другими объявлениями / бенчмарками
CPI (= Spend / Installs) — удельная стоимость одного инсталла
Revenue — итоговый доход, который мы получили с данного рекламного объявления / кампании (необходимо иметь инструменты для правильной атрибуции)
ROAS (= Revenue / Spend) — возврат инвестиций в рекламу, валовый доход с потраченного доллара, метрика показывает эффективность рекламной кампании с точки зрения вложенных в нее денег. К примеру, ROAS равный 300% говорит о том, что на каждый потраченный 1$ заработано 3$, а ROAS равный 30%, говорит о том, что на вложенный доллар заработано 30 центов.
Итого, мы уже имеем неплохую палитру метрик, с которыми можно работать — изучать их динамику, сравнивать объявления между собой, между разными источниками трафика. Например, простая табличка, содержащая эти показатели уже будет первым приближением к пониманию эффективности рекламы.
Facebook Campaign Efficiency
Advertisement
Spend ($)
Installs
CPI
Impressions
CPM
Clicks
CTR
CPC
ROAS
Creative Grow Food-1
x
x
x
x
x
x
x
x
x
Creative Grow Food-2
x
x
x
x
x
x
x
x
x
Данную таблицу можно перестроить таким образом, чтобы по вертикали оказались даты, а кампания выбиралась из фильтра, тогда мы начнем понимать изменения в динамике ключевых показателей привлечения трафика.
Резюме: мы можем измерять CTR разных баннеров и понимать какой из них интереснее для аудитории. Этот показатель можно использовать при A/B тестировании одного и того же баннера, выбирая наиболее эффективный. При подсчете эффективности помимо CTR следует иметь в виду CPC для того, чтобы выбрать не только наиболее кликабельный баннер, но и не самый дорогой.
Ключевые KPI, показатели эффективности с точки зрения денег — CPI / ROAS, первый показывает насколько дешево / дорого мы покупаем трафик, а второй — насколько хорошо купленный трафик монетизируется.
Активация
Предположим, что мы разрабатываем мобильную игру. Подумаем о том, что может являться активацией пользователя в этом случае? Мы привлекли пользователей, которые установили игру себе на смартфон. Наша следующая задача — зарегистрировать пользователя (сделать его игроком), предложить вводный тур для прохождения.
На этом этапе две метрики можно считать ключевыми: конверсия в зарегистрированного пользователя (= Registrations / Installs), конверсия в прошедших обучение (=Tutorial Users / Installs).
Соответственно, эти две метрики покажут нам: не слишком ли многого мы требуем от пользователя на этапе регистрации или, наоборот, регистрация дается крайне легко? Вторая метрика покажет насколько понятно введение в игру, заинтересованы ли пользователи проходить вводный тур, достаточно ли действий мы требуем от пользователя.
Более того, последнюю метрику можно декомпозировать, если в рамках обучения пользователю необходимо осуществить несколько действий, мы можем изучить воронку конверсий в каждое из действий и понять проблемные места активации новых пользователей. После того как мы активировали аудиторию, нам необходимо удержать ее, чтобы впоследствии заработать денег.
Удержание
Любая организация хотела бы, чтобы у нее существовала активная база лояльных клиентов, которые регулярно делают повторные заказы. В этой связи, очень важно отслеживать несколько ключевых метрик: Retention rate (или Rolling retention), Churn. Подробно я разбирал построение retention и rolling retention отчетов в одном из прошлых выпусков блога.
Другой важной фундаментальной метрикой можно считать Sticky Factor — степень вовлеченности пользователей. Sticky Factor за неделю достаточно просто рассчитывается: DAU / WAU * 100%. Разберем, более подробно на прошлом примере. У нас как и прежде есть таблица — client_session, в которой по каждому user_id хранятся таймстемпы активности created_at. Тогда расчет Sticky-фактора довольно несложно выполняется следующим SQL-запросом:
SELECT daily.dau/weekly.wau*100 AS sticky
FROM
-- Считаем среднее DAU за неделю
( SELECT avg(dau) AS dau
FROM
(SELECT from_unixtime(cs.created_at, "yyyy-MM-dd") AS event_date,
ndv(cs.user_id) AS dau
FROM client_session cs
WHERE 1=1
AND from_unixtime(cs.created_at)>=date_add(now(), -7)
AND from_unixtime(cs.created_at)<=now()
GROUP BY 1) d) daily,
-- Считаем WAU за неделю
( SELECT ndv(cs.user_id) AS wau
FROM client_session cs
WHERE 1=1
AND from_unixtime(cs.created_at)>=date_add(now(), -7)
AND from_unixtime(cs.created_at)<=now() ) weekly
Вместе с фундаментальными метриками следует рассматривать метрики, связанные с инструментами удержания клиентской базы. Такими могут являться инструменты директ-маркетинга: sms, email, push-уведомления. У каждого из инструментов обычно бывают такие описательные метрики: число отправленных сообщений / число доставленных сообщений / количество вернувшихся пользователей. Они показывают эффективность каждого из инструментов.
Монетизация
Наконец, мы добрались до ключевой метрики, которая интересвует всех бизнес-пользователей — деньги. Доход, выручка — денежные средства, которые мы получаем от пользователей при покупке нашего продукта. В абсолютном выражении эта метрика (или результат деятельности компании) не очень показательна, хотя важна для понимания текущих трендов.
Чаще всего оперируют рядом относительных метрик, которые описывают поведение пользователей: ARPU ( = Revenue / Users )— средняя выручка на одного пользователя cARPU( = cumulative Revenue / Users ) — накопленная средняя выручка на одного пользователя ARPPU ( = Revenue / Payers ) — средняя выручка на платящего пользователя Avg Receipt (= Revenue / Purchases ) — средний чек LTV / CLV — совокупный доход на одного пользователя (жизненная ценность клиента)
Вопросу LTV я планирую посвятить отдельный пост, поскольку это достаточно обширная тема. В данном посте разберем ARPU, накопленный ARPU и связь c LTV. Метрика ARPU покажет нам сколько мы в среднем зарабатываем с пользователя за какой-то период времени (обычно день или неделя). Это полезная информация, но ее может быть недостаточно. Задача эффективного маркетинга — привлекать таких пользователей, которые приносят компании больше денег, чем затрачивается на их привлечение. Таким образом, если мы модифицируем показатель ARPU и рассмотрим накопленный ARPU, например, за 30, 60, 90, 180 дней, то получим неплохое приближение к LTV пользователя. Еще лучше если мы построим кривую накопленного ARPU по дням.
Кривая накопленного ARPU
Добавив горизонтальной линией CPI, мы получим крайне полезный для понимания график. В точке пересечения двух линий мы получим день, начиная с которого доход от пользователя становится выше, чем затраты на его привлечение (привлечение пользователя становится эффективным). В рассматриваемом выше примере это 56-ой день жизни клиента. Решение этой задачи похоже на поиск точки безубыточности, однако надо помнить, что компания также несет и другие косвенные затраты, которые необходимо заложить, чтобы корректно посчитать точку безубыточности.
Рекомендации
Наилучшим сценарием взаимодействия с клиентами и высшей степенью награды для компании можно считать рекомендацию продуктов компании друзьям, родственникам, знакомым. С точки зрения метрик можно выделить: количество активированных приглашенных новых пользователей на одного клиента и NPS.
Количество активированных рекомендаций позволяет увеличить CAC / CPI. К примеру, мы привлекаем пользователя за $1 и хотим сохранить такую тенденцию. Мы разработали механику реферальных ссылок и выявили, что теперь после внедрения средний пользователь приглашает двух других. Получается, что в таком случае стоимость привлечения пользователя составит $1 / 3 = $0.33. Соответственно, мы можем позволить себе привлекать пользователей за $3, сохранив приемлемое для нас значение CAC.
NPS (Net Promote Score) — метрика, которая показывает уровень потребительской лояльности. Механика расчета подробна описана на Википедии, не будем на ней останавливаться. Скажем лишь о том, что рекомендуем регулярно замерять NPS, используя директ-маркетинговые каналы коммуникаций.
Иерархия метрик в организации
Мы достаточно подробно изучили важные метрики каждого этапа AARRR, осталось разобраться каким образом можно структурировать показатели, чтобы получить идеальный дашборд.
Для решения этой задачи имеет смысл декомпозировать цели компании и соответствующие им метрики на разные уровни. Зачастую каждый следующий уровень соответствует отделу компании и является KPI этого отдела. Упрощенно, мы можем представить главную верхнеуровневую цель компании — Прибыль и декомпозировать ее на составные части: Выручка, Расходы.
Иерархия метрик организации
Хороший пример — школа английского языка SkyEng, на видео можно ознакомиться с детально проработанной структурой метрик SkyEng.
Другой альтернативой может стать построение структуры дашборда на основании разобранного выше фреймворка AARRR. Схематично такой дашборд мог бы выглядеть таким образом:
Заключение
Сегодня мы разобрались с ключевыми маркетинговыми метриками, которые помогут отслеживать изменения на каждом этапе маркетинговой воронки, расскажут об эффективности каждого этапа и станут полезным инструментом деятельности маркетолога.
Разбавлю блог интересным отчетом, который в свое время был построен для компании Yota в ноябре 2011го года. Построить данный отчет нас вдохновила матрица BCG.
У нас было: один пакет Excel, 75 VBA макросов, ODBC подключение к Oracle, SQL-запросы к БД всех сортов и расцветок. На таком стеке и рассмотрим построение отчета, но в начале немного о самой идее отчета.
Матрица BCG — это матрица размером 2х2, на которой сегменты клиентов изображаются окружностями с центрами на пересечении координат, образуемых соответствующими темпами двух выбранных показателей.
Если упростить, то нам надо было поделить всех клиентов компании на 4 сегмента: ARPU выше среднего / ниже среднего, потребление трафика (основной услуги) выше среднего / ниже среднего. Таким образом получалось, что возникает 4 квадранта, в каждый из которых необходимо поместить пузырьковую диаграмму, где размер пузырька обозначает общее количество пользователей в сегменте. Дополнительно к этому добавляется еще один пузырек в каждом квадранте (более мелкий), который показывает отток в каждом сегменте (авторское усовершенствование).
Что хотелось получить на выходе?
График подобного вида:
Представление матрицы BCG на данных компании Yota
Постановка задачи более-менее ясна, перейдем к реализации отчёта.
Предположим, что мы уже собрали нужные данные (то есть научились определять средний ARPU и среднее потребление трафика, в данном посте не будем разбирать SQL-запрос), тогда первостепенная основная задача — понять как отобразить средствами Excel пузырьки в нужных местах.
Для этого на помощь приходит базовая пузырьковая диаграмма:
Вставка — Диаграмма — Пузырьковая
Идем в меню Выбор источника данных и оцениваем, что необходимо подготовить для построения диаграммы в нужном нам виде: координаты X, координаты Y, значения размеров пузырьков.
Отлично, выходит, если предположить, что наша диаграмма будет расположена в координатах по X от -1 до 1, а по Y от -1 до 1, то центр правого верхнего пузырька это точка (0.5; 0.5) на диаграмме. Аналогичным образом, расположим все остальные основные пузырьки.
Отдельно следует подумать о пузырьках типа Churn (для отображения оттока), они расположены правее и ниже основного пузырька и могут с ним пересекаться, поэтому правый верхний пузырек разместим в эмпирически полученных координатах (0.65; 0.35).
Таким образом, для четырех основных и четырех дополнительных пузырьков мы можем организовать данные в следующем виде:
Рассмотрим подробнее, как будем их использовать:
Итак, мы задаем по X — горизонтальные координаты центра наших пузырьков, которые лежат в ячейках A9:A12, по Y — вертикальные координаты центра наших пузырьков, которые лежат в ячейках B9:B12, а размеры пузырьков мы храним в ячейках E9:E12.
Далее, добавляем еще один ряд данных для Оттока и снова указываем все необходимые параметры.
Мы получим следующий график:
Дальше наводим красоту: меняем цвета, убираем оси и получаем красивый результат.
Добавив необходимые подписи данных, получим то, что требовалось в задаче.
Делитесь в комментариях — приходилось ли строить подобные графики, каким образом решали задачу?
В этой заметке разберём как правильно построить отчет по Retention с использованием Redash и языка SQL.
Для начала, в двух словах, что это за метрика Retention rate, почему она важна, какой бывает и каким образом считается.
Retention rate
Метрика Retention rate довольно широко распространена и особенно известна в мобильной индустрии, поскольку позволяет понять насколько хорошо продукт вовлекает пользователей в ежедневное использование. Вспомним (или узнаем) как рассчитывается Retention:
Retention дня X — это N% процентов пользователей, которые вернутся к продукту в день X. Иными словами, если в какой-то конкретный день (день 0) пришло 100 новых пользователей, а на 1-ый день вернулось 15, то Retention 1-го дня составит 15 / 100 = 15%.
Чаще всего выделяют Retention дней 1, 3, 7 и 30 как наиболее описательные метрики продукта, однако полезно в целом рассматривать кривую Retention и делать выводы исходя из нее.
Кривая retention
В конечном итоге нас интересует построение такой кривой, которая показывает удержание пользователей с 0-го дня до 30-го.
Кривая Retention rate c 0-го по 30-ый день
Rolling Retention (RR)
Кроме классического Retention rate выделяют также Rolling Retention (далее RR). При расчете Rolling Retention помимо дня X учитываются также все последующие дни. Таким образом RR 1-го дня — количество пользователей, которые вернулись в 1-ый и последующие дни.
Сравним Retention и Rolling Retention 10-го дня: Retention10 — количество пользователей, вернувшихся в 10-ый день / количество пользователей, установивших приложение 10 дней назад * 100%. Rolling Retention10 — количество пользователей, вернувшихся в 10-ый день или позже / количество пользователей, установивших приложение 10 дней назад * 100%.
Гранулярность (retention временных отрезков)
В некоторых отраслях и соответствующих задачах полезно понимать Retention конкретного дня (чаще всего в мобильной индустрии), в других случаях полезно понимать удержание пользователя на разных временных интервалах: например, недельные отрезки или месячные (часто полезно в e-commerce, ретейле).
Пример когорт по месяцам и соответствующий им месячный Retention
Как построить Retention отчет на языке SQL?
Выше мы разобрали как посчитать Retention в формулах. Теперь приложим это к языку SQL.
Допустим, что у нас есть две таблицы: user — храним данные об идентификаторах пользователей и мета-информацию, client_session — информация о посещениях пользователями мобильного приложения.
В запросе будут фигурировать только две эти таблицы, поэтому вы с легкостью сможете адаптировать запрос под себя. примечание: в рамках данного кода я использую Impala в качестве СУБД.
Собираем размер когорт
SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
ndv(user.id) AS users
FROM USER
WHERE from_unixtime(user.installed_at)>=date_add(now(), -60)
AND from_unixtime(user.installed_at)<=date_add(now(), -31)
GROUP BY 1
Разберем этот довольно несложный запрос: по каждому дню мы считаем количество уникальных пользователей для отрезка [60 дней назад; 31 день назад].
Чтобы не лезть в документацию: команда ndv() в Impala аналог команды count(distinct).
Считаем количество вернувшихся пользователей по каждой когорте
SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) AS date_diff,
ndv(user.id) AS ret_base
FROM USER
LEFT JOIN client_session cs ON cs.user_id=user.id
WHERE 1=1
AND datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) between 0 and 30
AND from_unixtime(user.installed_at)>=date_add(now(), -60)
AND from_unixtime(user.installed_at)<=date_add(now(), -31)
GROUP BY 1, 2
В этом запросе ключевая часть содержится в команде datediff: теперь мы считаем для каждой когорты и для каждого datediff количество уникальных пользователей все той же командой ndv() (фактически, количество пользователей которые вернулись в дни от 0-го до 30-го).
Отлично, теперь у нас есть размер когорт и количество вернувшихся пользователей
Собираем все вместе
SELECT reg.reg_date AS date_registration,
reg.users AS cohort_size,
cohort.date_diff AS day_difference,
cohort.ret_base AS retention_base,
cohort.ret_base/reg.users AS retention_rate
FROM
(SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
ndv(user.id) AS users
FROM USER
WHERE from_unixtime(user.installed_at)>=date_add(now(), -60)
AND from_unixtime(user.installed_at)<=date_add(now(), -31)
GROUP BY 1) reg
LEFT JOIN
(SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) AS date_diff,
ndv(user.id) AS ret_base
FROM USER
LEFT JOIN client_session cs ON cs.user_id=user.id
WHERE 1=1
AND datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) between 0 and 30
AND from_unixtime(user.installed_at)>=date_add(now(), -60)
AND from_unixtime(user.installed_at)<=date_add(now(), -31)
GROUP BY 1, 2) cohort ON reg.reg_date=cohort.reg_date
ORDER BY 1,3
Мы получили запрос, который для каждой когорты считает Retention, в итоге результат можно отобразить в таком виде:
Retention rate, посчитанный для каждой когорты пользователей
Построение единственной кривой Retention
Несколько модифицируем наш запрос и получим требуемые данные для построения одной кривой Retention:
SELECT
cohort.date_diff AS day_difference,
avg(reg.users) AS cohort_size,
avg(cohort.ret_base) AS retention_base,
avg(cohort.ret_base)/avg(reg.users)*100 AS retention_rate
FROM
(SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
ndv(user.id) AS users
FROM USER
WHERE from_unixtime(user.installed_at)>=date_add(now(), -60)
AND from_unixtime(user.installed_at)<=date_add(now(), -31)
GROUP BY 1) reg
LEFT JOIN
(SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) AS date_diff,
ndv(user.id) AS ret_base
FROM USER
LEFT JOIN client_session cs ON cs.user_id=user.id
WHERE 1=1
AND datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) between 0 and 30
AND from_unixtime(user.installed_at)>=date_add(now(), -60)
AND from_unixtime(user.installed_at)<=date_add(now(), -31)
GROUP BY 1,2) cohort ON reg.reg_date=cohort.reg_date
GROUP BY 1
ORDER BY 1
Теперь у нас для каждого дня посчитан средний по всем когортам Retention rate.
Продолжение предыдущей статьи о сборе данных с известного онлайн каталога товаров.
Если внимательно проанализировать поведение страницы с товарами, можно заметить, что товары подгружаются динамически, то есть при скролле страницы вниз мы получим новый набор товаров, и, тем самым, код из предыдущей статьи для данной задачи окажется бесполезным.
Динамическая подгрузка товаров на странице
Для таких случае в python также имеется решение — библиотека Selenium, она запускает движок браузера и эмулирует поведение человека.
В первой части скрипта мы соберем дерево категорий аналогично прошлой статье, но уже с использованием Selenium.
В этом сниппете мы как и раньше get-запросом с параметрами вызываем желаемую страницу браузера и скачиваем данные, затем у нас появляется объект класса bs, с которым мы проделываем аналогичные операции. Таким образом, мы получили словарь tree, в котором для каждой категории хранится URL страницы для данной категории, в дальнейшем этот словарь нам пригодится для перебора в цикле.
Приступим к сбору данных о товарах. Для этого импортируем библиотеку pandas и создадим новый dataframe с четырьмя колонками.
import pandas as pd
df = pd.DataFrame(columns=['SKU', 'Weight', 'Price','Category'])
Далее, воспользуемся нашим словарем tree и для каждой категории получим данные страницы. Ниже приведен код. Мы по-прежнему должны установить куки, которые установлены у пользователя, а также выполнить некоторые хитрые команды для работы движка браузеры, которые сэмулируют перемещение курсора вниз страницы.
for cat, link in tree.items():
browser.maximize_window()
browser.get('https://i****.ru'+link)
cookies_1= {'domain': '.i****.ru', 'expiry': 1962580137, 'httpOnly': False, 'name': '_i****_session_cross_domain', 'path': '/', 'secure': False, 'value': 'WWJFaU8wMTBMSE9uVlR2YnRLKzlvdHE3MVgyTjVlS1JKVm1qMjVNK2JSbEYxcVZNQk9OR3A4VU1LUzZwY1lCeVlTNDVsSkFmUFNSRWt3cXdUYytxQlhnYk5BbnVoZktTMUJLRWQyaWxFeXRsR1ZCVzVnSGJRU0tLVVR0MjRYR2hXbXpaZnRnYWRzV0VnbmpjdjA5T1RzZEFkallmMEVySVA3ZkV3cjU5dVVaZjBmajU5bDIxVkEwbUQvSUVyWGdqaTc5WEJyT2tvNTVsWWx1TEZhQXB1L3dKUXl5aWpOQllEV245VStIajFDdXphWFQxVGVpeGJDV3JseU9lbE1vQmxhRklLa3BsRm9XUkNTakIrWXlDc3I5ZjdZOGgwYmplMFpGRGRxKzg3QTJFSGpkNWh5RmdxZzhpTXVvTUV5SFZnM2dzNHVqWkJRaTlwdmhkclEyNVNDSHJsVkZzeVpBaGc1ZmQ0NlhlSG43YnVHRUVDL0ZmUHVIelNhRkRZSVFYLS05UkJqM24yM0d4bjFBRWFVQjlYSzJnPT0%3D--e17089851778bedd374f240c353f399027fe0fb1'}
cookies_2= {'domain': '.i****.ru', 'expiry': 1962580137, 'httpOnly': False, 'name': 'sa_current_city_coordinates_cross_domain', 'path': '/', 'secure': False, 'value': '%5B59.91815364%2C30.305578%5D'}
cookies_3= {'domain': '.i****.ru', 'expiry': 1962580137, 'httpOnly': False, 'name': 'sa_current_city_cross_domain', 'path': '/', 'secure': False, 'value': '%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3'}
browser.add_cookie(cookies_1)
browser.add_cookie(cookies_2)
browser.add_cookie(cookies_3)
browser.get('https://i****.ru'+link)
# Скрипт, который через каждые 3 секунды ищет конец страницы и выполняется пока не закончится получение свежих данных
lenOfPage = browser.execute_script("window.scrollTo(0, document.body.scrollHeight);var lenOfPage=document.body.scrollHeight;return lenOfPage;")
match=False
while(match==False):
lastCount = lenOfPage
time.sleep(3)
lenOfPage = browser.execute_script("window.scrollTo(0, document.body.scrollHeight);var lenOfPage=document.body.scrollHeight;return lenOfPage;")
if lastCount==lenOfPage:
match=True
Теперь мы дошли до конца страницы и можем собрать данные для работы библиотеки beautifulsoup.
# Собираем данные со страницы
source_data = browser.page_source
soup = bs(source_data)
skus=soup.find_all('div', {'class':['b-product-small-card']})
last_value=len(df)+1 if len(df)>0 else 0
for i,x in enumerate(skus):
df.loc[last_value+i]=[x.findNext('a').contents[0],\
x.findNext('div',{'class':'product-weight'}).contents[0],\
x.findNext('div',{'class':'g-cart-action small'})['data-price'],\
cat]
browser.close()
В приведенном выше фрагменте кода мы ищем все элементы <div>, у которых класс — b-product-small-card, а далее для каждого найденного товара собираем значения полей веса и цены.
Исходный код сайта продуктовой карточки
Ставим выполняться скрипт и пьем кофе. Вуа-ля, теперь у нас есть pandas dataframe с данными всех товаров:
DataFrame с товарами, собранными с сайта
Теперь, у нас есть отличные данные для обучения NLP модели — наименования товаров и их принадлежность к различным категориям.
Продолжим разбираться с Python и чековыми данными. У нас имеется информация о чековых записях, полученная с сайта налоговой, это хорошо. Теперь нужно научиться определять категорию, к которой относится товар. Мы будем это делать в автоматическом режиме, предварительно построив модель машинного обучения.
Ключевая «боль» / сложность — нам требуется так называемый сет для обучения (training set). Забегая вперед скажу, что успел протестировать такой сет для обучения на реальных данных из одной продуктовой сети, результаты, к сожалению, оказались не самые радужные, поэтому попробуем собрать другой честный сет для обучения, используя открытые источники в интернете.
Для работы возьмем популярный сайт по доставке еды из гипермаркетов. Сегодня будем использовать простую, удобную и функциональную библиотеку в Python для парсинга данных с .html страниц — Beatiful Soup.
Целевая страница, на которой интересующие нас данные имеет следующий вид.
Страница каталога сайта по доставке еды из гипермаркетов
Однако если мы впервые зайдем на сайт нас перебросит на главную, поскольку мы не выбрали ближайший гипермаркет.
Главная страница сайта по доставке еды из гипермаркетов
Проблема понятна, скорее всего, ее можно решить установлением cookies, подобных тем, что сохраняются локально у пользователя.
Теперь важно определить порядок действий и структуру сбора данных.
Основная задача:
Собрать наименования всех верхнеуровневых категорий и соответствующие им URL сайта
Используя полученный в пункте 1 список, собрать данные о товарах с каждой страницы категории.
Сегодня будем разбирать первую часть. Приступим, в теории нам должно хватить таких команд:
from bs4 import BeautifulSoup #импортируем модуль BeautifulSoup
import requests
r = requests.get('https://i****.ru/products?category_id=1-ovoschi-frukty-griby-yagody&from_category=true')
soup = BeautifulSoup(r.text)
Однако если мы посмотрим содержимое soup, то обнаружим, что мы получили исходный код главной страницы, а не той, что интересует нас. Главная страница не подходит для целей нашего анализа и сбора информации.
Получили исходный код главной страницы сайта
Поэтому воспользуемся методом Session библиотеки requests, которым можно передать cookies в качестве параметра. Итого наш код выглядит так:
import requests
s = requests.Session()
r = s.get('https://i****.ru/products?category_id=1-ovoschi-frukty-griby-yagody&from_category=true', \
cookies = {'_igooods_session_cross_domain':'WWJFaU8wMTBMSE9uVlR2YnRLKzlvdHE3MVgyTjVlS1JKVm1qMjVNK2JSbEYxcVZNQk9OR3A4VU1LUzZwY1lCeVlTNDVsSkFmUFNSRWt3cXdUYytxQlhnYk5BbnVoZktTMUJLRWQyaWxFeXRsR1ZCVzVnSGJRU0tLVVR0MjRYR2hXbXpaZnRnYWRzV0VnbmpjdjA5T1RzZEFkallmMEVySVA3ZkV3cjU5dVVaZjBmajU5bDIxVkEwbUQvSUVyWGdqaTc5WEJyT2tvNTVsWWx1TEZhQXB1L3dKUXl5aWpOQllEV245VStIajFDdXphWFQxVGVpeGJDV3JseU9lbE1vQmxhRklLa3BsRm9XUkNTakIrWXlDc3I5ZjdZOGgwYmplMFpGRGRxKzg3QTJFSGpkNWh5RmdxZzhpTXVvTUV5SFZnM2dzNHVqWkJRaTlwdmhkclEyNVNDSHJsVkZzeVpBaGc1ZmQ0NlhlSG43YnVHRUVDL0ZmUHVIelNhRkRZSVFYLS05UkJqM24yM0d4bjFBRWFVQjlYSzJnPT0%3D--e17089851778bedd374f240c353f399027fe0fb1', \
'sa_current_city_coordinates_cross_domain' : '%5B59.91815364%2C30.305578%5D', \
'sa_current_city_cross_domain' : '%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3',\
'lazy_loader_last_url' : '/products?category_id=1-ovoschi-frukty-griby-yagody&from_category=true'})
soup = BeautifulSoup(r.text)
Мы устанавливаем 4 cookies, которые эмулируют поведение человека и выбранный гипермаркет (их мы установили эмпирическим путем из cookies, установленных браузером, при выборе соответствующего гипермаркета):
Cookies, влияющие на отображение главной страницы сайта
Прекрасно, осталось лишь собрать нужные нам категории и ссылки на них. Для этого напишем следующий код:
categories=soup.find_all('div', {'class':['with-children']})
tree = {}
for x in categories:
tree[x.findNext('span').text]=x.findNext('a').get('href')
В этом сниппете мы как и раньше GET-запросом с параметрами (cookies) вызываем желаемую страницу браузера и скачиваем данные, затем у нас появляется объект класса BeautifulSoup.
Мы используем команду:
Что фактически означает: мы берем текст следующего, за найденным <div> элемента <span> и адрес ссылки, следующей за найденным <div>.
Именно это нам и требовалось получить. Таким образом мы получили словарь вида {Категория: URL сайта}:
Справочник категорий и соответствующих им URL
В следующей статье — продолжение о сборе информации по карточкам товаров каталога.
Временно отвлечемся от проекта по сбору чековых данных. О нескольких следующих шагах этого проекта поговорим чуть позже.
Сегодня обсудим новый сервис от Яндекса — DataLens (доступ к демо мне любезно предоставил мой большой друг Василий Озеров и команда Fevlake / Rebrain). Сервис находится в режиме Preview и по сути является облачным BI. Ключевая фишка сервиса в том, что он легко и удобно работает с кликхаусом (Yandex Clickhouse).
Подключение источников данных
Рассмотрим основные вещи: подключение источника данных и настройку датасета.
Выбор СУБД не велик, но некоторые основные вещи присутствуют. Для целей нашего тестирования возьмем MySQL.
Выбор источников данных DataLens
На основе созданного подключения предлагается создать датасет:
Интерфейс настройки датасета, определение измерений и метрик
На этом этапе определяется какие атрибуты таблицы станут измерениями, а какие метриками. Можно выбрать тип агрегации данных для метрик.
К сожалению, мне не удалось найти каким образом возможно вместо одной таблицы указать несколько связанных таблиц (например, присоединить справочник для измерений). Вероятно, на данном этапе разработчики предлагают решать данный вопрос созданием нужных view.
Визуализация данных
Что касается самого интерфейса для визуализации — все довольно легко и удобно. Напоминает облачную версию Tableau. А в сравнении с Redash, который чаще всего используется в связке с Clickhouse, возможности визаулизации — просто потрясают.
Чего стоят сводные таблицы, в которых можно использовать Measure Names в качестве названия столбцов:
Настройка сводных таблиц в DataLens
Разумеется, в DataLens от Яндекса есть возможность собрать и базовые графики:
Построение линейного графика в DataLens
Есть и диаграммы с областями:
Построение диаграммы с областями в DataLens
Однако мне не удалось обнаружить каким образом осуществляется группировка дат по месяцам / кварталам / неделям. Судя по примеру данных, доступному в пробной версии, разработчики пока решают этот вопрос созданием дополнительных атрибутов (DayMonth, DayWeek, etc).
Дашборды
Пока интерфейс создания блоков дашборда выглядит громоздко и не всегда понятны интерфейсные окна. Вот, например, окно позволяющее указать параметр:
Не совсем очевидное окно настройки параметров дашборда
Однако в галерее примеров имеются очень функциональные и удобные дашборды с селекторами, вкладками и параметрами:
Пример работающего дашборда с параметрами и вкладками в DataLens
Ждем исправления интерфейсных недочетов, улучшения DataLens и готовимся к использованию в паре с Clickhouse!
Update: к сожалению, информация в данном посте устарела. Рекомендуем изучить наш новый пост.
Недавно, покупая в очередной раз продукты в гипермаркете, вспомнил, что согласно ФЗ-54 любой оператор торговли, который пробивает кассовый чек, обязан отправлять данные чека в налоговую.
Чек из гипермаркета «Лента», QR-код, который нас интересует, обведен
Что это значит для нас, аналитиков данных? Что мы можем лучше узнать себя, свои потребности и получить интересные данные о собственных покупках.
Попробуем в рамках серии постов собрать небольшой прототип приложения, которое позволит строить динамику своих покупок. Итак, начнем с того, что в каждом чеке есть QR-code, если его распознать, то мы получим следующую строку:
t — timestamp, время, когда вы осуществили покупку s — сумма чека fn — кодовый номер fss, потребуется далее в запросе к API i — номер чека, он нам потребуется далее в запросе к API fp — параметр fiscalsign, потребуется далее в запросе к API
В рамках решения первого шага нашей задачи мы будем парсить данные чека и собирать их в pandas dataframe, используя модули Python.
Мы воспользуемся API, который отдает данные по чеку с сайта налоговой.
В начале получим аутентификационные данные:
import requests
your_phone = '+7XXXYYYZZZZ' #нужно указать ваш телефон, на него придет СМС с паролем
r = requests.post('https://proverkacheka.nalog.ru:9999/v1/mobile/users/signup', json = {"email":"email@email.com","name":"USERNAME","phone":your_phone})
В результате выполнения POST-запроса мы получим пароль в виде SMS на указанный мобильный телефон. Далее, мы будем использовать его в переменной pwd
Теперь распарсим нашу строку со значениями из QR-кода:
Будем использовать полученные переменные для извлечения данных.
В посте на Хабре довольно подробно изучены статусы ошибок при формировании запроса к API, не буду повторять эту информацию.
В начале необходимо проверить, что по данному чеку есть данные, формируем GET-запрос.
В запросе необходимо указать headers, хотя бы пустые. В моем случае GET-запрос возвращает ошибку 406, из чего я понимаю, что такой чек находится (почему GET-запрос возвращает 406 для меня загадка, буду рад подсказкам в комментариях). Если не указать сумму или дату, то GET-запрос вернет ошибку 400 — bad request.
Переходим к самому интересному, получаем данные чека: