scipy.stats: распределения и описательная статистика

scipy.stats — это «статистический пакет» внутри SciPy: десятки распределений и инструментов описать данные одним числом.

scipy.stats — подмодуль для вероятностных распределений, описательной статистики и статистических тестов.

Зачем отдельный статистический подмодуль

Данные почти всегда содержат случайность, и наука требует её описывать строго: какое распределение у ошибок измерений, насколько правдоподобна гипотеза, какова доверительная вероятность. В нашем отдельном курсе статистики мы разбираем теорию; здесь — как этим пользоваться в научном Python. scipy.stats даёт готовые «объекты-распределения» и тесты, чтобы не выводить формулы вручную.

Описательная статистика «руками»

Прежде чем звать SciPy, поймём, что считается. Среднее, дисперсия, стандартное отклонение — на чистом stdlib (модуль statistics):

import statistics

sample = [4, 8, 15, 16, 23, 42]
print("Среднее     :", statistics.mean(sample))
print("Медиана     :", statistics.median(sample))
print("Дисперсия   :", round(statistics.pvariance(sample), 3))
print("Ст. откл.   :", round(statistics.pstdev(sample), 3))

Вывод:

Среднее     : 18
Медиана     : 15.5
Дисперсия   : 151.667
Ст. откл.   : 12.315

Распределения как объекты

В scipy.stats каждое распределение — объект с единым набором методов. Для нормального распределения norm:

МетодЧто даёт
.pdf(x)плотность вероятности в точке x
.cdf(x)P(X ≤ x) — функция распределения
.ppf(q)квантиль: x, при котором cdf = q (обратная к cdf)
.rvs(n)сгенерировать n случайных значений
.mean(), .std()теоретические среднее и отклонение
from scipy.stats import norm

print(norm.cdf(0))        # 0.5 — половина массы левее нуля
print(norm.ppf(0.975))    # 1.959963... — знаменитое "1.96" для 95%
print(norm.pdf(0))        # 0.3989... — пик плотности
sample = norm.rvs(size=1000, loc=0, scale=1)  # 1000 случайных из N(0,1)

Единый интерфейс — мощь подмодуля: тот же набор методов есть у expon, binom, poisson, t, chi2 и десятков других.

Считаем «правило 1.96» руками

Откуда берётся 1.96? Это квантиль 0.975 стандартной нормали. Аппроксимируем функцию распределения через math.erf (она есть в stdlib!) и проверим:

import math

def normal_cdf(x):
    # CDF стандартной нормали через функцию ошибок
    return 0.5 * (1 + math.erf(x / math.sqrt(2)))

print("P(Z <= 1.96) =", round(normal_cdf(1.96), 4))
print("P(Z <= 0)    =", round(normal_cdf(0.0), 4))
print("Доля в [-1.96, 1.96] =",
      round(normal_cdf(1.96) - normal_cdf(-1.96), 4))

Вывод:

P(Z <= 1.96) = 0.975
P(Z <= 0)    = 0.5
Доля в [-1.96, 1.96] = 0.95

Вот почему доверительный интервал «±1.96 σ» накрывает 95% — мы только что вывели это руками.

Как работает под капотом

Все непрерывные распределения в scipy.stats наследуются от общего класса rv_continuous. Если у распределения задана плотность pdf, библиотека умеет автоматически (численно) получить из неё cdf (интегрированием), ppf (обращением cdf поиском корня), моменты (интегрированием) и генерацию rvs. Поэтому добавить своё распределение — это часто просто задать одну функцию плотности. А «быстрые» распределения вроде нормального имеют аналитические формулы (через erf из scipy.special), чтобы не интегрировать численно каждый раз.

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

  • Путать pdf и cdf. Плотность pdf сама по себе не вероятность; вероятность — это площадь, то есть cdf.
  • Выборочная vs генеральная дисперсия. statistics.pvariance делит на n, variance — на n−1; SciPy и pandas по умолчанию используют разные соглашения (параметр ddof).
  • loc/scale. В SciPy сдвиг и масштаб задаются параметрами loc и scale, а не как аргументы — частая путаница.

Итог

  • scipy.stats — распределения как объекты с единым набором методов (pdf/cdf/ppf/rvs).
  • Описательную статистику можно считать и на stdlib (statistics), и в SciPy.
  • «Правило 1.96» — это квантиль 0.975 нормали; выводится через erf.
  • Под капотом непрерывные распределения порождают cdf/ppf/моменты из одной плотности.
Проверьте себя
1. Что возвращает метод .ppf(q) объекта-распределения в scipy.stats?
AПлотность вероятности
BКвантиль: значение x, при котором cdf(x) = q (обратная к cdf)
CСлучайную выборку
DСреднее распределения
2. Почему доверительный интервал ±1.96σ накрывает 95% нормального распределения?
AЭто произвольное соглашение
BПотому что 1.96 — квантиль 0.975, а cdf(1.96) − cdf(−1.96) = 0.95
CПотому что 1.96 ≈ 2
DИз-за центральной предельной теоремы
3. В чём разница между pdf и cdf?
AЭто одно и то же
Bpdf — плотность (сама не вероятность), cdf — накопленная вероятность P(X ≤ x), то есть площадь под pdf
Ccdf — это случайная выборка
Dpdf всегда равна 1