Про продуктовую аналитику в IT, мысли, методы анализа и алгоритмы. Всё, что ты хотел знать, но стеснялся спросить.
ЛС тут: @de_kn
Post #74
842
This post (sticker, poll or similar) has no web preview. Open in Telegram
- 😁 13
Я @datadeepdive
Showing posts older than #75 · Back to latest
This post (sticker, poll or similar) has no web preview. Open in Telegram


Однажды на собесе меня спросили “если бы ты выбирал только один метод расчёта АБ-тестов, что бы это было?” — не сомневаясь ни секунды я ответил — бутстрэп.

И тот и другой тест, по сути, делают нужную нам проверку, но Z-тест лучше работает на больших выборках.
# создание таблицы сопряженности
table <- table(df$var, df$metric)
n <- colSums(table) # вычисляем суммы по каждому варианту
x <- table[, 2] # вычисляем количество единиц в каждой группе
# z-тест
prop.test(x, n)
# хи-квадрат
chisq.test(table)
import pandas as pd
from scipy import stats
from statsmodels.stats.proportion import proportions_ztest
# создание таблицы сопряженности
table = pd.crosstab(df['var'], df['metric'])
n = table.sum(axis=1).values # вычисляем суммы по каждому варианту
x = table[1].values # вычисляем количество единиц в каждой группе
# z-тест
z_test = proportions_ztest(x, n)
# хи-квадрат
chi2_test = stats.chi2_contingency(table)




ab_test, где хранятся данные по юзерам, тестам и вариантам). В общем-то это всё. Это один из самых частых флоу, который я встречал. Но он, естественно, не всегда такой и у тебя будут свои нюансы на каждом шаге. Иногда у твоей сплитовалки нет интерфейса и ты “заводишь” тест напрямую в задаче на разработку, иногда это сторонняя система со своими правилами.
Умение правильно считать T-тест это хороший базовый скилл, которым должны обладать все аналитики, продуктовые в особенности.
Two Sample t-test
data: money by var
t = -14.679, df = 998, p-value < 2.2e-16
alternative hypothesis: true difference in means between group A and group B is not equal to 0
95 percent confidence interval:
-10.869281 -8.305884
sample estimates:
mean in group A mean in group B
15.49617 25.08376




# Расчет t-теста Стьюдента
t.test(metric ~ var, data = df, var.equal = T)
# чтобы переключиться на Уэлча, замени в var.equal T на F
from scipy.stats import ttest_ind
# Расчет t-теста Стьюдента
group1 = df[df['var'] == 'A']['metric']
group2 = df[df['var'] == 'B']['metric']
t_stat, p_value = ttest_ind(group1, group2, equal_var = True)
# чтобы переключиться на Уэлча, замени в equal_var True на False
Главное самому не обмануться — дельты, полученные по итогам теста вообще ничего не прогнозируют. Если ты получаешь стат. значимый рост метрики в +30%, это не значит что раскатив вариант, у вас бизнес вырастет на 30%. Это просто саммари по итогам конкретного теста, в конкретный период, на конкретных юзерах.


Чтобы попытаться выровнять распределение, мы можем использовать много методов — от простого логарифмирования и стандартизации, до рангового преобразования и трансформации Йео-Джонсона.
library(MASS)
library(car)
# Вычисляем лучшее значение лямбды
par(mfrow = c(1, 1))
boxcox_result <- boxcox(df$metric ~ 1, lambda = seq(-3,3,0.1))
# Сохраняем его
best_lambda <- boxcox_result$x[which.max(boxcox_result$y)]
# Применяем преобразование
df$metric_transformed <- car::bcPower(df$metric, best_lambda)
# При наличии отрицательных значений, смещаем нашу метрику на минимальную величину +1
df$metric <- df$metric+ abs(min(df$metric)) + 1

Долгое время (да и сейчас не редкость) в интернетах активно форсилась позиция, что T-тест ограничивается нормальным распределением выборки. Это не так. В основе этого ограничения лежит та самая ЦПТ.
generate_sample_means <- function(data, sample_size, n_samples) {
sample_means <- numeric(n_samples)
for (i in 1:n_samples) {
sample <- sample(data, size = sample_size, replace = TRUE)
sample_means[i] <- mean(sample)
}
return(sample_means)
}def generate_sample_means(data, sample_size, n_samples):
sample_means = []
for _ in range(n_samples):
sample = np.random.choice(data, size = sample_size, replace = True)
sample_means.append(np.mean(sample))
return sample_means
# тест Шапиро-Уилка
shapiro.test(df)
# тест Колмогорова-Смирнова на нормальность
ks.test(df, "pnorm", mean = mean(df), sd = sd(df))
import numpy as np
from scipy import stats
# тест Шапиро-Уилка
shapiro_test = stats.shapiro(df)
# тест Колмогорова-Смирнова на нормальность
ks_test = stats.kstest(df, 'norm', args=(np.mean(df), np.std(df)))

Для любого анализа, включая анализ экспериментов, данные нужно сначала почистить и подготовить.
boxplot.stats(df$metric)$out (в Python такого аналога не завезли).