Семинар¶
I. Мультиколлинеарность.¶
Строгая мультиколлинераность появляется при нарушении условия ТГМ о том, что матрица \(X\) является матрицей полного ранга и возникает при наличии линейной зависимости между регрессорами. Однако на практике проблемы возникают и при не строгой мультиколлинеарности (квазимультиколлинеарности), при которой \(\det(X'X) \approx 0\).
В теории: строгая мультиколлинеарность приведёт к тому, что оценки МНК будут получаться неединственными, нестрогая мультиколлинеарность не нарушит ТГМ.
На практике: мультиколлинеарность приводит к тому, что:
оценки \(\hat{\beta}\) становятся неустойчивыми
оценки коэффициентов имеют высокие стандартные ошибки
доверительные интервалы становятся широкими
коэффициенты меняют знак или становятся незначимыми при небольших изменениях данных
t-статистики отдельных коэффициентов малы, но F-статистика на значимость в целом велика.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.formula.api as smf
diamonds = sns.load_dataset('diamonds')
# Создадим строгую мультиколлинеарность
X = diamonds[['price', 'x', 'y']]
X['sum_xy'] = X['x'] + X['y']
model1 = smf.ols(data = X, formula='price ~ x + y + sum_xy').fit()
# Слегка изменим данные
X1 = X.copy()
X1['x'] = X1['x'] + 0.001
model2 = smf.ols(data = X1, formula='price ~ x + y + sum_xy').fit()
# Посмотрим на разницу между коэффициентами
print(model1.params)
print(model2.params)
print('Суммарная квадратичная разность: %f' % (np.sum(np.square(model1.params - model2.params))))
<ipython-input-80-5cb0f7e5d949>:3: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
X['sum_xy'] = X['x'] + X['y']
Intercept -14105.076664
x 1863.157850
y -814.091949
sum_xy 1049.065901
dtype: float64
Intercept -2.326418e+11
x 2.326418e+14
y 2.326418e+14
sum_xy -2.326418e+14
dtype: float64
Суммарная квадратичная разность: 162366708248113373337532497920.000000
# Для сравнения: модель без мультиколлинеарности
Z = diamonds[['price', 'x', 'y']]
model3 = smf.ols(data = Z, formula='price ~ x + y').fit()
# Слегка изменим данные
Z1 = Z.copy()
Z1['x'] = Z1['x'] + 0.001
model4= smf.ols(data = Z1, formula='price ~ x + y').fit()
# Посмотрим на разницу между коэффициентами
print(model3.params)
print(model4.params)
print('Суммарная квадратичная разность: %f' % (np.sum(np.square(model3.params - model4.params))))
Intercept -14105.076664
x 2912.223752
y 234.973952
dtype: float64
Intercept -14107.988887
x 2912.223752
y 234.973952
dtype: float64
Суммарная квадратичная разность: 8.481047
# Исследуем ковариационную матрицу коэффициентов
model2.summary()
Dep. Variable: | price | R-squared: | 0.782 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.782 |
Method: | Least Squares | F-statistic: | 6.462e+04 |
Date: | Mon, 16 Nov 2020 | Prob (F-statistic): | 0.00 |
Time: | 22:59:20 | Log-Likelihood: | -4.8265e+05 |
No. Observations: | 53940 | AIC: | 9.653e+05 |
Df Residuals: | 53936 | BIC: | 9.653e+05 |
Df Model: | 3 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | -2.326e+11 | 2.87e+11 | -0.811 | 0.418 | -7.95e+11 | 3.3e+11 |
x | 2.326e+14 | 2.87e+14 | 0.811 | 0.418 | -3.3e+14 | 7.95e+14 |
y | 2.326e+14 | 2.87e+14 | 0.811 | 0.418 | -3.3e+14 | 7.95e+14 |
sum_xy | -2.326e+14 | 2.87e+14 | -0.811 | 0.418 | -7.95e+14 | 3.3e+14 |
Omnibus: | 18702.724 | Durbin-Watson: | 0.442 |
---|---|---|---|
Prob(Omnibus): | 0.000 | Jarque-Bera (JB): | 143426.007 |
Skew: | 1.464 | Prob(JB): | 0.00 |
Kurtosis: | 10.433 | Cond. No. | 8.90e+14 |
Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The smallest eigenvalue is 1.4e-23. This might indicate that there are
strong multicollinearity problems or that the design matrix is singular.
Индикаторы мультиколлинеарности.¶
Очевидный: посмотреть на \(\mathrm{scorr}(X)\):
np.corrcoef(X1.T)
array([[1. , 0.88443516, 0.8654209 , 0.88042812],
[0.88443516, 1. , 0.97470148, 0.99354016],
[0.8654209 , 0.97470148, 1. , 0.99376929],
[0.88042812, 0.99354016, 0.99376929, 1. ]])
Condition number.
Напоминание: \(\|\cdot\|\) – это норма матрицы, если
\(\|A\| \geq 0\), и если \(A = 0\), то \(\|A\| = 0\).
\(\|a A\| = |a| \|A\|\)
\(\|A+B\| \leq \|A\| + \|B\|\)
Некоторые матричные нормы также удовлетворяют свойству субмультипликативности: \(\Vert A B \Vert \leq \Vert A \Vert \Vert B \Vert\).
Condtition number определяется как \(\mathrm{K}(A) = \Vert A \Vert \Vert A^{-1} \Vert\). Можно показать, что в случае \(l_2\)-нормы для симметричной положительно определённой матрицы \(A\) верно:
np.linalg.cond(X.T @ X)
8.396003693050826e+18
Variance Inflation Factor (VIF).
где \(R^2_j\) – коэффициент детерминации в регрессии \(X_j\) на остальные факторы.
Если VIF превышает некоторое пороговое значение (5/10/12\(\ldots\)), то \(X_j\) сильно коллинеарен прочим регрессорам.
from statsmodels.stats.outliers_influence import variance_inflation_factor
variance_inflation_factor(model.model.exog, 1)
inf
variance_inflation_factor(model.model.exog, 2)
inf
variance_inflation_factor(model.model.exog, 3)
inf
Борьба с мультиколлинеарностью.¶
Ничего не делать: оценки остаются несмещёнными и эффективными среди линейных по \(y\) несмещённых оценок.
Выбросить часть регрессоров. В этом случае жертвуем несмещённостью оставшихся.
Регуляризация. В этом случае жертвуем несмещённостью коэффициентов.
Иногда – PCA.
Получить больше наблюдений.
II. Отбор регрессоров.¶
Метод последовательного исключения:
Оценить модель.
Выбросить наиболее незначимый регрессор.
Переоценить модель.
Повторять шаги B-C до тех пор, пока все регрессоры не станут значимыми.
Метод последовательного включения:
Оценить регрессию \(y\) на каждый из \(X_1\), \(\ldots\), \(X_k\). Выбрать самый значимый: \(X_q\).
Оценить регерссию \(y\) на \(X_q\) и на каждый из \(X_1\), \(\ldots\), \(X_{q-1}\), \(X_{q+1}\), \(\ldots\), \(X_k\). Выбрать сымый значимый.
Повторять до тех пор, пока среди включаемых коэффициентов не останется незначимых.
Если есть \(k\) регрессоров и хотим узнать, нужно ли включать все или можно включить \(m<k\) регрессоров: F-тест (см. позапрошлый семинар).
Если есть \(k\) регрессоров и хотим узнать, не нужно ли было включить ещё какие-то переменные, которых у нас нет: RESET-тест Рамсея.
Схема:
Оценить регрессию \(\hat{y} = X\hat{\beta}\).
Оценить регрессию \(y_i = \beta_0 + \beta_1X_1 + \ldots + \beta_kX_k + a_1\hat{y}_i^2 + \ldots + a_m\hat{y}_i^m + u_i\).
Проверить гипотезу $\( \begin{cases} H_0: \forall i : a_i = 0, \\ H_1: \sum_i a_i^2 > 0, \end{cases} \)$
например, при помощи F-теста.
from statsmodels.stats.diagnostic import linear_reset
model = smf.ols('np.log(price) ~ depth + table + x + y + z', data = diamonds).fit()
linear_reset(model, use_f = True)
# H0 отвергается на 5% уровне значимости!
<class 'statsmodels.stats.contrast.ContrastResults'>
<F test: F=array([[7697.00812222]]), p=0.0, df_denom=5.39e+04, df_num=2>