Финальный разбор: полный чек-лист
// Условие
Финал бесплатного блока — полный цикл разбора, как на работе. Тест нового каталога: дизайн 50/50, две недели, выборка по плану. Перед тобой сырые данные — сделай всё сам: проверь сплит, посчитай эффект, дай вердикт.
result — dict с ключами p_value_srm, conv_a, conv_b, p_value.// Данные
user_id, group (A/B), converted (0/1) — одна строка на юзера// Решение
Подсказка
Порядок тот же, что в чек-листе аналитика: сначала валидность (SRM), потом эффект. stats.chisquare([n_a, n_b], f_exp=[total/2, total/2]).
Дальше — знакомый z-тест двух долей на пулированной SE.
Прежде чем выбирать вывод, собери всё: сплит валиден? эффект значим? какой он в процентных пунктах?
Сначала попробуй сам → Показать решение
df = load_data()
from scipy import stats
# Шаг 1: валидность — SRM-чек
vc = df['group'].value_counts()
n_a, n_b = int(vc['A']), int(vc['B'])
total = n_a + n_b
p_srm = stats.chisquare([n_a, n_b], f_exp=[total / 2, total / 2]).pvalue
# Шаг 2: эффект
s = df.groupby('group')['converted'].agg(['sum', 'count'])
conv_a = s.loc['A', 'sum'] / s.loc['A', 'count']
conv_b = s.loc['B', 'sum'] / s.loc['B', 'count']
p_pool = df['converted'].mean()
se = (p_pool * (1 - p_pool) * (1 / n_a + 1 / n_b)) ** 0.5
z = (conv_b - conv_a) / se
p_value = 2 * (1 - stats.norm.cdf(abs(z)))
result = {'p_value_srm': p_srm, 'conv_a': conv_a, 'conv_b': conv_b, 'p_value': p_value}
print(result)
Полный вердикт для команды?
Это «эталонно чистый» тест: сплит валиден (25 100 vs 24 900 — p ≈ 0.37, обычный шум), выборка собрана по плану, эффект +1 п.п. и p ≈ 5·10⁻⁴ — вероятность получить такое случайно ничтожна. «Незначимо, потому что всего 1 п.п.» путает размер эффекта со значимостью; «нужно p как в учебниках» — несуществующий стандарт: порог был зафиксирован (0.05), и он пройден с запасом. Решение — раскатывать.