Однажды мы решили посмотреть, какие сезонные интересы есть у пользователей 2ГИС в разных городах. Всплески интереса к цветам, новогодним подаркам и шинам — вполне ожидаемы. Мы решили ими не ограничиваться и пойти дальше, проверив все сферы деятельности во всех 113 городах присутствия.
В этой статье я расскажу, как мы искали сезонности и какие особенности поведения пользователей в них обнаружили.
Зачем вообще нужно измерять сезонность?
Потребности пользователей 2ГИС меняются в течение года: товары народного потребления, сферы услуг, строительство, государственные службы. Знания о сезонностях полезны по нескольким причинам:
- Мы начинаем больше понимать о ценностях и интересах наших пользователей в данный момент и в ближайшем будущем.
- Мы можем «предугадывать» запрос пользователя.
- Менеджеры по продажам концентрируются на актуальных для пользователей сферах деятельности.
Виды трафика
Прежде чем рассказывать о том, как мы обрабатываем трафик, стоит уточнить, что мы его делим на несколько видов.
Рекавери — вид трафика, при котором пользователь точно знает компанию, в которую хочет обратиться. Ему надо уточнить адрес, расписание работы или найти вход. В этом случае поиск происходит по названию фирмы, номеру телефона и прочим атрибутам самой компании.
Дискавери-трафик — это когда пользователь формулирует запрос в более общих терминах: «мягкая мебель», «поесть на Ленина», «бани». То есть пользователь исследует варианты и предложения рынка, зачастую обзванивая организации или заходя на их сайты.
Гео-трафик возникает в том случае, когда пользователь работает с картой. Например, ищет ближайшие к дому аптеки или СТО.
Все пользовательские запросы и все последующие за ними действия размечаются по трафику. Для поиска сезонностей в анализ взяли дискавери + гео-трафик. Так как они, во-первых, соответствуют проявлению пользовательских интересов, во-вторых, ими можно управлять. Управлять рекавери-трафиком нельзя.
Про тренды
График 1: Вы видите сезонность?
Перед тем, как приступить к поиску сезонности, нужно учесть изменения объёма трафика на разных типах устройств. Мы учитывали, что аудитории WinPhone и ПК-версий непрерывно падают. Тогда как онлайн, Android, iOS — перманентно растут.
Проверка гипотез о наличии тренда
Критерии определения сезонности разработаны для стационарных рядов. Нужно проверить гипотезу о том, что ряд содержит тренд. Будем рассматривать временной ряд как случайный процесс. Тогда элементы ряда представляют собой реализации некоторой случайной величины.
Можем проверить гипотезу о том, что все выборочные значения принадлежат к одной генеральной совокупности со средним m. Тогда основная гипотеза имеет вид:
против конкурирующей гипотезы о наличии тренда
$inline$H_1: ∣m_{i+1}−m_i∣ > 0, i=1,2,...,N−1$inline$
где N — это количество элементов в ряде.
Для того, чтобы проверить гипотезу, нужно воспользоваться одним из критериев значимости тренда. На основе проведённых исследований выбрали критерий инверсий.
Если гипотеза не отвергается, то необходимо убрать тренд из данных. Предполагаем, что в наших данных может быть только линейный тренд. Эх, был бы он экспоненциальный! Также будем предполагать, что у нас может быть не более одной точки перегиба.
График 2: Временной ряд (из графика 1) и его тренд
Про грабли и открытия
Мы наметили границы исследования и взялись за проверку гипотез. Конечно же, без граблей и открытий не обошлось: так как задача не ограничивалась популярными услугами в городах-миллионниках, мы собрали список частных случаев, на которые стоит обращать внимание.
- Пропуски в данных. В узких сферах деятельности в некоторые даты может не быть данных. Особенно кейс актуален для маленьких городов. Эту особенность нужно учитывать для корректного построения регрессии.
- В случае обнаружения точки перегиба — учитывайте её близость к началу или концу ряда. Возможна неправильная интерпретация поведения ряда в краевых условиях. Например, на графике 2 первые 120 точек, казалось бы, указывают на кусочно-линейный рост. Но, на самом деле, это проявила себя сезонность, которую мы увидим позже.
- Выбор правильной точки отсчёта. Чтобы получить корректные коэффициенты, нужно использовать ряд, начиная с первой значимой точки, и запоминать координату X этой точки (дату появления данных в ряде). Тренд будет строится в системе координат с 0 в этой точке. Этот кейс актуален для сравнения сфер между собой. Например, в 2ГИС города запускались не одномоментно и, соответственно, начинали отправлять статистику в разное время. То же самое справедливо для появления новых бизнесов, таких как, например, барбершопы.
- Пожалуй, самое сложное — это найти компромисс между уровнем детализации и достаточностью объёма данных. Мы остановились на тройке измерений: город, сфера деятельности, платформа пользовательского устройства.
График 3: Ряд без тренда
Поиск сезонностей
После вычитания из временных рядов соответствующих им трендов можно переходить к задаче поиска сезонностей. Она состоит из двух подзадач:
- Обнаружения самого факта наличия сезонности ряда.
- Определения высокого и низкого сезонов — именно это имеет практическую ценность.
Обнаружение корреляций
Готовые функции поиска корреляций есть как в R, так и в Python. Мы использовали корреляцию Пирсона. При работе с векторами пользовательских интересов надо иметь в виду следующее:
- После вычитания тренда из исходного ряда могут получиться отрицательные значения. Это нормально на данном этапе.
- Для нашей задачи достаточно проверки корреляции временных рядов в 365 дней. Да, влияние високосного года незначительно и мы его в расчёт не берём.
- Для поиска сезонности необходимо наличие данных как минимум за два полных периода. В наших расчётах использовались данные за четыре периода.
Ищем корреляцию двух векторов: X: [0; N-365], Y: [366; N]. Где N — длина ряда.
График 4: Обнаружение корреляции
Получаем пользу
Сам факт наличия сезонности не несет в себе практической ценности. Надо понимать, какое внимание к сфере деятельности будет в следующем месяце: повышенное, пониженное или обычное.
В качестве конечного результата была выбрана мультипликативная шкала. Где единица — это «нормальный» уровень пользовательского интереса к сфере деятельности. Значение отличное от единицы характеризует кратное увеличение или спад интереса.
В нашем случае [пока] достаточно временного масштаба продолжительностью в месяц. Для определения уровня 1 использовали медиану помесячного внимания пользователей. Далее вычисляли кратное отклонение от этой медианы.
График 5: Годовая сезонность
Пора раскрыть тайну, какие данные показывают графики статьи. На этом и всех предыдущих графиках — клики в музеи Санкт-Петербурга. Как видно из последнего графика, музеи популярны в январские праздники, когда много выходных, и летом.
А что если ...
… взять и скормить алгоритму не интересы пользователей, а, например, продажи?
Алгоритм действий такой же:
- Ищем тренды. Аудитория городов растёт, и вместе с ней растёт число рекламодателей. Надо вычитать тренд, связанный с ростом популярности 2ГИС как рекламной площадки, для получения отраслевых всплесков и спадов.
- Находим корреляции продаж из года в год, а затем высокий и низкий сезоны.
Понадобилось внести ряд коррективов
Реклама в 2ГИС продаётся помесячно, поэтому за масштаб взяли месяц, но пользовательскую сезонность анализировали с точностью до дня. Для обратной совместимости адаптировали алгоритмы, чтобы они работали с произвольными рядами, где по оси X — порядковый номер точки, а по оси Y некое value (на этом уровне семантика value не имеет значения).
Точка отсчёта ряда продаж (начало координат), как правило, не совпадает с точкой отсчёта пользовательской сезонности. Ведь сначала город набирает аудиторию, и лишь затем появляется реклама. На этом этапе не стоит совмещать результаты двух сезонностей.
Так как продажи помесячные, то и точек в рядах у нас существенно меньше. В этом случае корреляцию надо считать в 12 точек вместо 365.
График 6: Сезонность продаж
В качестве завершающего этапа мы решили наложить сезонность продаж на пользовательскую сезонность. Теперь видно, где мы продаём рекламу позже, чем надо, и отстаём от пользовательского спроса.
Так, например, пользователи проявляют интерес по закупу бетона в Нижнем Новгороде в период с апреля по октябрь. Тогда как компании занимаются продвижением всего лишь с мая по сентябрь.
График 7: Пересечение пользовательской сезонности и сезонности продаж (зеленый — пользовательская, голубой — продажи, красный — совпадение)
На чём делали
Всё выше описанное реализовано в MS SQL Server 2016. Для поиска линейной регрессии и корреляции используется R, который входит в состав сервера, начиная с версии 2016. И поскольку у нас Data Warehouse и аналитика пользовательской статистики уже на SQL Server, оказалось очень удобным использовать R для математических расчётов.
Пример использования R из TSQL:
INSERT INTO #tmp
EXEC sp_execute_external_script
@language = N'R',
@script = @R,
@input_data_1 = N'SELECT DataId, Number, Value FROM #data ORDER BY 1, 2'
Где:
- переменная R содержит непосредственно R-код;
- language = N'R' — указывает на то, что скрипт, переданный в переменной R, содержит код на языке R. В SQL Server 2017 помимо R можно использовать language = N'Python'. Тогда, соответственно, в параметр script необходимо передать код на Python.
- input_data_1 — содержит SQL-запрос, к результатам которого внутри R-кода можно обращаться как к InputDataSet;
- Результатом выполнения процедуры будет рекордсет OutputDataSet, сформированный как OutputDataSet < — calcAllTrend(data = InputDataSet);
- Обязательно указать формат результирующего рекордсета: количество и типы столбцов. В данном случае формат OutputDataSet определяется таблицей #tmp, куда происходит запись результатов. Либо можно воспользоваться WITH RESULT SETS для описания результирующего рекордсета.
В нашем случае оказалось, что существенную часть времени выполнения sp_execute_external_script занимает непосредственно обращение к R Services. Сам R-код отрабатывал быстро.
Напомню, мы хотели посчитать тренды и сезонности для всех городов покрытия 2ГИС и всех сфер деятельности. Поэтому решили передавать в InputDataSet не один ряд (Number, Value), а сразу несколько, сгруппированных по DataId. А цикл по DataId организовали внутри R. Тем самым существенно сэкономили на вызове службы R Services.
Факты из жизни
Вы дочитали статью до конца, это похвально. Чтобы развлечь вас перед итогами, делимся интересными фактами, выявленными в ходе анализа сезонности.
Факт №1
Автомобилисты знают из личного опыта, что сезон поиска шин и шиномонтажа — весна и осень. Но, если быть точным, сезон в разных городах может сильно отличаться. Поэтому при формировании подборок и дашбордов (стартовых экранов в продуктах) важно учитывать сезонность в каждом конкретном городе.
График 8: Сезонности шин в Краснодаре, Новосибирске и Норильске
Факт №2
Сезонности могут отличаться даже внутри одной сферы деятельности — на разных типах устройств. Например, «новогодние подарки» на большом экране начинают искать в октябре. Тогда как в мобильных версиях поиск стартует на месяц позже и пик приходится ближе к Новому году.
График 9: Поиск новогодних подарков в онлайне и мобильной версии 2ГИС
Факт №3
В процессе работы над сезонностями обнаружили сферу деятельности, интерес к которой проявляется раз в три месяца. Причём это справедливо для разных городов России.
Попробуйте угадать, что это за сфера деятельности.
3 месяца = квартал. Обратите внимание на апрель…
График 10
Ещё раз о главном
Собираетесь искать корреляции в данных? Не забудьте эти данные подготовить: обработать пропуски, очистить от трендов и выбросов. Имейте в виду, что подготовка займет бОльшую часть времени.
На этапе работы с рядами максимально абстрагируйтесь от семантики: работайте с value и порядковым номером точки, тогда код будет проще переиспользовать. Очень сильно поможет визуализация на каждом этапе обработки.
PS: задача поиска трендов и сезонностей делалась в рамках другой задачи — прогнозирования изменения пользовательского внимания после приобретения рекламы в 2ГИС. Готов рассказать, как мы делали прогноз, если вам будет интересно.
Автор: Shirshakov