Heteroscedasticity: residual variance depends on predictor values (Var(ε|X)=σ^2(X)), violating OLS assumption of constant variance. Consequence: OLS coefficients remain unbiased but standard errors (and thus t/Z-tests, CIs) are invalid — leading to misleading inference.Detection — graphical:- Residuals vs fitted plot: look for “funnel” or pattern (increasing/decreasing spread). Also plot sqrt(|residuals|) vs fitted or vs key predictors.- Scale-Location plot (spread ~ fitted) and residuals vs individual predictors help locate which X relates to variance.Formal tests:- Breusch–Pagan: regress squared residuals on original X (or subset). Null: homoscedasticity. Sensitive to linear variance patterns.- White test: regress squared residuals on X, X^2 and cross-products (flexible for non-linear heteroscedasticity). Null: homoscedasticity. More general but can lose power with many regressors.- Goldfeld–Quandt: splits data by suspected variance-driving variable.Report p-values and complementary visual checks.Remedies:- Transform response (log, Box–Cox): can stabilize variance and sometimes linearize relationships. Use when transformation is interpretable.- Weighted Least Squares (WLS): weight by 1/Var(ε|X) estimate (or proxy), gives efficient, unbiased estimates with correct SEs if weights correct. Iteratively reweighted least squares if variance function unknown.- Robust (heteroscedasticity-consistent) standard errors: e.g., HC0–HC4 (sandwich estimators). Easy to apply (preserves OLS point estimates; fixes inference). Use when you trust model form but not constant variance.- Model variance explicitly (e.g., GLS, mixed models) or use bootstrap for SEs.Python sketch — White test (statsmodels):python
import statsmodels.api as sm
import statsmodels.formula.api as smf
from statsmodels.stats.diagnostic import het_white, het_breuschpagan
# fit OLS
model = smf.ols('y ~ x1 + x2', data=df).fit()
# Breusch-Pagan
bp_test = het_breuschpagan(model.resid, model.model.exog)
# returns (lm_stat, lm_pvalue, f_stat, f_pvalue)
# White test
white_test = het_white(model.resid, model.model.exog)
# returns (lm_stat, lm_pvalue, f_stat, f_pvalue)
print('BP p:', bp_test[1], 'White p:', white_test[1])
# Robust SEs example
robust_model = model.get_robustcov_results(cov_type='HC3')
print(robust_model.summary())
Practical advice: always combine plots and tests; if inference is primary and model form is plausible, prefer WLS/GLS or robust SEs; if prediction matters, transformations or models that directly model variance often help.