Какие знаешь методы снижения дисперсии?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Методы снижения дисперсии в A/B тестировании
Дисперсия (variance) в контексте A/B тестирования — это естественная вариативность результатов из-за случайности. Высокая дисперсия означает, что нужно больше пользователей для получения статистически значимого результата, что требует больше времени и денег. Рассмотрю методы для её снижения.
1. Стратификация (Stratification)
Суть: разделить аудиторию на подгруппы (страты) до распределения в тест и контроль, чтобы каждая страта была представлена в обоих группах поровну.
Пример:
Общая аудитория:
├─ New users (зарегистрировались < 7 дней): 20%
├─ Active users (использовали > 5 раз в неделю): 30%
├─ At-risk users (не активны 2+ недели): 20%
└─ Dormant users (не активны 1+ месяца): 30%
Без стратификации:
Тест группа может случайно получить 25% new users, контроль 15%
→ Это увеличивает дисперсию
Со стратификацией:
Каждая группа получит ровно 20% new users, 30% active и т.д.
→ Это снижает дисперсию на ~10-30%
Реализация:
WITH user_strata AS (
SELECT
user_id,
CASE
WHEN DATEDIFF(day, created_at, GETDATE()) < 7 THEN 'new'
WHEN last_activity_date >= DATEADD(day, -7, GETDATE()) THEN 'active'
WHEN last_activity_date >= DATEADD(day, -30, GETDATE()) THEN 'at_risk'
ELSE 'dormant'
END AS user_stratum
FROM users
),
stratified_assignment AS (
SELECT
user_id,
user_stratum,
CASE
WHEN RAND() < 0.5 THEN 'test'
ELSE 'control'
END AS group_assignment
FROM user_strata
)
SELECT * FROM stratified_assignment;
2. CUPED (Controlled Experiment Using Pre-Experiment Data)
Суть: использовать исторические данные пользователя до эксперимента как контрольную переменную, чтобы уменьшить шум.
Как это работает:
Обычный расчёт:
metric_diff = avg(test_group) - avg(control_group)
Variance = Var(test) + Var(control) [высокая]
С CUPED:
adjusted_metric = current_metric - historical_metric * correlation_coefficient
Variance снижается на (1 - r²), где r = корреляция
Пример:
Есть гипотеза, что новый алгоритм рекомендаций увеличит engagement (likes/day).
Пользователь в тесте:
- Historical avg (последние 30 дней): 5 лайков/день
- During test: 6.5 лайков/день
- Raw lift: 1.5 лайка/день
Пользователь в контроле:
- Historical avg: 5 лайков/день
- During test: 5.8 лайков/день
- Raw lift: 0.8 лайка/день
Сырая разница: 1.5 - 0.8 = 0.7 лайка/день
С CUPED (если исторические данные сильно коррелируют с текущими, r=0.9):
- CUPED metric test = 6.5 - 5 * 0.9 = 1.6
- CUPED metric control = 5.8 - 5 * 0.9 = 1.3
- Adjusted diff = 1.6 - 1.3 = 0.3 (меньше дисперсия!)
import numpy as np
def cuped_metric(current, historical, correlation):
"""Adjust metric using CUPED method"""
return current - historical * correlation
# Example data
current_test = np.array([6.5, 7.2, 5.8, 6.9])
historical_test = np.array([5.0, 5.5, 5.2, 5.8])
corr = 0.85
adjusted = cuped_metric(current_test, historical_test, corr)
print(f"Variance reduced by {1 - corr**2:.1%}")
3. Variance Reduction по метрикам (Choosing the Right Metric)
Суть: некоторые метрики имеют низкую дисперсию, некоторые высокую. Выбор правильной метрики снизит время теста.
Пример:
Гипотеза: новый CTA "Купить сейчас" увеличит конверсии.
Вариант A (высокая дисперсия):
- Метрика: Revenue ($)
- Дисперсия высокая (клиенты платят по-разному)
- Needed sample: 50,000 юзеров
Вариант B (низкая дисперсия):
- Метрика: Click Rate (% кликнув на CTA)
- Дисперсия низкая (клик либо есть, либо нет)
- Needed sample: 5,000 юзеров
Вариант C (баланс):
- Метрика: Orders per user (не revenue, а количество заказов)
- Средняя дисперсия
- Needed sample: 10,000 юзеров
Вывод: выбирать метрику, которая:
- Релевантна бизнесу
- Имеет низкую дисперсию
- Может быть собрана быстро
4. Увеличение размера выборки (Power Analysis)
Суть: рассчитать минимально необходимый размер выборки заранее.
from scipy.stats import norm
def required_sample_size(baseline_rate, min_detectable_effect, alpha=0.05, power=0.8):
"""
Рассчитать размер выборки для A/B теста
baseline_rate: текущий率 конверсии
min_detectable_effect: минимальный lift, который хотим обнаружить (e.g., 0.05 = 5%)
alpha: вероятность Type I error (false positive) - обычно 0.05
power: вероятность обнаружить эффект, если он есть - обычно 0.8
"""
z_alpha = norm.ppf(1 - alpha/2) # Two-tailed
z_beta = norm.ppf(power)
# Effect size (Cohen's h)
h = 2 * (np.arcsin(np.sqrt(baseline_rate)) -
np.arcsin(np.sqrt(baseline_rate + min_detectable_effect)))
# Required sample per group
n_per_group = 2 * ((z_alpha + z_beta) / h) ** 2
return n_per_group
# Example: baseline CR 10%, want to detect 15% lift (1.5% absolute)
baseline = 0.10
effect = 0.015
n = required_sample_size(baseline, effect)
print(f"Required {n:,.0f} users per group")
print(f"Total: {2*n:,.0f} users")
print(f"If 10k users/day, need ~{2*n/10000:.1f} days")
5. Sequential Testing (Peek-able Designs)
Суть: вместо фиксированного размера выборки, смотреть на результаты во время теста и остановить, когда достигли статистической значимости.
Преимущество: можешь остановить тест раньше, если результат очевидный.
Процесс:
День 1: 1,000 юзеров - недостаточно данных
День 2: 2,000 юзеров - всё ещё не значимо
День 3: 3,000 юзеров - p-value = 0.04 ✓ СТОП!
(вместо планируемых 5,000 юзеров)
Предупреждение: нужно правильно рассчитать alpha level, чтобы избежать false positives. Используй Bonferroni correction или другие методы.
6. Matching (Propensity Score Matching)
Суть: для каждого пользователя в тесте найти подобного пользователя в контроле, чтобы уменьшить confounding variables.
Пример:
Тестируем новый UI для платёжной формы. Но есть confound: более опытные пользователи (которые ввели платёж 10+ раз) уже хорошо ориентируются в текущем UI.
Без matching:
- Test group случайно получит 25% опытных
- Control group случайно получит 15% опытных
- → Дисперсия высокая
С matching:
- Каждый опытный пользователь в тесте паруется с опытным в контроле
- Каждый новичок в тесте паруется с новичком в контроле
- → Дисперсия снижена
7. Bayesian Methods
Суть: вместо frequentist подхода (p-values), использовать Bayesian inference с prior информацией.
Преимущество: можешь использовать исторические данные как prior, что снижает дисперсию.
Пример:
from scipy.stats import beta
# Historical data: 1000 tests, avg lift 5%
prior_alpha = 1000 * 0.05
prior_beta = 1000 * (1 - 0.05)
# Current test: 100 conversions из 1500
current_conversions = 100
current_total = 1500
# Posterior distribution
posterior = beta(prior_alpha + current_conversions,
prior_beta + current_total - current_conversions)
# Credible interval
print(f"95% credible interval: [{posterior.ppf(0.025):.3f}, {posterior.ppf(0.975):.3f}]")
8. Machine Learning для прогнозирования метрик
Суть: использовать ML модель для прогнозирования, какой будет метрика, на основе признаков пользователя. Потом использовать остатки (residuals) от модели как метрику теста.
Эффект: может снизить дисперсию на 20-50%.
Сравнение методов по эффективности
| Метод | Снижение дисперсии | Сложность | Применимость |
|---|---|---|---|
| Стратификация | 10-30% | Низкая | Всегда |
| CUPED | 20-40% | Средняя | Когда есть historical data |
| Правильная метрика | 30-70% | Средняя | Всегда |
| Sequential testing | N/A (ускоряет) | Высокая | Когда возможно |
| Matching | 15-25% | Высокая | Специфичные cases |
| Bayesian | 20-50% | Высокая | Когда есть strong prior |
| ML prediction | 20-50% | Очень высокая | Большие датасеты |
Практическая рекомендация
Большинство компаний применяют:
- Стратификация (простая, всегда помогает)
- Правильный выбор метрики (выбрать метрику с низкой дисперсией)
- Power analysis (рассчитать нужный sample size заранее)
- CUPED (если есть исторические данные)
Эта комбинация обычно снижает время тестирования на 30-50%.
Заключение
Снижение дисперсии — это критический навык для Product Analyst'а, так как это позволяет компании быстрее принимать решения на основе тестов. Выбор метода зависит от конкретной ситуации, но начать стоит со стратификации и правильного выбора метрики — это даст быстрый выигрыш без сложности.