Видите, что тут есть поправка на среднее значение ковариаты. Чисто интуитивно на больших данных мы можем делать поправку на среднее значение ковариаты метрики, но по факту это делать некорректно с точки зрения математики, т.к. истинного матожидания генеральной совокупности мы не знаем, мы оперируем значением выборки, про это кстати говорилось на одном из курсов по A/B (увидел в чатиках).
Отсюда вопрос, как можно корректно оценивать метрику CUPED? Относительно чего считать относительное MDE? При расчете MDE мы тоже используем стандартное нормальное распределение, хотя должны использовать по-хорошему t-распределение.
Тогда еще один вопрос, зачем проводить тесты, если можно взять просто значения средних и сравнивать? Можете запустить симуляцию и проверить. В некоторых компаниях есть ограничения к MDE (если у метрики он большой, значит выбираем другую метрику). К абсолютному MDE вопросов нет, мы не используем среднее, а дисперсии у метрик CUPED равны. Как раз это возникает еще из-за того, что мы берем значение ковариации и дисперсии выборочно. В реальном же мире мы не знаем истинного значения матожидания ковариаты, поэтому поправка, с точки зрения математики неверна.
import numpy as np
from scipy.stats import norm
def generate_data(sample_size, corr, mean=2000, sigma=300):
"""Генерируем коррелированные данные исходной метрики и ковариаты.
sample_size - размер выборки
corr - корреляция исходной метрики с ковариатой
mean - среднее значение исходной метрики
sigma - стандартное отклонение исходной метрики
Возвращает:
'metric' - значения исходной метрики,
'covariate' - значения ковариаты.
"""
np.random.seed(1337)
means = np.array([mean, mean])
cov = sigma ** 2 * np.array([[1, corr], [corr, 1]])
data = np.random.multivariate_normal(means, cov, sample_size).astype(int)
metric, covariate = data[:, 0], data[:, 1]
return metric, covariate
def get_theta_for_cuped(values, covariate):
'''Функция получает значение theta
values - значения
covariate - значение ковариаты
Возвращает:
theta - значение theta'''
import numpy as np
cov = np.cov(values, covariate)[0][1]
var = np.var(covariate)
theta = cov / var
return theta
def get_mde(values, alpha = 0.05, beta = 0.2):
'''На вход получает значения метрики
На выходе
mde_abs - абсолютный MDE
mde_rel - относительный MDE'''
std = np.std(values)
mu = np.mean(values)
sample_size = len(values)
f = norm.ppf(1 - alpha / 2) + norm.ppf(1 - beta)
mde_abs = 2 * std * f / np.sqrt(sample_size)
mde_rel = mde_abs / mu * 100
return mde_abs, mde_rel
corr = 0.6
values, covariate = generate_data(20000, corr)
print(f' Коэффициент корреляции - {np.corrcoef(values, covariate)[0][1]}')
theta = get_theta_for_cuped(values, covariate)
print('')
cuped_metric = values - theta * covariate
cuped_metric_with_mu = values - theta * (covariate - np.mean(covariate))
print(f' Стандартное отклонение метрики - {np.std(values)}')
print(f' Стандартное отклонение CUPED метрики - {np.std(cuped_metric)}')
print(f' Старндартное отклонение CUPED метрики с поправкой - {np.std(cuped_metric_with_mu)}')
print('')
print(f' Среднее метрики - {np.mean(values)}')
print(f' Стандартное отклонение CUPED метрики - {np.mean(cuped_metric)}')
print(f' Старндартное отклонение CUPED метрики с поправкой - {np.mean(cuped_metric_with_mu)}')
mde_standard_abs, mde_standard_rel = get_mde(values)
mde_cuped_abs, mde_cuped_rel = get_mde(cuped_metric)
mde_cuped_with_mu_abs, mde_cuped_with_mu_rel = get_mde(cuped_metric_with_mu)
print('')
print(f' Абслютный и относительный MDE метрики {float(mde_standard_abs), float(mde_standard_rel)}')
print(f' Абслютный и относительный MDE CUPED метрики {float(mde_cuped_abs), float(mde_cuped_rel)}')
print(f' Абслютный и относительный MDE CUPED метрики с поправкой {float(mde_cuped_with_mu_abs), float(mde_cuped_with_mu_rel)}')
А что думаете вы? Ставьте реакции, пишите комментарии!