Описательная статистика и проверка нормальности
Вычислите асимметрию и эксцесс с помощью scipy.stats, выполните тест Шапиро—Уилка на нормальность и интерпретируйте p-значение
«Описательная статистика и проверка нормальности» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Описательная и выводная статистика
Описательная статистика обобщает то, что содержится в Ваших данных: mean, медиану, стандартное отклонение, асимметрию. Выводная статистика позволяет делать утверждения о генеральной совокупности на основе выборки: проверять гипотезы, вычислять доверительные интервалы и p-значения. Модуль scipy.stats в SciPy объединяет эти направления: он предоставляет как описательные показатели, выходящие за рамки describe() в Pandas, так и полный набор классических проверок гипотез. Сочетание Pandas для обработки данных и SciPy для статистических проверок — стандартный рабочий процесс анализа данных на Python.
Расширенная описательная статистика
describe() в Pandas возвращает количество значений, mean, std, минимальное и максимальное значения, а также квартили. scipy.stats добавляет асимметрию (меру несимметричности распределения) и kurtosis (степень тяжести хвостов). Асимметрия, равная 0, означает симметричное распределение; положительная асимметрия означает более длинный правый хвост (много небольших значений и несколько очень больших). kurtosis, равный 3 (или 0 в форме избыточной kurtosis), соответствует normal распределению; более высокие значения указывают на более тяжёлые хвосты и большее количество экстремальных выбросов, чем даёт normal распределение.
import pandas as pd
from scipy import stats
import numpy as np
np.random.seed(0)
data = np.concatenate([
np.random.exponential(scale=2, size=500), # right-skewed
np.random.normal(loc=5, scale=1, size=500)
])
print('Mean:', round(data.mean(), 3))
print('Std:', round(data.std(), 3))
print('Skewness:', round(stats.skew(data), 3))
print('Kurtosis (excess):', round(stats.kurtosis(data), 3))Понимание асимметрии
Асимметрия важна при выборе статистических проверок и преобразований. Распределение с правой асимметрией (положительной асимметрией) имеет mean, превышающий медиану; это типично для данных о доходах, времени реакции и объёмах продаж. Распределение с левой асимметрией (отрицательной асимметрией) имеет mean, меньший медианы. Эвристическое правило: |асимметрия| < 0.5 означает приблизительно симметричное распределение; значение 0.5–1.0 — умеренную асимметрию; значение > 1.0 — сильную асимметрию, при которой перед применением проверок, предполагающих normal распределение, может быть полезно выполнить логарифмическое или корневое преобразование.
import numpy as np
from scipy import stats
# Right-skewed: income-like data
right_skewed = np.random.lognormal(mean=1, sigma=1, size=1000)
print('Right skew:', round(stats.skew(right_skewed), 3))
# After log transform
print('After log transform:', round(stats.skew(np.log(right_skewed)), 3))
# Symmetric normal
normal_data = np.random.normal(0, 1, 1000)
print('Normal skew:', round(stats.skew(normal_data), 3))Почему важна проверка нормальности
Многие статистические проверки — t-тесты, ANOVA, корреляция Пирсона — предполагают, что данные (или остатки) подчиняются normal (гауссовскому) распределению. При нарушении этого предположения на небольших выборках p-значения проверки могут быть неточными. Проверка нормальности позволяет выяснить, выполняется ли это предположение. На больших выборках (n > 100) проверки нормальности становятся чрезмерно чувствительными и почти всегда отвергают нормальность из-за незначительных отклонений — в таком случае следует опираться на центральную предельную теорему (средние по выборкам приблизительно normal), а не проверять исходные данные.
Проверка Шапиро—Уилка
Проверка Шапиро—Уилка (scipy.stats.shapiro(data)) — наиболее мощная проверка нормальности для выборок размером примерно до 5 000 элементов. Она возвращает W-статистику и p-значение. Если p > 0.05, у нас нет оснований отвергать нормальность — данные согласуются с normal распределением. Если p ≤ 0.05, нормальность отвергается. Помните: отсутствие оснований отвергать нормальность не доказывает, что данные имеют normal распределение; это лишь означает, что недостаточно свидетельств утверждать обратное.
import numpy as np
from scipy import stats
np.random.seed(42)
# Normal data
normal = np.random.normal(0, 1, 100)
stat, p = stats.shapiro(normal)
print(f'Normal data: W={stat:.4f}, p={p:.4f}')
print('Conclusion:', 'Looks normal' if p > 0.05 else 'Not normal')
# Skewed data
skewed = np.random.exponential(1, 100)
stat2, p2 = stats.shapiro(skewed)
print(f'Skewed data: W={stat2:.4f}, p={p2:.4f}')
print('Conclusion:', 'Looks normal' if p2 > 0.05 else 'Not normal')Проверка Колмогорова—Смирнова
Проверка Колмогорова—Смирнова (K-S) (scipy.stats.kstest(data, 'norm', args)) проверяет, соответствует ли выборка заданному распределению. В отличие от проверки Шапиро—Уилка, она работает с любым распределением, а не только с normal, и подходит для больших выборок. Она вычисляет максимальное различие между эмпирической CDF Ваших данных и теоретической CDF. Вариант этой проверки — двухвыборочная проверка K-S (stats.ks_2samp(a, b)) — определяет, происходят ли две выборки из одного распределения; это полезно для сравнения распределений до и после изменения.
import numpy as np
from scipy import stats
np.random.seed(0)
data = np.random.normal(loc=5, scale=2, size=200)
# One-sample K-S test against N(5, 2) distribution
stat, p = stats.kstest(data, 'norm', args=(5, 2))
print(f'K-S test: stat={stat:.4f}, p={p:.4f}')
print('Follows N(5,2)?', 'Yes' if p > 0.05 else 'No')
# Two-sample K-S test
data2 = np.random.normal(loc=5.5, scale=2, size=200)
stat2, p2 = stats.ks_2samp(data, data2)
print(f'Two-sample K-S: stat={stat2:.4f}, p={p2:.4f}')Визуальная проверка нормальности: график QQ
График Q-Q (квантиль—квантиль) — это визуальная проверка нормальности: на нём квантили Ваших данных сопоставляются с квантилями normal распределения. Если данные имеют normal распределение, точки ложатся на прямую диагональную линию. Отклонения от линии указывают на отклонение от нормальности: S-образные кривые свидетельствуют о kurtosis, а изгибы — об асимметрии. Статистические проверки показывают, являются ли отклонения значимыми, а графики Q-Q — их характер и расположение. Используйте scipy.stats.probplot(), чтобы получить данные для построения графика Q-Q.
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
np.random.seed(1)
data = np.random.exponential(2, 200)
# Q-Q plot
(osm, osr), (slope, intercept, r) = stats.probplot(data, dist='norm')
print(f'R-squared of Q-Q fit: {r**2:.4f}') # Close to 1 = normal
# Visual inspection: if plotting, points on the line = normal
# plt.figure()
# stats.probplot(data, dist='norm', plot=plt)
# plt.show()Центральная предельная теорема на практике
Центральная предельная теорема (CLT) утверждает, что распределение выборочных mean приближается к normal по мере увеличения размера выборки независимо от исходного распределения. При n ≥ 30 выборочное mean приблизительно normal, даже если отдельные наблюдения имеют асимметрию. Поэтому t-тесты устойчивы на больших выборках: Вы проверяете, отличается ли mean, а mean приблизительно normal, даже когда исходные данные таковыми не являются. Для небольших выборок из ненормальных генеральных совокупностей используйте непараметрические проверки.
import numpy as np
from scipy import stats
np.random.seed(0)
# Population: heavily right-skewed (exponential)
population = np.random.exponential(scale=1, size=10000)
print('Population skewness:', round(stats.skew(population), 3))
# Sample means are approximately normal (CLT)
sample_means = [np.random.choice(population, 50).mean()
for _ in range(1000)]
print('Sample means skewness:', round(stats.skew(sample_means), 3))
_, p = stats.shapiro(sample_means)
print('Shapiro p-value for means:', round(p, 4))Нормальность по группам
В проверках сравнения групп (t-тест, ANOVA) нормальность требуется внутри каждой группы, а не для данных в целом. Если Вы проверяете, различаются ли продажи по регионам, проверяйте нормальность продаж отдельно внутри каждого региона. Распределение, не являющееся normal в целом, всё же может удовлетворять условию нормальности внутри подгрупп. Применяйте stats.shapiro() к каждой группе с помощью groupby() в Pandas и перебирайте группы, чтобы систематически проверить это предположение.
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(0)
df = pd.DataFrame({
'region': ['N']*50 + ['S']*50 + ['E']*50,
'sales': np.concatenate([
np.random.normal(100, 20, 50),
np.random.normal(110, 25, 50),
np.random.normal(95, 15, 50)
])
})
for region, group in df.groupby('region'):
stat, p = stats.shapiro(group['sales'])
print(f'Region {region}: W={stat:.4f}, p={p:.4f} -> '
f'{"Normal" if p>0.05 else "Not normal"}')Непараметрические альтернативы
Если нормальность нарушена, используйте непараметрические проверки, не делающие предположений о распределении. Проверка Манна—Уитни U (stats.mannwhitneyu) заменяет t-тест для независимых выборок; критерий знаковых рангов Уилкоксона (stats.wilcoxon) заменяет парный t-тест; критерий Краскела—Уоллиса (stats.kruskal) заменяет однофакторный ANOVA. Эти проверки используют ранги, а не исходные значения, и устойчивы к выбросам, но при фактическом выполнении условия нормальности обладают меньшей статистической мощностью, чем их параметрические аналоги.
import numpy as np
from scipy import stats
np.random.seed(0)
# Non-normal data
group_a = np.random.exponential(2, 40)
group_b = np.random.exponential(2.5, 40)
# Parametric would be wrong here; use non-parametric
stat, p = stats.mannwhitneyu(group_a, group_b, alternative='two-sided')
print(f'Mann-Whitney U test: U={stat:.1f}, p={p:.4f}')
print('Groups differ?' , 'Yes' if p < 0.05 else 'No')Сводная статистика для нескольких столбцов
При проведении EDA часто требуется одновременно проверить нормальность и асимметрию всех числовых столбцов. Объедините select_dtypes в Pandas с циклом по столбцам, вызывающим stats.shapiro() и stats.skew(). Создайте сводный DataFrame со столбцами для асимметрии, kurtosis и p-значения Шапиро, чтобы получить общий обзор того, какие столбцы не имеют normal распределения и требуют преобразования перед моделированием. Это часть систематического контрольного списка качества данных.
import pandas as pd
import numpy as np
from scipy import stats
np.random.seed(0)
df = pd.DataFrame({
'age': np.random.normal(35, 10, 200),
'income': np.random.lognormal(10, 1, 200),
'score': np.random.uniform(0, 100, 200)
})
rows = []
for col in df.select_dtypes(include='number').columns:
s = stats.skew(df[col])
_, p = stats.shapiro(df[col][:200])
rows.append({'column': col, 'skewness': round(s, 3),
'shapiro_p': round(p, 4), 'normal': p > 0.05})
print(pd.DataFrame(rows).to_string(index=False))Быстрая проверка
Проверьте, насколько хорошо Вы усвоили понятия анализа данных из этого урока.
Итоги урока
В этом уроке Вы узнали, что scipy.stats.skew() и kurtosis() описывают форму распределения подробнее, чем describe(), scipy.stats.shapiro() проверяет нормальность, причём p > 0.05 означает отсутствие свидетельств против нормальности, а центральная предельная теорема делает t-тесты устойчивыми на больших выборках даже при ненормальных данных. Далее мы применим проверку гипотез с помощью t-тестов, чтобы сравнить mean групп.
Изучай Python с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Описательная статистика и проверка нормальности» бесплатный?
Да — полный текст урока «Описательная статистика и проверка нормальности» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Чему я научусь в уроке «Описательная статистика и проверка нормальности»?
Вычислите асимметрию и эксцесс с помощью scipy.stats, выполните тест Шапиро—Уилка на нормальность и интерпретируйте p-значение Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?
Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Описательная статистика и проверка нормальности»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?
Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Описательная статистика и проверка нормальности
- T-тесты для сравнения средних
- Критерий хи-квадрат для проверки независимости
- ANOVA и апостериорные тесты