Ранний восторг: хватает ли выборки?
// Условие
Тест нового онбординга идёт три дня, собрано по 700 юзеров на группу. В дашборде конверсия 8.0% против 10.0% — команда в восторге: «+2 пункта, останавливаем и катим!»
result — dict с ключами p_value, n_needed.// Данные
user_id, group (A/B), converted (0/1) — одна строка на юзера// Решение
Подсказка
p-value — обычный z-тест двух долей на текущих данных.
Выборка на группу: n = (z_alpha + z_beta)**2 * (p1*(1-p1) + p2*(1-p2)) / mde**2, где z_alpha = 1.96 (α=0.05, двусторонний), z_beta = 0.84 (мощность 80%), p1 = 0.08, p2 = 0.10, mde = 0.02.
Сравни n_needed с фактическими 700 на группу — вот и ответ, почему «+2 пункта» пока ничего не значат.
Сначала попробуй сам → Показать решение
df = load_data()
from scipy import stats
s = df.groupby('group')['converted'].agg(['sum', 'count'])
pa = s.loc['A', 'sum'] / s.loc['A', 'count']
pb = s.loc['B', 'sum'] / s.loc['B', 'count']
p_pool = df['converted'].mean()
se = (p_pool * (1 - p_pool) * (1 / s.loc['A', 'count'] + 1 / s.loc['B', 'count'])) ** 0.5
z = (pb - pa) / se
p_value = 2 * (1 - stats.norm.cdf(abs(z)))
# Требуемая выборка на группу: α=0.05 (z=1.96), мощность 80% (z=0.84)
p1, p2, mde = 0.08, 0.10, 0.02
n_needed = (1.96 + 0.84) ** 2 * (p1 * (1 - p1) + p2 * (1 - p2)) / mde ** 2
result = {'p_value': p_value, 'n_needed': n_needed}
print(result)
Команда ждёт отмашку. Твой ответ?
Сейчас невозможно отличить реальный эффект от шума: p ≈ 0.19, а мощности при 700 юзерах хватает лишь на огромные эффекты. Говорить «эффекта нет» так же неверно, как «эффект есть» — данных просто недостаточно. Расчёт выборки (≈3200 на группу) должен был случиться до запуска; сейчас честный ход — продолжить тест до плановой выборки и не подглядывать в p-value каждый день.