Как рассчитать размер выборки A/B

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это знать

«Сколько выборки нужно?» — fundamental в A/B. Запустили с 1000 users → не detect 1% lift → waste. Запустили с 1M → over-powered → waste time.

На собесах A/B power analysis — обязательный знать.

Короткое объяснение

Sample size зависит от:

  • Alpha (α): желаемый FPR (обычно 0.05)
  • Power (1 - β): шанс detect true effect (обычно 0.80)
  • MDE (minimum detectable effect): lift, который важно catch
  • Baseline: текущий conversion (или mean / variance)

Формула (proportions)

Для binary metrics (conversion):

n = 2 × (z_α/2 + z_β)² × p̄ × (1 - p̄) / (p1 - p2)²

Где:

  • z_α/2 = 1.96 для α=0.05
  • z_β = 0.84 для power=0.80
  • p̄ = average proportion
  • p1, p2 — baseline and treatment expected

Практический пример

Setup

  • Baseline CR = 10%
  • Хотим detect +1 p.p. (=1 pp) lift
  • α = 0.05, power = 0.80

Compute

p1 = 0.10, p2 = 0.11, p̄ = 0.105 (p1 - p2)² = 0.0001

n = 2 × (1.96 + 0.84)² × 0.105 × 0.895 / 0.0001 n ≈ 2 × 7.84 × 0.094 / 0.0001 n ≈ 14 740 per group

Total ≈ 29 500 users.

Python функция

from scipy import stats
import numpy as np

def sample_size_proportion(p1, p2, alpha=0.05, power=0.80):
    z_alpha = stats.norm.ppf(1 - alpha/2)
    z_beta = stats.norm.ppf(power)
    p_avg = (p1 + p2) / 2
    n = 2 * (z_alpha + z_beta)**2 * p_avg * (1 - p_avg) / (p1 - p2)**2
    return int(np.ceil(n))

# Example
print(sample_size_proportion(0.10, 0.11))  # 14 758

Для continuous

Revenue, time-on-site — continuous.

n = 2 × (z_α/2 + z_β)² × σ² / (μ1 - μ2)²

σ — standard deviation.

def sample_size_continuous(mu1, mu2, sigma, alpha=0.05, power=0.80):
    z_alpha = stats.norm.ppf(1 - alpha/2)
    z_beta = stats.norm.ppf(power)
    n = 2 * (z_alpha + z_beta)**2 * sigma**2 / (mu1 - mu2)**2
    return int(np.ceil(n))

statsmodels

Complex cases — use library:

from statsmodels.stats.power import NormalIndPower

analysis = NormalIndPower()
sample_size = analysis.solve_power(
    effect_size=0.01 / np.sqrt(0.105 * 0.895),  # Cohen's h-like
    alpha=0.05,
    power=0.80
)

Или proportions specifically:

from statsmodels.stats.power import proportions_effectsize, NormalIndPower

es = proportions_effectsize(0.10, 0.11)
n = NormalIndPower().solve_power(effect_size=es, alpha=0.05, power=0.80)

Trade-offs

Smaller MDE

Больше N нужно. n ∝ 1 / MDE².

MDE cut в 2 → N в 4 раза.

Larger alpha

Easier detect → меньше N. Но more false positives.

Larger power

Safer → больше N.

MDE выбор

Business decision:

«Какой effect важен для shipping?»

  • +0.5%: trivial. Usually ship по другим reasons.
  • +5%: meaningful.
  • +20%: huge.

Pick threshold matching business value.

SRM

Assignment 50/50 сбалансированный. Actual могло быть 50.2/49.8.

Chi-square тест:

from scipy.stats import chisquare

obs = [users_control, users_test]
expected = [sum(obs) / 2, sum(obs) / 2]
chi2, p = chisquare(obs, expected)

if p < 0.01:
    print("SRM issue! Investigate.")

Allocation

50/50 — default. Но:

  • 90/10: conservative, small treatment exposure
  • 50/50 / 50: multiple variants

Unequal splits require recalc.

n ∝ 1/k + 1/(1-k) для ratio k

Equal (0.5) minimizes total N для given power.

Variance reduction

CUPED reduces variance → нужно меньше N. Подробнее.

Duration

N / daily traffic = duration.

Пример: N = 30k per group, 60k total. Traffic 10k/day → 6 days.

Plus consider:

  • Full weeks (weekly patterns)
  • Novelty (2+ weeks)
  • Seasonality

Rule: minimum 2 weeks обычно, даже если N hit earlier.

Ratio metrics

«Revenue per session» — ratio. Delta method для variance (complex).

Approximation:

# Use bootstrap for complex metrics
from scipy.stats import bootstrap

Calculators

Online:

  • Evan's A/B calc
  • Optimizely (старый калькулятор)
  • Статьи с калькуляторами

Для quick estimates.

Повторный расчёт

Что если actual baseline отличается от expected?

Re-compute. Lower baseline → часто больше N нужно.

Частые ошибки

Use observed effect для MDE

MDE — до теста. «Ожидаемо +1%». Не «observed +1%».

Ignore baseline variability

Small p → heavy influence from p × (1-p).

Forget traffic constraint

N 1M required. Daily traffic 1k → 1000 days. Infeasible.

Accept higher MDE или другой approach.

Peeking inflates Type I

Pre-registered sample size matters.

На собесе

«Sample size для A/B»

Ask:

  • Baseline CR
  • Desired MDE
  • Alpha / power (usually default)

Compute или give rough (formula).

«Почему 80% power?»

Convention. Balance: false negative cost vs time / effort.

«Больше выборка лучше?»

Более precise, но more time. Match к business need.

Связанные темы

FAQ

Power меньше 80%?

Possible. Business может accept more misses.

Alpha 0.01 vs 0.05?

Stricter → fewer false positives, require больше N.

One-tailed vs two-tailed?

Two-tailed safer. One-tailed only если strong priors about direction.


Тренируйте A/B — откройте тренажёр с 1500+ вопросами для собесов.