статистиканормальное распределениеpythona/b-тестыпроверка нормальности

Нормальное распределение и проверка нормальности

2026-07-10 12 мин

Коротко: Нормальное распределение (распределение Гаусса) — это симметричный колокол, где около 68% значений лежат в пределах одного стандартного отклонения от среднего, 95% — двух и 99.7% — трёх (правило 68-95-99.7). Проверять нормальность нужно, чтобы выбрать корректный статистический тест: t-тест и линейная регрессия опираются на неё в малых выборках. Смотрят на данные тремя способами — гистограмма, QQ-plot и формальный тест (Шапиро-Уилка на выборках до ~5000, Колмогорова-Смирнова с оговорками). Если данные не нормальны — помогают лог-трансформация, непараметрические тесты (Манна-Уитни) или бутстрап.

Что такое нормальное распределение и правило 68-95-99.7?

Нормальное распределение задаётся двумя числами: средним mu (центр колокола) и стандартным отклонением sigma (ширина). Плотность симметрична относительно среднего, а среднее, медиана и мода совпадают. Чем больше sigma, тем более пологий и растянутый колокол.

Правило 68-95-99.7 — это доля наблюдений, попадающих в интервалы вокруг среднего:

ИнтервалДоля наблюденийЧто это значит
mu ± 1·sigma~68.3%Две трети всех значений
mu ± 2·sigma~95.4%Почти все обычные значения
mu ± 3·sigma~99.7%За пределами — редкие выбросы (0.3%)

Пример с числами. Пусть время загрузки страницы распределено нормально со средним 800 мс и стандартным отклонением 100 мс. Тогда:

Именно на правиле трёх сигм строится классическое определение выброса: всё, что дальше трёх стандартных отклонений от среднего, помечают как аномалию. Работает это только если данные действительно близки к нормальным — для скошенных распределений (выручка, время сессии) правило трёх сигм врёт, и лучше опираться на медиану и перцентили вместо среднего.

Почему нормальность важна для выбора статистического теста?

Многие классические методы выведены в предположении, что данные (или ошибки модели) нормальны. Если предположение нарушено на маленькой выборке — p-value и доверительные интервалы становятся кривыми, и вы можете принять неверное решение по A/B-тесту.

Где нормальность реально нужна:

Где нормальность НЕ требуется (частая путаница):

Ключевая тонкость: t-тест требует нормальности не самих наблюдений, а выборочного среднего. А среднее по мере роста выборки становится нормальным само по себе — об этом в разделе про ЦПТ ниже. Поэтому на больших выборках (тысячи наблюдений на группу) проверка нормальности исходных данных почти не влияет на валидность t-теста. Разбор того, как ошибки в выборе теста превращаются в ошибки первого и второго рода, стоит держать под рукой.

Как проверить нормальность визуально: гистограмма и QQ-plot?

Начинать всегда стоит с глаз, а не с p-value. Два инструмента закрывают 90% случаев.

Гистограмма. Строите распределение и смотрите на форму. Нормальные данные дают симметричный колокол с одним пиком. Тревожные сигналы:

QQ-plot (квантиль-квантильный график). Это более честный инструмент. По оси X — теоретические квантили нормального распределения, по оси Y — квантили ваших данных. Если данные нормальны, точки ложатся на прямую диагональ. Как читать отклонения:

QQ-plot полезнее гистограммы, потому что не зависит от выбора числа бинов и наглядно показывает поведение именно в хвостах, где и прячутся проблемы.

Как работают тесты Шапиро-Уилка и Колмогорова-Смирнова?

Формальные тесты дают число. У всех них нулевая гипотеза одна: «данные взяты из нормального распределения». Если p-value меньше 0.05 — нормальность отвергаем.

Тест Шапиро-Уилка — рабочая лошадка для проверки нормальности. Он сравнивает порядковые статистики выборки с ожидаемыми при нормальности и выдаёт статистику W близко к 1 для нормальных данных. Мощный на малых и средних выборках. Ограничение: scipy считает его надёжным примерно до n = 5000; на больших данных тест становится сверхчувствительным и отвергает нормальность из-за микроскопических отклонений.

Тест Колмогорова-Смирнова сравнивает эмпирическую функцию распределения с теоретической и берёт максимальное расхождение между ними. Важная ловушка: классический одновыборочный KS-тест требует, чтобы mu и sigma были известны заранее, а не оценены по той же выборке. Если подставить выборочные среднее и стандартное отклонение — тест становится слишком консервативным. Для проверки нормальности с оценёнными параметрами корректнее тест Лиллиефорса или Андерсона-Дарлинга. KS чаще применяют для сравнения двух выборок между собой.

ТестКогда применятьОграничение
Шапиро-УилкОсновной выбор, n от 3 до ~5000Сверхчувствителен на больших n
Колмогоров-СмирновСравнение двух выборокТребует известных параметров для проверки нормальности
Андерсона-ДарлингаАкцент на хвостахСложнее интерпретировать

Главный риск любого формального теста нормальности: на большой выборке он почти всегда даёт p < 0.05, потому что идеально нормальных данных в реальности не бывает. Поэтому решение не должно строиться только на p-value — сочетайте тест с QQ-plot и здравым смыслом про размер эффекта. Кстати, о том, что на самом деле означает p-value, полезно освежить перед собеседованием.

Как проверить нормальность в Python за один проход?

Соберём проверку в один блок: гистограмма, QQ-plot и Шапиро-Уилк на синтетических данных.

import numpy as np
from scipy import stats

rng = np.random.default_rng(42)

# Нормальные данные и скошенные (экспоненциальные) для сравнения
normal_data = rng.normal(loc=800, scale=100, size=500)
skewed_data = rng.exponential(scale=300, size=500)

def check_normality(x, name):
    stat, p = stats.shapiro(x)
    skew = stats.skew(x)
    kurt = stats.kurtosis(x)  # избыточный эксцесс, для нормального ~0
    verdict = "нормально" if p > 0.05 else "НЕ нормально"
    print(f"{name}: W={stat:.3f}, p={p:.4f} -> {verdict}")
    print(f"   асимметрия={skew:.2f}, эксцесс={kurt:.2f}")

check_normality(normal_data, "Нормальные")
check_normality(skewed_data, "Скошенные")

Ожидаемый вывод: у нормальных данных p будет заметно больше 0.05, асимметрия около нуля; у экспоненциальных — p близко к нулю и положительная асимметрия около 2. Дополнительно смотрят на два числа:

Для визуальной части добавьте QQ-plot:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
stats.probplot(normal_data, dist="norm", plot=axes[0])
axes[0].set_title("Нормальные: точки на линии")
stats.probplot(skewed_data, dist="norm", plot=axes[1])
axes[1].set_title("Скошенные: загиб в хвосте")
plt.tight_layout()

Прогнать этот код и поменять параметры распределений можно прямо в браузере — в Python-тренажёре есть scipy и numpy без локальной установки. Если хочется сначала подтянуть базу по массивам и векторизации, стартуйте с курса по NumPy, а генерацию случайных выборок для симуляций разбирает отдельная статья про случайность и A/B-симуляции в NumPy.

Что делать, если данные не нормальны?

Ненормальность — это не тупик, а развилка. Есть три рабочих пути.

1. Трансформация. Скошенные вправо данные (выручка, время, счётчики) часто выпрямляются логарифмом. Логарифм сжимает большие значения сильнее маленьких и убирает длинный правый хвост.

# Лог-трансформация для правоскошенных данных.
# log1p безопасен при нулях: log(1 + x)
transformed = np.log1p(skewed_data)
stat, p = stats.shapiro(transformed)
print(f"После log1p: p={p:.4f}")  # часто становится > 0.05

Помимо логарифма используют квадратный корень (для счётчиков) и преобразование Бокса-Кокса, которое подбирает оптимальную степень автоматически (scipy.stats.boxcox). Минус трансформаций — результат приходится интерпретировать в преобразованной шкале, а среднее логарифмов не равно логарифму среднего.

2. Непараметрические тесты. Вместо t-теста берёте тест, который не требует нормальности. Он сравнивает не средние, а ранги значений:

# Вместо t-теста при ненормальных данных
u_stat, p = stats.mannwhitneyu(normal_data, skewed_data, alternative="two-sided")
print(f"Манна-Уитни: p={p:.4f}")

Соответствия простые: двухвыборочный t-тест меняете на тест Манна-Уитни, парный t-тест — на тест Уилкоксона, ANOVA — на тест Краскела-Уоллиса. Платите за это небольшой потерей мощности, если данные всё-таки были нормальны.

3. Бутстрап. Самый универсальный подход. Многократно пересэмплируете данные с возвращением, каждый раз считаете нужную статистику (среднее, медиану, разность) и строите распределение оценки эмпирически. Никаких допущений о форме.

def bootstrap_mean_ci(x, n_boot=10000, alpha=0.05):
    boot_means = np.array([
        rng.choice(x, size=len(x), replace=True).mean()
        for _ in range(n_boot)
    ])
    lo = np.percentile(boot_means, 100 * alpha / 2)
    hi = np.percentile(boot_means, 100 * (1 - alpha / 2))
    return lo, hi

lo, hi = bootstrap_mean_ci(skewed_data)
print(f"95% ДИ среднего (бутстрап): [{lo:.1f}, {hi:.1f}]")

Бутстрап отлично работает для метрик вроде выручки на пользователя или ARPU, которые почти никогда не нормальны из-за китов в правом хвосте. Механика доверительных интервалов подробно разобрана в статье про доверительный интервал простыми словами.

Как выбрать путь из трёх? Если цель — построить понятную модель или объяснить эффект бизнесу, начинайте с трансформации: лог-шкала часто ещё и осмысленнее (проценты роста вместо абсолютных рублей). Если нужен просто ответ «есть разница или нет» на небольшой выборке — берите непараметрику, она быстрая и не требует ничего настраивать. Если же метрика денежная, с редкими огромными значениями, и вам важен именно доверительный интервал среднего — бутстрап честнее всех, потому что не пытается натянуть колокол на данные, которые колоколом никогда не были. На практике эти подходы не конкурируют: аналитики часто считают и бутстрап, и Манна-Уитни, и если оба дают согласованный вывод — доверия к результату больше.

Как нормальность связана с ЦПТ и A/B-тестами?

Центральная предельная теорема (ЦПТ) — причина, по которой аналитики в продакшене редко паникуют из-за ненормальных данных. ЦПТ утверждает: распределение выборочного среднего стремится к нормальному по мере роста размера выборки, независимо от того, как распределены исходные данные (при конечной дисперсии).

Что это значит на практике. Даже если время сессии распределено экспоненциально с уродливым хвостом, среднее время по группе из нескольких тысяч пользователей будет распределено практически нормально. А t-тест сравнивает именно средние. Поэтому:

Практический чек-лист для A/B-теста:

Скорость роста «нормальности среднего» зависит от того, насколько кривые исходные данные: для слегка скошенных хватит пары сотен наблюдений, для экстремальных хвостов нужны тысячи. Проверить это своими руками легко — сгенерируйте выборки, посчитайте средние много раз и постройте их распределение (та самая симуляция из раздела про Python). Как всё это увязывается с расчётом статзначимости, показано в разборе A/B-тестов на Python через scipy.stats.

Что запомнить

Статистика на собеседовании аналитика — почти гарантированный блок вопросов. Прогнать типовые формулировки помогут подборка главных вопросов по статистике и живая тренировка в AI-интервьюере. А закрепить SQL-часть, без которой аналитику никуда, удобно на банке SQL-задач и в тренажёре вопросов — первые задачи открыты бесплатно, дальше подписка Pro.

Отточи статистику на реальных задачах
Тесты нормальности, лог-трансформация и бутстрап понятны, только когда ты сам гоняешь код по данным. Первые 5 задач в Python-тренажёре бесплатны, дальше — Pro.
Открыть Python-тренажёр →