Собесов

Сценарий: bootstrap confidence interval — когда и как

Статистика и теорверConfidence intervalsСредняяMiddle

Условие

Метрика — медиана LTV. Параметрический CI для медианы — сложно. Использовать bootstrap. Как?

Решение

Идея bootstrap

Семплируем из данных с замещением, считаем статистику много раз, смотрим распределение.

import numpy as np
 
def bootstrap_ci(data, stat=np.median, n_boot=10_000, alpha=0.05):
    boot = np.array([stat(np.random.choice(data, len(data), replace=True))
                      for _ in range(n_boot)])
    lo, hi = np.percentile(boot, [100 * alpha / 2, 100 * (1 - alpha / 2)])
    return lo, hi
 
ci = bootstrap_ci(ltv_data, stat=np.median)

Виды bootstrap CI

1. Percentile: квантили эмпирического распределения. Простой, но смещён при не-симметрии.

2. Basic (reverse percentile):

CI = [2·θ_hat - θ_high, 2·θ_hat - θ_low]

3. BCa (bias-corrected and accelerated): учитывает bias и skewness. Лучший, но сложнее.

from scipy.stats import bootstrap
 
res = bootstrap((data,), np.median, confidence_level=0.95,
                method='BCa', n_resamples=10_000)
print(res.confidence_interval)

4. Studentized: использует SE bootstrap-репликаций. Требует pivot, обычно избыточно.

Когда bootstrap нужен

  • Сложная статистика (медиана, IQR, MAD, p99).
  • Нелинейные комбинации (ratio metric, CTR между сессиями).
  • Малая выборка с неизвестным распределением.
  • Стат. фунция, для которой нет аналитического SE.

Когда не нужен

  • Простое среднее на большой выборке — t/Normal CI быстрее.
  • Очень малая выборка (n < 10) — bootstrap нестабильный.
  • Высокая корреляция / clustered data — нужен block bootstrap.

Bootstrap для A/B

def diff_median(a, b):
    return np.median(a) - np.median(b)
 
def bootstrap_diff(a, b, n_boot=10_000):
    diffs = []
    for _ in range(n_boot):
        a_re = np.random.choice(a, len(a), replace=True)
        b_re = np.random.choice(b, len(b), replace=True)
        diffs.append(np.median(a_re) - np.median(b_re))
    return np.array(diffs)
 
diffs = bootstrap_diff(group_a, group_b)
lo, hi = np.percentile(diffs, [2.5, 97.5])
p_value = 2 * min((diffs > 0).mean(), (diffs < 0).mean())

Block bootstrap для time series

Если данные коррелированы (time series, panel), наивный bootstrap нарушает зависимость. Block bootstrap режет данные на блоки и сэмплирует блоки.

Подводные камни

  1. n_boot ≥ 10 000 для стабильных CI; 1 000 — недостаточно для 95% CI с разумной точностью.
  2. Percentile CI смещён в сторону среднего на асимметричных распределениях — BCa лучше.
  3. Bootstrap не работает на n < 10 — выборки повторяются слишком сильно.
  4. Не для extreme quantiles (p99 на n=100) — bootstrap не может «изобрести» новые tail точки.
  5. Clustered/time series данные нарушают iid assumption — block bootstrap.

Эталонный ответ

Resample with replacement, calculate stat, repeat 10k раз, take percentiles. Best для медиан, ratio metrics, сложных статистик. BCa> percentile при асимметрии. Block bootstrap для time series.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти