Всем привет! Меня зовут Михаил, я продуктовый аналитик в команде Foodtech мобильного приложения Магнит. В своей прошлой статье я рассказывал о применении алгоритмов PrefixSpan и Seq2Pat для анализа путей пользователей, и сегодня продолжу тему изучения передвижения пользователей внутри приложения. На этот раз речь пойдёт о методе, который позволяет оценить влияние каждого сегмента аудитории на изменение метрики всей пользовательской базы.
Введение
Бывало у вас такое, что по какой-то необъяснимой причине конверсия из шага А в шаг Б резко снижается, и надо понять, в чём причина? Проще всего решить эту задачу поиском той группы пользователей, чья конверсия снизилась сильнее всего. Правда, при такой постановке задачи мы, скорее всего, сделаем ошибочный вывод. Продемонстрирую на примере. Представим, что к нам приходит руководитель и говорит, что резко просела некоторая конверсия. Он пытается понять, какая группа пользователей сильнее всего её «просадила». Мы выгружаем данные по этим группам и видим:
| Группа | Количество пользователей | Конверсия до | Конверсия после | Дельта | 
| X | 150 | 16 % | 10 % | -6 % | 
| Y | 2500 | 16 % | 15 % | -1 % | 
| Z | 2800 | 12 % | 11 % | -1 % | 
Если опираться только на колонку «Дельта», то можно сделать вывод, что всему виной группа пользователей X. Но учитывая количество пользователей в этой группе, возникают сомнения, что снижение их конверсии могло существенным образом сказаться на конверсии всего продукта. Хорошо, тогда посмотрим только группы Y и Z — там пользователей больше. Но вот проблема: у них дельты одинаковые. И как понять, на что обращать внимание в таком случае? Смотреть только на количество пользователей или также учитывать конверсию до? Возможно, нужно каким-то образом учесть долю группы от общего количества пользователей?
Описание методологии
Для решения этой задачи нам понадобится учесть и изменение конверсии, и изменение долей групп. Математическая формула будет выглядеть так:

где:
- — количество конверсий; 
- — количество пользователей; 
- — изучаемый период; 
- — период до; 
- — сегмент. 
Выглядит жутко, но, поверьте, суть метода достаточно проста. В формуле всего два слагаемых:
- слева — изменение метрики, умноженное на долю сегмента в актуальном периоде; 
- справа — изменение доли сегмента, умноженное на значение метрики в предыдущем периоде. 
Проще говоря, слева мы наблюдаем изолированный эффект от изменения метрики, а справа — изолированный эффект от изменения доли сегмента. Сложив эти два эффекта, мы получим суммарный вклад сегмента в изменение метрики. Если мы сложим все эти вклады, то получим общее изменение метрики.
Практическая реализация
Попробуем протестировать метод на более реалистичном примере. В нашем приложении мы оцениваем перемещения пользователей между ключевыми этапа воронки с помощью конверсии, и бывает так, что в какой-то день конверсия сильно падает или, наоборот, по необъяснимой причине растёт. Для нашего примера используем конверсию с главной страницы на страницу каталога доставки (назовем её «Launch2Delivery»). Итак, представим, что 2 июля 2025 года Launch2Delivery выросла относительно 1 июля с 15 % до 20 %. Исторические данные указывают на то, что это не случайность (обычно Launch2Delivery меняется в пределах 1 процентного пункта), поэтому списать это на стандартное отклонение не выйдет. Сгенерируем датафрейм:
import pandas as pd
import numpy as np
import os
import random
### Создаем датафрейм с данными о периоде до и во время аномалии
os = ['ios', 'android']
user_type = ['new', 'old']
city = ['Moscow', 'Krasnodar', 'Saint-Petersburg', 'Ekaterinburg']
install_source = ['organic', 'paid']
s_pre = np.random.binomial(1, 0.15, 10000)
s_cur = np.random.binomial(1, 0.20, 10000)
df_pre = pd.DataFrame(data={
    'os': [random.choice(os) for i in range(10000)],
    'user_type': [random.choice(user_type) for i in range(10000)],
    'city': [random.choice(city) for i in range(10000)],
    'install_source': [random.choice(install_source) for i in range(10000)],
    'conv_pre': s_pre})
df_cur = pd.DataFrame(data={
    'os': [random.choice(os) for i in range(10000)],
    'user_type': [random.choice(user_type) for i in range(10000)],
    'city': [random.choice(city) for i in range(10000)],
    'install_source': [random.choice(install_source) for i in range(10000)],
    'conv_cur': s_cur})В скрипте указано четыре характеристики пользователей: операционная система, тип пользователя, город и источник установки. На практике таких характеристик может быть сколько угодно, и мы всё-равно сможем определить вклад каждой из них в изменение метрики.
Теперь, для удобства, сгруппируем наши данные по сегментам и посчитаем в каждом общее количество пользователей (тех, кто открыл главную), общее количество сконвертировавшихся пользователей (перешли с главной страницы в каталог доставки) и конверсию сегмента, а затем соединим два датафрейма:
### Сгруппируем данные и соединим датафрейм
df_pre_gr = df_pre.groupby(['os','user_type','city','install_source'], as_index=False).agg(users_pre=('conv_pre', 'count'), converted_pre=('conv_pre', 'sum'), conversion_pre=('conv_pre', 'mean'))
df_cur_gr = df_cur.groupby(['os','user_type','city','install_source'], as_index=False).agg(users_cur=('conv_cur', 'count'), converted_cur=('conv_cur', 'sum'), conversion_cur=('conv_cur', 'mean'))
df = pd.merge(df_cur_gr, df_pre_gr, on=['os','user_type','city','install_source'], how='left')Теперь, опираясь на формулу из начала статьи, посчитаем изолированный эффект от изменения метрики (назовём его contrib_conv_change) и изолированный эффект от изменения доли сегмента (назовём его contrib_share_change), а также сразу сложим их в колонке total_contribution:
df['pre_share'] = df['users_pre'] / df['users_pre'].sum()
df['cur_share'] = df['users_cur'] / df['users_cur'].sum()
df['contrib_conv_change'] = (df['conversion_cur'] - df['conversion_pre']) * df['cur_share']
df['contrib_share_change'] = (df['cur_share'] - df['pre_share']) * df['conversion_pre']
df['total_contribution'] = df['contrib_share_change'] + df['contrib_conv_change']На выходе мы получаем таблицу, в которой видим вклад каждого конкретного сегмента в общее изменение конверсии. С её помощью, мы можем однозначно указать, какой именно сегмент пользователей в большей или меньшей степени причастен к изменению метрики. Это значительно упростит понимание причин тех или иных изменений и, как следствие, ускорит ответ на вопросы бизнес-заказчиков. В нашем примере итоговая таблица будет выглядеть так:
| os | user_type | city | install_source | total_contribution | 
| ios | old | Krasnodar | paid | 0,0043 | 
| android | new | Moscow | organic | 0,0039 | 
| android | new | Saint-Petersburg | paid | 0,0037 | 
| ios | old | Moscow | paid | 0,0037 | 
| android | old | Moscow | paid | 0,0028 | 
| … | … | … | … | … | 
| ios | new | Ekaterinburg | paid | 0,0004 | 
| android | old | Krasnodar | paid | 0,0003 | 
| ios | old | Saint-Petersburg | organic | 0,0002 | 
| ios | new | Ekaterinburg | organic | 0,0002 | 
| ios | new | Saint-Petersburg | paid | 1E-04 | 
Здесь мы видим, что сегмент старых пользователей из Краснодара, которые скачали приложение после взаимодействия с рекламой, внёс наибольший вклад в рост метрики. В ряде случаев этого уже будет достаточно для понимания причин (к примеру, если мы знаем, что команда маркетинга в эту дата раскатила на сегмент дополнительные скидки или купоны), но мы можем немного детализировать наш анализ. Удобство работы с вкладами заключается в том, что мы можем складывать их внутри нужных сегментов и делать более общие выводы. К примеру, можно сгруппировать данные по городам и понять, есть ли разница между ними:
| os | user_type | 
| Ekaterinburg | 0,0087 | 
| Krasnodar | 0,0131 | 
| Moscow | 0,0206 | 
| Saint-Petersburg | 0,0125 | 
Или посмотреть на различия между типами пользователей:
| os | user_type | 
| new | 0,0265 | 
| old | 0,0284 | 
И даже если мы не сможем сразу сделать вывод о причинах изменения метрики, то поймём, в какую сторону имеет смысл копать. В примере мы не видим существенных различий между старыми и новыми пользователями, но видим достаточно значимые изменения между городами. Изучив особенности различающихся городов (погода, начало и конец рекламных кампаний, запуск новых форматов, проведение A/Б-тестов), мы сможем предоставить бизнесу конкретные причины изменений.
Как это используется в Magnit Tech?
В нашей команде мы уже пошли немного дальше, начав разработку полноценного RCA-сервиса (Root Cause Analysis) на основе этой методологии. Как было описано выше, формула не всегда даёт исчерпывающий ответ на вопрос, почему именно изменилась метрика. Она может указать только на сегмент, который повлиял на это изменение. Поэтому мы добавили к анализу сегментов коррелирующие метрики, а также информацию о раскатке новых версий, проведении A/Б-тестов и запуске рекламы. В будущем эта система станет основой для системы мониторинга аномалий и значительно ускорит реагирование на них. Если вам интересно принять участие в разработке подобных систем, присоединяйтесь к команде Magnit Tech.
 
          