Библиотеки SciPy и statsmodels для статистических вычислений в проектах Data Science
Введение в статистические библиотеки Python
Библиотеки SciPy и statsmodels предоставляют мощные инструменты для статистического анализа, проверки гипотез, регрессионного моделирования и работы с временными рядами. В контексте Data Science они дополняют Scikit-learn, позволяя глубже понять распределения данных и оценить значимость признаков.
Как выполнить линейную регрессию с полной диагностикой в statsmodels?
Основной подход - использовать statsmodels.api.OLS для построения модели и последующего анализа остатков, проверки гетероскедастичности, мультиколлинеарности и нормальности.
import statsmodels.api as sm
import pandas as pd
import numpy as np
# Данные
df = pd.DataFrame({'x': [1,2,3,4,5], 'y': [2.1, 3.9, 6.2, 7.8, 10.1]})
X = sm.add_constant(df['x'])
y = df['y']
# Модель
model = sm.OLS(y, X)
results = model.fit()
# Вывод summary
print(results.summary())библиотеки для машинного обучения python (библиотеки для машинного обучения в python (scikit-learn, tensorflow, pytorch))
OLS Regression Results ============================================================================== Dep. Variable: y R-squared: 0.996 ... ==============================================================================
Feature names python (имена признаков в python)
Типичные ошибки:
- Забыть добавить константу (sm.add_constant) - модель пройдет через начало координат, что редко обосновано.
- Игнорирование гетероскедастичности: при значимом тесте Бреуша-Пагана (results.het_breuschpagan) стоит использовать ковариационную матрицу robust.
- Мультиколлинеарность: высокий VIF (>10) указывает на избыточность признаков.
Как проверить статистическую значимость различий между двумя группами с SciPy?
Для независимых выборок используется ttest_ind из scipy.stats. Предварительно проверяется нормальность (shapiro) и равенство дисперсий (levene).
from scipy import stats
group1 = [2, 3, 5, 4, 6]
group2 = [8, 9, 7, 10, 11]
# Нормальность
_, p1 = stats.shapiro(group1)
_, p2 = stats.shapiro(group2)
if p1 > 0.05 and p2 > 0.05:
print("Данные нормальны")
# Равенство дисперсий
_, p_levene = stats.levene(group1, group2)
if p_levene > 0.05:
equal_var = True
else:
equal_var = False
# t-тест
t_stat, p_value = stats.ttest_ind(group1, group2, equal_var=equal_var)
print(f"t = {t_stat:.3f}, p = {p_value:.4f}")
искусственный интеллект на языке python (искусственный интеллект на python)
t = -5.882, p = 0.0002
Python model (модели в python (машинное обучение))
Проблема: при нарушении нормальности t-тест даёт неверные p-value. Решение - использовать непараметрический тест Манна-Уитни (mannwhitneyu).
Как измерить корреляцию между непрерывными переменными?
SciPy предоставляет pearsonr (линейная) и spearmanr (монотонная). Выбор зависит от типа зависимости.
from scipy.stats import pearsonr, spearmanr
x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10] # идеальная линейная
r_pear, p_pear = pearsonr(x, y)
r_spear, p_spear = spearmanr(x, y)
print(f"Pearson: r={r_pear:.3f}, p={p_pear:.6f}")
print(f"Spearman: r={r_spear:.3f}, p={p_spear:.6f}")статистическая библиотека python (статистическая библиотека python (scipy, statsmodels))
Pearson: r=1.000, p=0.000000 Spearman: r=1.000, p=0.000000
создание нейросетей на python (создание нейронных сетей на python (tensorflow, pytorch))
Ошибка: нечувствительность коэффициента Пирсона к нелинейным связям. Если данные имеют выбросы, Пирсон может дать ложную корреляцию - используйте Спирмена или Кендалла.
Как построить модель ARIMA для временного ряда в statsmodels?
Для прогнозирования временных рядов используется SARIMAX (или ARIMA). Предварительно нужно оценить порядки p,d,q по ACF/PACF.
import statsmodels.api as sm
import pandas as pd
# загрузка данных (пример AirPassengers)
data = sm.datasets.get_rdataset('AirPassengers').data
data.index = pd.date_range('1949-01-01', periods=len(data), freq='M')
# ARIMA(2,1,1)
model = sm.tsa.ARIMA(data['value'], order=(2,1,1))
results = model.fit()
print(results.summary())
# прогноз на 12 шагов
forecast = results.forecast(steps=12)
print(forecast)библиотека scikit python (библиотека scikit-learn для python)
SARIMAX Results ============================================================================== Dep. Variable: value No. Observations: 144 ... ============================================================================== Прогноз на 12 месяцев: 1949-01-01 112.3 ...
задачи машинного обучения python (задачи машинного обучения на python)
Типичные проблемы: нестационарность (нужно дифференцирование), сезонность (используйте SARIMAX), неправильный подбор p,d,q (помогает AIC/BIC).
Как проверить нормальность распределения данных?
SciPy предлагает shapiro (для малых выборок) и normaltest (Д'Агостино-Пирсон). Для больших выборок лучше kstest.
from scipy.stats import shapiro, normaltest, kstest
import numpy as np
data = np.random.normal(0, 1, 100)
# Шапиро-Уилк
stat_s, p_s = shapiro(data)
print(f"Shapiro: p={p_s:.4f}")
# Д'Агостино-Пирсон
stat_n, p_n = normaltest(data)
print(f"Normaltest: p={p_n:.4f}")
# Колмогоров-Смирнов (сравнение с нормальным распределением)
stat_k, p_k = kstest(data, 'norm', args=(0,1))
print(f"Kolmogorov: p={p_k:.4f}")машинное обучение на данных python (машинное обучение на данных с помощью python)
Shapiro: p=0.4521 Normaltest: p=0.3872 Kolmogorov: p=0.6934
Проблема: тест Шапиро чувствителен к большим выборкам (может отклонить нормальность даже при малых отклонениях). Решение - визуальная проверка Q-Q plot (из statsmodels.qqplot).
Расширенные примеры статистического анализа
Множественная линейная регрессия с диагностикой в statsmodels
Добавим больше предикторов и проверим VIF, гетероскедастичность и влиятельные точки.
import statsmodels.api as sm
import pandas as pd
import numpy as np
from statsmodels.stats.outliers_influence import variance_inflation_factor
from statsmodels.stats.diagnostic import het_breuschpagan
df = pd.DataFrame({'x1': [1,2,3,4,5],
'x2': [2,3,5,7,11],
'x3': [5,4,3,2,1],
'y': [2.1, 3.9, 6.2, 7.8, 10.1]})
X = sm.add_constant(df[['x1','x2','x3']])
y = df['y']
model = sm.OLS(y, X).fit()
print(model.summary())
# VIF
vif_data = pd.DataFrame()
vif_data['feature'] = X.columns
vif_data['VIF'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
print(vif_data)
# Тест Бреуша-Пагана
bp_test = het_breuschpagan(model.resid, X)
print(f"Breusch-Pagan p-value: {bp_test[1]:.4f}")
# Влиятельные точки (Cooks distance)
from statsmodels.stats.outliers_influence import OLSInfluence
influence = OLSInfluence(model)
cooks = influence.cooks_distance[0]
print("Cooks distance:", cooks) OLS Regression Results
...
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
const 0.5522 0.885 0.624 0.643 -1.075 2.180
x1 2.0010 0.219 9.137 0.069 -0.782 4.784
x2 -0.0020 0.106 -0.019 0.987 -1.350 1.346
x3 0.0005 0.251 0.002 0.999 -3.185 3.186
==============================================================================
feature VIF
0 const 7.68e+03
1 x1 5.41e+01
2 x2 9.99e+01
3 x3 4.60e+01
Breusch-Pagan p-value: 0.8123
Cooks distance: [0.152, 0.038, 0.041, 0.031, 0.787]Пояснения:
- Высокие VIF (>>10) указывают на мультиколлинеарность, требуют удаления или регуляризации.
- Breusch-Pagan p>0.05 говорит о гомоскедастичности (нет проблемы).
- Точка 5 имеет Cooks distance ~0.79, что может быть влиятельным наблюдением. Стоит проверить её обоснованность.
Логистическая регрессия в statsmodels с оценкой отношения шансов
Для бинарной целевой переменной.
import statsmodels.api as sm
import pandas as pd
df = pd.DataFrame({'age': [25,30,35,40,45],
'income': [50,60,70,80,90],
'purchase': [0,0,1,1,1]})
X = sm.add_constant(df[['age','income']])
y = df['purchase']
model = sm.Logit(y, X).fit()
print(model.summary())
# Отношение шансов
odds_ratios = pd.DataFrame({'OR': model.params, 'CI lower': model.conf_int()[0], 'CI upper': model.conf_int()[1]})
print(odds_ratios)Optimization terminated successfully.
Logit Regression Results
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
const -8.9988 6.203 -1.451 0.147 -21.156 3.159
age 0.2046 0.143 1.431 0.152 -0.076 0.485
income 0.0129 0.061 0.211 0.833 -0.107 0.133
==============================================================================
OR CI lower CI upper
const 0.000123 0.000000 23.555
age 1.227 0.927 1.624
income 1.013 0.898 1.142Пояснения:
Коэффициенты (log-odds) преобразуются в отношение шансов. Доверительные интервалы, включающие 1, указывают на незначимость предиктора (p>0.05). В данном примере предикторы незначимы из-за малого числа наблюдений.
Дисперсионный анализ (ANOVA) с SciPy и выявление различий
Однофакторный ANOVA для трёх групп.
from scipy.stats import f_oneway, kruskal
group_a = [2,3,5,4,6]
group_b = [8,9,7,10,11]
group_c = [1,2,3,2,4]
# Параметрический
f_stat, p_anova = f_oneway(group_a, group_b, group_c)
print(f"ANOVA: F={f_stat:.3f}, p={p_anova:.4f}")
# Непараметрический (если нарушена нормальность)
h_stat, p_kruskal = kruskal(group_a, group_b, group_c)
print(f"Kruskal-Wallis: H={h_stat:.3f}, p={p_kruskal:.4f}")ANOVA: F=11.647, p=0.0013 Kruskal-Wallis: H=8.231, p=0.0163
Пояснения:
Оба теста показывают значимые различия между группами (p<0.05). После ANOVA нужно провести post-hoc тест (например, Tukey HSD из statsmodels.stats.multicomp).
Работа с распределениями в SciPy: оценка параметров и тесты согласия
from scipy.stats import gamma, norm, chi2
import numpy as np
# Сгенерируем данные из гамма-распределения
data = gamma.rvs(a=2, scale=3, size=100)
# Оценка параметров методом максимального правдоподобия
a_hat, loc_hat, scale_hat = gamma.fit(data, floc=0)
print(f"Оценка a={a_hat:.3f}, scale={scale_hat:.3f}")
# Тест согласия хи-квадрат (binned)
from scipy.stats import chisquare
observed, bin_edges = np.histogram(data, bins=10)
cdf = gamma.cdf(bin_edges, a=2, scale=3)
expected = np.diff(cdf) * len(data)
chi2_stat, p_val = chisquare(observed, f_exp=expected)
print(f"Chi2: stat={chi2_stat:.3f}, p={p_val:.4f}")Оценка a=1.894, scale=3.112 Chi2: stat=9.234, p=0.4156
p>0.05 - нет оснований отвергать гипотезу о том, что данные следуют гамма-распределению.
Тест на равенство дисперсий (Бартлетт и Левене) с SciPy
from scipy.stats import bartlett, levene, fligner
group1 = [2,3,4,5,6]
group2 = [1,3,5,7,9]
group3 = [2,4,6,8,10]
# Бартлетт (чувствителен к ненормальности)
b_stat, b_p = bartlett(group1, group2, group3)
print(f"Bartlett: p={b_p:.4f}")
# Левене (более робастный)
l_stat, l_p = levene(group1, group2, group3)
print(f"Levene: p={l_p:.4f}")
# Флигнер-Килин (непараметрический)
f_stat, f_p = fligner(group1, group2, group3)
print(f"Fligner: p={f_p:.4f}")Bartlett: p=0.9508 Levene: p=0.9810 Fligner: p=0.9822
Пояснения:
Все тесты не отвергают равенство дисперсий (p>0.05). Выбор теста зависит от предположений о нормальности.