Собесов

Сценарий: гетероскедастичность — Breusch-Pagan и как лечить

Статистика и теорверRegressionСредняяMiddle

Условие

В регрессии revenue ~ age + tenure residuals растут с возрастом юзера. Гетероскедастичность? Что делать?

Решение

Что это

Гомоскедастичность: Var(ε|X) = σ² — постоянна. Гетероскедастичность: дисперсия остатков зависит от X.

Типичные паттерны:

  • Cone/fan shape в residual plot (разлёт растёт с fitted value).
  • Размер дисперсии масштабируется с уровнем переменной (выручка у больших клиентов колеблется больше).

Последствия

  • β оценки unbiased — но не efficient.
  • SE неверны — обычно занижены → inflate significance.
  • t-tests / CIs неточны.

Тесты

import statsmodels.api as sm
from statsmodels.stats.diagnostic import het_breuschpagan, het_white
 
X = sm.add_constant(df[['age', 'tenure']])
model = sm.OLS(df['revenue'], X).fit()
 
# Breusch-Pagan
lm, p, _, _ = het_breuschpagan(model.resid, X)
print(f'Breusch-Pagan p={p:.4f}')   # < 0.05 → heteroscedasticity
 
# White's test (более общий, но менее мощный)
lm, p, _, _ = het_white(model.resid, X)

Визуальная проверка

import matplotlib.pyplot as plt
plt.scatter(model.fittedvalues, model.resid)
plt.axhline(0)

Cone shape — heteroscedasticity.

Лечение

1. Robust standard errors (самое простое):

model_robust = sm.OLS(y, X).fit(cov_type='HC3')   # White's robust SE

Не меняет β, но даёт корректные SE. Часто достаточно.

2. Log-transform Y:

df['log_revenue'] = np.log1p(df['revenue'])
model = sm.OLS(df['log_revenue'], X).fit()

Часто стабилизирует variance.

3. Weighted Least Squares:

weights = 1 / df['age']   # если variance ~ age
model = sm.WLS(y, X, weights=weights).fit()

4. Generalized Linear Models (GLM):

Для positive continuous Y — Gamma regression with log link.

import statsmodels.api as sm
model = sm.GLM(y, X, family=sm.families.Gamma(link=sm.families.links.log())).fit()

HC0, HC1, HC2, HC3

Различные robust SE формулы:

  • HC0 — оригинальный White.
  • HC1 — корректировка для малых n.
  • HC3 — для очень малых n или leverage точек, рекомендуется.

Кластеризация

Если данные группируются (юзеры одной компании), residuals скоррелированы внутри кластера. Cluster-robust SE:

model = sm.OLS(y, X).fit(cov_type='cluster', cov_kwds={'groups': df['company_id']})

Подводные камни

  1. Breusch-Pagan на больших n находит малые отклонения — не всё значимое требует лечения.
  2. Robust SE решает inference, но не efficiency. Если данные сильно гетерос — WLS даёт лучшие оценки.
  3. Log(Y) меняет интерпретацию: β — это % изменение Y, не absolute.
  4. Cluster SE требует много кластеров (>20-30) для корректности.
  5. Heteroscedasticity часто следствие model misspecification (omitted variable, non-linearity), не «свойство данных».

Эталонный ответ

Breusch-Pagan p<0.05 → heteroscedasticity. Лечение: HC3 robust SE (самое простое), log(Y) transform, WLS с весами 1/variance, или GLM. β unbiased, но SE врут — robust SE достаточно для inference.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти