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

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

Тут к вам обращается руководитель одного из отделов и говорит: «Мало сделок с рекламы!». Знакомая ситуация? Вы знаете, что автоматическое распределение обеспечивает примерно одинаковым количеством рекламных сделок все отделы продаж. И это действительно так: 817 сделок в ОП № 1 и 798 сделок в ОП № 2.


Но здесь вы решаете проверить качество квалификации лидов и замечаете, что во втором отделе квалят почти в два раза лучше чем в первом (CRQL≈38% в ОП № 2 по сравнению с CRQL≈19% в ОП № 1).
CRQL — конверсия в квалификацию лида (отношение квалифицированных лидов к общему числу лидов), далее в формулах будет представлена в абсолютном выражении, а не в процентном.
Казалось бы, теперь всё очевидно: в ОП № 1 просто плохо квалят. Если бы поддерживали CRQL на уровне ОП № 2, получили бы в два раза больше рекламных сделок. Так ведь?
Но тут вы осознаете, что ОП № 1 обработал лидов в 2 раза больше чем первый (4300 против 2100)! Да и РОП заверяет вас, что в ОП № 1 работают первоклассные специалисты, которые ничем не уступают ОП № 2. К тому же сбытовая территория и товарная матрица одинаковы, даже скрипты идентичны.

Возможно, оценка CRQL≈19% более точна на таком количестве наблюдений? Или качество лидов ухудшилось в какой‑то конкретный момент? А поскольку ОП № 1 обрабатывает их в большем количестве, его конверсия просела намного быстрее? Или, напротив, ОП № 1 не справляется с обработкой такого количества рекламных лидов, а ОП № 2 уделяет большее внимание каждому лиду?
Что делать в такой ситуации? Является ли разница в квалификации случайной или обусловлена системно низким качеством обработки лида в среднем? Как действовать в ситуации, когда у нас есть всего 2 цифры, 2 значения метрики CRQL — 19% и 38%? Что предложит нам для решения данной задачи статистика?
Прочитав статью, вы узнаете, как:
получить мощный аргумент для переговоров: «нет, вероятность, что конверсия вашего отдела продаж просела случайно — статистически ничтожна»;
получить вероятностные оценки метрик для медиапланирования;
прокачать «План/Факт», построив эталонное распределение.
Статистика и вывод критерия
Действительно ли кроме конверсий у нас ничего нет? У нас есть целый вектор данных!
А так ли у нас мало данных? Обозначим статусы лидов, которые не были квалифицированы (конвертированы в сделку) за 0, а «конвертированные» лиды за 1. И заменим их обозначения в исходных данных.
Тогда мы получим вектор лидов вида L = {0, 0, 1, 0, 1, …} для каждого ОП. Но чем нам полезна такая замена? На самом деле, мы работаем с событием (квалификация лида) с бинарным исходом, иначе говоря: 0 — лид сочли некачественным, 1 — качественным.
Что будет, если мы повторим эксперимент несколько раз? Возьмем три лида, посмотрим, сочли ли их квалифицированными, и составим вероятностное пространство для такого эксперимента:

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

Лида всего три, а это означает, что вероятность того, что из трех лидов два будут квалифицированными (при CRQL=19%), равна 3*0.19*0.19*(1–0.19) = 0.0877 = 8.77%. Эту комбинацию можно посчитать вручную, но что делать, если повторений эксперимента не три, а тысяча?
И здесь на помощь нам приходит комбинаторика. Количество способов получить k (квалифицированных лидов) из n (общего количества) можно найти по формуле:
где <число>! представляет собой факториал (произведение всех натуральных чисел от 1 до этого числа: n! = 1*2*3*4*…*(n-1)*n).
Биномиальное распределение
Если обобщить это рассуждение, то в нашей задаче о биномиальном эксперименте (повторении эксперимента с двумя исходами n раз) конверсия в квалификацию лида (она же вероятность успеха) = CRQL, а неуспеха = 1 — CRQL, где:
Если мы обрабатываем n лидов (проводим эксперимент n раз), то вероятность получить любое количество квалифицированных лидов k из этих n лидов равна:
Как работает эта формула?
Лид квалифицировали раз, значит, мы перемножаем его вероятность
раз, возводя
в степень
. Если лид был квалифицированным
раз, то неквалифицированным он оказался
раз, поэтому вероятность
возводим в степень
. Чтобы учесть все возможные комбинации такого количества
квалифицированных и
неквалифицированных лидов, домножаем результат на соответствующее число сочетаний.
Теперь легко можно как посчитать вероятность конкретного количества квалифицированных лидов при известном числе всех лидов и значении CRQL, так и построить для этих двух параметров полное распределение вероятности всех возможных вариантов числа квалифицированных лидов: от нуля (если все n лидов были неквалифицированными) до n (если все n лидов оказались квалифицированными).
Однако здесь важно проговорить условия, при которых можно утверждать, что наши лиды распределены биномиально:
мы рассматриваем конечное фиксированное число лидов n;
каждый лид представляет собой простой биномиальный эксперимент ровно с двумя исходами (квал / не квал);
попытки независимы между собой (если это не так — например, 2 лида оставил один и тот же клиент, мы всегда имеем возможность предварительно подготовить данные);
CRQL (вероятность успеха) одинакова для всех n лидов.
Чтобы работать с биномиальным распределением на Python, могут понадобиться два метода из пакета scipy.stats: binom.pmf и binom.cdf. Названия методов расшифровываются так:
pmf — probability mass function — функция вероятности в точке;
cdf — cumulative distribution function — кумулятивная функция распределения.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import binom
n = 4300
cr_ql = 0.19
mean = n * cr_ql
std = np.sqrt(n * cr_ql * (1 - cr_ql))
k_values = np.arange(0, 1000)
probs = binom.pmf(k_values, n, cr_ql)
plt.bar(k_values, probs)
plt.show()Как выглядят наши фактические распределения по отделам продаж:

Как мы видим, наиболее вероятные количества квалифицированных лидов, которые ОП-1 и ОП-2 могут получить после обработки всех лидов, достаточно близки, несмотря на то, что ОП-1 обработал в 2 раза больше первичных лидов.
Попробуем смоделировать ситуацию, при которой ОП-1 и ОП-2 обрабатывают одинаковое количество первичных лидов:

Здесь мы видим более выраженные различия, но по‑прежнему не можем сказать, насколько они статистически значимы. Чтобы понять это, обратимся к нормальному распределению.
Аппроксимация биномиального эксперимента
При большом количестве повторений простого биномиального эксперимента биномиальное распределение приближается к нормальному. Это доказывается исходя из локальной теоремы Муавра‑Лапласа:

От биномиального нормальное распределение принципиально отличается тем, что оно непрерывно и, соответственно, служит для описания распределения непрерывных случайных величин.
То есть для дискретного биномиального распределения квалифицированных лидов, заданного общим числом лидов n и конверсией в квал. лид (вероятностью успеха) CRQL, математическое ожидание (значение, к которому будет стремиться средний результат эксперимента, если повторять его много раз) равно n*CRQL, а дисперсия равна n*CRQL*(1-CRQL):
Давайте также посмотрим, как распределены средние значения (доли) конверсий в квал. лид, эта информация пригодится нам чуть позже.
Заметим, что средняя конверсия (доля) — это отношение квалифицированных лидов ко всем лидам, то есть:
Оценим параметры распределения:
Поскольку мы совершили линейное преобразование, средняя конверсия также распределена нормально:

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

Чтобы работать с нормальным распределением, могут понадобиться два метода из пакета scipy.stats: norm.pdf и norm.cdf. Названия методов расшифровываются так:
pdf — probability density function — плотность вероятности;
cdf — cumulative distribution function — кумулятивная функция распределения.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
n = 4300
cr_ql = 0.19
mean = n * cr_ql
std = np.sqrt(n * cr_ql * (1 - cr_ql))
x_cont = np.linspace(0, 1000, 500)
y_norm = norm.pdf(x_cont, mean, std)
_, ax = plt.subplots()
ax.plot(x_cont, y_norm, linestyle="--", alpha=0.9)
plt.show()
Обратите внимание, что для нормального распределения мы работаем с PDF, а для биномиального — с PMF. Они соотносятся следующим образом:

Описание критерия для сравнения конверсий
Вернемся к вопросу о значимости различий в конверсиях при обработке лидов в наших отделах продаж.
I. Предположим, что различий на самом деле нет, и в среднем качество обработки одинаково:
II. Тогда в противном случае:
III. Ранее мы поняли, что средние конверсии распределены так:
а их выборочные дисперсии:
IV. По свойству нормального распределения:
Но мы помним, что при верности нулевой гипотезы в среднем качество обработки лидов одинаково (), поэтому наш итоговый критерий:
То, что мы вывели, называется двухвыборочный Z‑критерий для пропорций. В нём:
и
— это средние значения наших векторов L, состоящих из 0 и 1;
и
— это оценки дисперсии среднего (конверсий в квал. лид) для отделов.
Минимальный размер эффекта
А какой минимальную разницу в конверсиях мы можем обнаружить? Здесь поможет формула MDE — это наименьшая разница между двумя конверсиями, которую тест может обнаружить при выбранном уровне ошибки первого рода (α), ошибки второго рода (β) и размерах выборки (n1 и n2):
Несколько слов об ошибках I и II рода:
ошибка I рода (α) — это отклонение предположения о том, что различий в качестве обработки лидов между ОП нет, когда качество на самом деле одинаково;
ошибка II рода (β) — это ситуация, при который мы не найдем различий в обработке, хотя на самом деле они существуют.
Возьмём некоторый индустриальный стандарт в IT и скажем, что α=0.05, β=0.2.
import scipy.stats as stats
import numpy as np
def get_mde(n_1, n_2, cr_1, cr_2, alpha, beta) -> np.float64:
"""
Рассчитывает MDE
"""
var_mean_sample = lambda cr, n: cr * (1 - cr) / n
z_a = stats.norm.ppf(1 - alpha / 2)
z_b = stats.norm.ppf(1 - beta)
var_1 = var_mean_sample(cr_1, n_1)
var_2 = var_mean_sample(cr_2, n_2)
return np.sqrt(np.pow((z_a + z_b), 2) * (var_1 + var_2))
get_mde(
n_1=4300,
n_2=2100,
cr_1=0.19,
cr_2=0.38,
alpha=0.05,
beta=0.20
)
# np.float64(0.034080587386566004)То есть мы можем статистически значимо обнаружить разницу в качестве обработки не менее 3.41% — этого вполне достаточно в нашем случае!
Практические сценарии использования
Бизнес‑сценарий № 1. Вычисляем значимость различий в качестве обработки
Теперь, используя выведенный критерий, мы можем ответить на вопрос, значимы ли различия в обработке лидов? Обратимся к коду:
import numpy as np
import scipy.stats as stats
def z_test_proportions(cr_1, cr_2, n_1, n_2):
"""
Двухвыборочный Z-тест для пропорций
"""
var_mean_sample = lambda cr, n: cr * (1 - cr) / n
var_1 = var_mean_sample(cr_1, n_1)
var_2 = var_mean_sample(cr_2, n_2)
# Стандартная ошибка разности
se = np.sqrt(var_1 + var_2)
if se == 0:
return 0, 1.0
# Z-статистика
z_stat = (cr_1 - cr_2) / se
# p-value через нормальное распределение
p_value = 2 * stats.norm.sf(np.abs(z_stat))
return z_stat, p_value
z_test_proportions(
n_1=4300,
n_2=2100,
cr_1=0.19,
cr_2=0.38
)
# (np.float64(-15.618897215700224), np.float64(5.41338226549789e-55))Мы получили . Что это означает? Значение критерия — это количество стандартных ошибок, на которое отклоняется наблюдаемая оценка от нуля. В нашем случае ~15 стандартных ошибок — это экстремальное отклонение (большинство отклонений должны укладываться в интервал ±3). Вероятность получить такое отклонение, если различий в качестве квалификации лидов нет, равна p‑value (то есть
).
Интерпретация p‑value — если верно предположение о том, что в среднем качество обработки в отделах одинаково, то вероятность получить такие или еще более выраженные различия (как в большую, так и в меньшую сторону) равняется , то есть исключительно мала.
Важно, p‑value это:
не вероятность ошибки;
не вероятность получить такие же результаты при воспроизведении на новых данных;
не вероятность, что верна нулевая гипотеза;
не вероятность, что верна альтернативная гипотеза.
Вывод: мы сильно уверены, что наблюдаемая разница в качестве обработки не случайна, а обусловлена именно тем, как организован процесс квалификации лидов в отделах. Отделу продаж № 1 стоит задуматься и перенять опыт коллег у второго отдела, прежде чем просить большее количество рекламных лидов.
Подобный эксперимент применим ко многим ситуациям, когда мы работаем с бинарным исходом: успех сделки, CTR, конверсия сайта.
Дисклеймер: при прочих равных эффект может быть обусловлен многими факторами: сезонностью, товарной матрицей, региональными сбытовыми особенностями. Если мы хотим сравнить качество обработки, важно убедиться, что эти условия действительно идентичны для обоих отделов продаж. В нашем примере эти условия были соблюдены, поэтому мы можем формулировать вывод таким образом.
В противном же случае вы легко можете столкнуться с возражениями: «Они в Сочи, а мы в Норильске. У них ещё разгар сезона, а у нас заморозки. Это влияет на сделку.», «Они продают трубы, а мы трубопроводную арматуру.» или «Мы работаем с тендерами, а они продают с колёс.», — и эти возражения будут справедливы.
Бизнес‑сценарий № 2. Применение в медиапланировании
Зная среднее качество, с которым отдел / филиал обрабатывает лиды, можно планировать количество квал. лидов с опорой на вероятностное поле благодаря возможностям нормальной аппроксимации, рассмотренной далее.
Например:
(1) Вероятность, что при заданном качестве фактическое количество квал. лидов у ОП-2 будет от 800 до 820:
from scipy.stats import norm
mu = 2100 * 0.38
var = 2100 * 0.38 * (1 - 0.38)
dist = norm(loc=mu, scale=np.sqrt(var))
a = 800
b = 820
dist.cdf(b) - dist.cdf(a)
# np.float64(0.30286197463409636)
# Вероятность 30.29%
(2) Вероятность, что при заданном качестве фактическое количество квал. лидов у ОП-2 будет более 800:
from scipy.stats import norm
mu = 2100 * 0.38
var = 2100 * 0.38 * (1 - 0.38)
dist = norm(loc=mu, scale=np.sqrt(var))
a = 800
1 - dist.cdf(a)
# np.float64(0.4641773347903908)
# Вероятность 46.42%
(3) Вероятность, что при заданном качестве фактическое количество квал. лидов у ОП-2 будет менее 750:
from scipy.stats import norm
mu = 2100 * 0.38
var = 2100 * 0.38 * (1 - 0.38)
dist = norm(loc=mu, scale=np.sqrt(var))
a = 750
dist.cdf(a)
# np.float64(0.015465363412816917)
# Вероятность 1.55%
Представим, что вы планируете в медиаплане 2100 первичных лидов на период, но отчитываетесь по рекламным сделкам (квалифицированным лидам). Это шаг воронки, на который вы непосредственно не влияете, поскольку квалификация — ответственность менеджера отдела продаж. Как оценить вероятность, что в результате вы получите не менее 800 целевых действий и план будет успешно выполнен? Обращаемся к коду в блоке (2) и приходим к выводу: вероятность такого события 46.42%.
Другой пример: рекламные кампании в Директе обучаются на офлайн‑данных из CRM, цель «CRM: Заказ создан» прогружается только когда лид является квалифицированным. Ретроспективно мы знаем, что рекламная кампания не обучатся, если будет менее 750 конверсий (это условный пример, обычно иной порядок цифр). Обратимся к коду в блоке (3) и приходим к выводу: вероятность, что кампании не хватит данных для обучения ~1.55%.
Более того, если смоделировать распределения для каждого шага воронки, то мы можем cпрогнозировать значение почти для любой метрики с помощью метода Монте‑Карло.
Бизнес‑сценарий № 3. Управление эталонными значениями
Данный пример больше подходит для отдела продаж, хотя и для маркетинговых кейсов такая логика отлично применима. Например, вы знаете фактическую конверсию в квалификацию лида и планируете её увеличить. Однако период ещё не закончен, фактически конверсия не дотягивает до целевых показателей, как оценивать прогресс?
Можно построить «целевое» эталонное распределение и сравнивать фактический результат с помощью нашего Z‑критерия (аналогично тому, как мы делали выше), ориентируясь на p‑value и оценивая, насколько вероятны такие или еще более выраженные различия, когда целевое качество все‑таки достигается.
Представим ситуацию: эталонная конверсия, к которой мы стремимся, — 41% (первичных лидов по медиаплану — 1200). Уже прошло три недели из четырех, получено 900 первичных лидов, но конверсия пока не дотягивает — 38%. Есть ли возможность достигнуть целевого значения конверсии, если обработка останется на прежнем уровне? Возможно, она недотягивает статистически случайно?
Обратимся к коду:
# текущее распределение
n, cr = 900, 0.38
n_etalon, cr_etalon = 1200, 0.41
z_test_proportions(
n_1=n,
n_2=n_etalon,
cr_1=cr,
cr_2=cr_etalon
)
(np.float64(-1.393674853237643), np.float64(0.16341581325817744))
# p-value=0.16 > 0.05 => приближаемся к цели
Мы видим, что p‑value=0.16 > 0.05. Получается, если текущая обработка лидов и правда соответствует той, которой мы стремимся (то есть наблюдаемое «недотянутое» значение конверсии обусловлено случайностью), то вероятность получить такие или еще более выраженные различия равняется 16%.
Вывод: это достаточно много, чтобы считать такой сценарий несбыточным или нереалистичным, то есть план конверсии может быть выполнен при прочих равных.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.