Skill Tracer
Skills › Scientific Agent Skills › statsmodels › linear_models.md

linear_models.md

451 lines · skills/statsmodels/references/linear_models.md at 5f2bc03

Open on GitHub
1# Linear Regression Models Reference
2
3This document provides detailed guidance on linear regression models in statsmodels, including OLS, GLS, WLS, quantile regression, and specialized variants.
4

Linear Regression Models Reference

This document provides detailed guidance on linear regression models in statsmodels, including OLS, GLS, WLS, quantile regression, and specialized variants.

Raw lines 1 to 4
5## Core Model Classes
6

Core Model Classes

Raw lines 5 to 6
7### OLS (Ordinary Least Squares)
8
9OLS estimates a linear conditional mean. Conventional covariance assumes homoscedastic, uncorrelated errors; normal errors support exact small-sample t/F inference. Robust covariance changes uncertainty, not an omitted-variable or endogeneity bias.
11**When to use:**
12- Standard regression analysis
13- Errors are independent and have constant variance
14- No autocorrelation or heteroscedasticity
15- Most common starting point
17**Basic usage:**
18```python
19import statsmodels.api as sm
20import numpy as np
22# Array interface: add a constant if an intercept is intended
23X = sm.add_constant(X_data) # Adds column of 1s for intercept
25# Fit model
26model = sm.OLS(y, X)
27results = model.fit()
29# View results
30print(results.summary())
31```
33**Key results attributes:**
34```python
35results.params # Coefficients
36results.bse # Standard errors
37results.tvalues # T-statistics
38results.pvalues # P-values
39results.rsquared # R-squared
40results.rsquared_adj # Adjusted R-squared
41results.fittedvalues # Fitted values (predictions on training data)
42results.resid # Residuals
43results.conf_int() # Confidence intervals for parameters
44```
46**Prediction with confidence/prediction intervals:**
47```python
48# For in-sample predictions
49pred = results.get_prediction(X)
50pred_summary = pred.summary_frame()
51print(pred_summary) # Contains mean, std, confidence intervals
53# For out-of-sample predictions
54X_new = sm.add_constant(X_new_data, has_constant="add")
55pred_new = results.get_prediction(X_new)
56pred_summary = pred_new.summary_frame()
58# Access intervals
59mean_ci_lower = pred_summary["mean_ci_lower"]
60mean_ci_upper = pred_summary["mean_ci_upper"]
61obs_ci_lower = pred_summary["obs_ci_lower"] # Prediction intervals
62obs_ci_upper = pred_summary["obs_ci_upper"]
63```
65**Formula API (R-style):**
66```python
67import statsmodels.formula.api as smf
69# Automatic handling of categorical variables and interactions
70formula = 'y ~ x1 + x2 + C(category) + x1:x2'
71results = smf.ols(formula, data=df).fit()
72```

OLS (Ordinary Least Squares)

OLS estimates a linear conditional mean. Conventional covariance assumes homoscedastic, uncorrelated errors; normal errors support exact small-sample t/F inference. Robust covariance changes uncertainty, not an omitted-variable or endogeneity bias.

When to use:

  • Standard regression analysis
  • Errors are independent and have constant variance
  • No autocorrelation or heteroscedasticity
  • Most common starting point

Basic usage:

import statsmodels.api as sm
import numpy as np

# Array interface: add a constant if an intercept is intended
X = sm.add_constant(X_data)  # Adds column of 1s for intercept

# Fit model
model = sm.OLS(y, X)
results = model.fit()

# View results
print(results.summary())

Key results attributes:

results.params           # Coefficients
results.bse              # Standard errors
results.tvalues          # T-statistics
results.pvalues          # P-values
results.rsquared         # R-squared
results.rsquared_adj     # Adjusted R-squared
results.fittedvalues     # Fitted values (predictions on training data)
results.resid            # Residuals
results.conf_int()       # Confidence intervals for parameters

Prediction with confidence/prediction intervals:

# For in-sample predictions
pred = results.get_prediction(X)
pred_summary = pred.summary_frame()
print(pred_summary)  # Contains mean, std, confidence intervals

# For out-of-sample predictions
X_new = sm.add_constant(X_new_data, has_constant="add")
pred_new = results.get_prediction(X_new)
pred_summary = pred_new.summary_frame()

# Access intervals
mean_ci_lower = pred_summary["mean_ci_lower"]
mean_ci_upper = pred_summary["mean_ci_upper"]
obs_ci_lower = pred_summary["obs_ci_lower"]  # Prediction intervals
obs_ci_upper = pred_summary["obs_ci_upper"]

Formula API (R-style):

import statsmodels.formula.api as smf

# Automatic handling of categorical variables and interactions
formula = 'y ~ x1 + x2 + C(category) + x1:x2'
results = smf.ols(formula, data=df).fit()
Raw lines 7 to 73
74### WLS (Weighted Least Squares)
76Handles heteroscedastic errors (diagonal Σ) where variance differs across observations.
78**When to use:**
79- Known heteroscedasticity (non-constant error variance)
80- Different observations have different reliability
81- Weights are known or can be estimated
83**Usage:**
84```python
85# If you know the weights (inverse variance)
86weights = 1 / error_variance
87model = sm.WLS(y, X, weights=weights)
88results = model.fit()
90# Common weight patterns:
91# - 1/variance: when variance is known
92# - n_i: sample size for grouped data
93# - 1/x: when variance proportional to x
94```
96**Feasible WLS (estimating weights):**
97```python
98# Step 1: Fit OLS
99ols_results = sm.OLS(y, X).fit()
101# Step 2: Model squared residuals to estimate variance
102abs_resid = np.abs(ols_results.resid)
103variance_model = sm.OLS(np.log(np.maximum(abs_resid**2, np.finfo(float).tiny)), X).fit()
105# Step 3: Use estimated variance as weights
106weights = 1 / np.exp(variance_model.fittedvalues)
107wls_results = sm.WLS(y, X, weights=weights).fit()
108```

WLS (Weighted Least Squares)

Handles heteroscedastic errors (diagonal Σ) where variance differs across observations.

When to use:

  • Known heteroscedasticity (non-constant error variance)
  • Different observations have different reliability
  • Weights are known or can be estimated

Usage:

# If you know the weights (inverse variance)
weights = 1 / error_variance
model = sm.WLS(y, X, weights=weights)
results = model.fit()

# Common weight patterns:
# - 1/variance: when variance is known
# - n_i: sample size for grouped data
# - 1/x: when variance proportional to x

Feasible WLS (estimating weights):

# Step 1: Fit OLS
ols_results = sm.OLS(y, X).fit()

# Step 2: Model squared residuals to estimate variance
abs_resid = np.abs(ols_results.resid)
variance_model = sm.OLS(np.log(np.maximum(abs_resid**2, np.finfo(float).tiny)), X).fit()

# Step 3: Use estimated variance as weights
weights = 1 / np.exp(variance_model.fittedvalues)
wls_results = sm.WLS(y, X, weights=weights).fit()
Raw lines 74 to 109
110### GLS (Generalized Least Squares)
112Handles arbitrary covariance structure (Σ). Superclass for other regression methods.
114**When to use:**
115- Known covariance structure
116- Correlated errors
117- More general than WLS
119**Usage:**
120```python
121# Specify covariance structure
122# Sigma should be (n x n) covariance matrix
123model = sm.GLS(y, X, sigma=Sigma)
124results = model.fit()
125```

GLS (Generalized Least Squares)

Handles arbitrary covariance structure (Σ). Superclass for other regression methods.

When to use:

  • Known covariance structure
  • Correlated errors
  • More general than WLS

Usage:

# Specify covariance structure
# Sigma should be (n x n) covariance matrix
model = sm.GLS(y, X, sigma=Sigma)
results = model.fit()
Raw lines 110 to 126
127### GLSAR (GLS with Autoregressive Errors)
129Feasible generalized least squares with AR(p) errors for time series data.
131**When to use:**
132- Time series regression with autocorrelated errors
133- Need to account for serial correlation
134- Violations of error independence
136**Usage:**
137```python
138# AR(1) errors
139model = sm.GLSAR(y, X, rho=1) # rho=1 for AR(1), rho=2 for AR(2), etc.
140results = model.iterative_fit(maxiter=20) # Default maxiter=3 may be insufficient
142print(results.summary())
143print(f"Estimated rho: {results.model.rho}")
144```

GLSAR (GLS with Autoregressive Errors)

Feasible generalized least squares with AR(p) errors for time series data.

When to use:

  • Time series regression with autocorrelated errors
  • Need to account for serial correlation
  • Violations of error independence

Usage:

# AR(1) errors
model = sm.GLSAR(y, X, rho=1)  # rho=1 for AR(1), rho=2 for AR(2), etc.
results = model.iterative_fit(maxiter=20)  # Default maxiter=3 may be insufficient

print(results.summary())
print(f"Estimated rho: {results.model.rho}")
Raw lines 127 to 145
146### RLS (Recursive Least Squares)
148Expanding-window estimates of a fixed-coefficient regression, plus stability diagnostics; this is not a stochastic time-varying-coefficient model.
150**When to use:**
151- Assess coefficient stability as observations accumulate
152- Inspect recursive residuals and CUSUM
153- Refit with additional data; do not assume a streaming update interface
155**Usage:**
156```python
157from statsmodels.regression.recursive_ls import RecursiveLS
159model = RecursiveLS(y, X)
160results = model.fit()
162# Expanding-window filtered/smoothed coefficient estimates
163params_over_time = results.recursive_coefficients.filtered
164cusum = results.cusum # CUSUM statistic for structural breaks
165```

RLS (Recursive Least Squares)

Expanding-window estimates of a fixed-coefficient regression, plus stability diagnostics; this is not a stochastic time-varying-coefficient model.

When to use:

  • Assess coefficient stability as observations accumulate
  • Inspect recursive residuals and CUSUM
  • Refit with additional data; do not assume a streaming update interface

Usage:

from statsmodels.regression.recursive_ls import RecursiveLS

model = RecursiveLS(y, X)
results = model.fit()

# Expanding-window filtered/smoothed coefficient estimates
params_over_time = results.recursive_coefficients.filtered
cusum = results.cusum  # CUSUM statistic for structural breaks
Raw lines 146 to 166
167### Rolling Regressions
169Compute estimates across moving windows for time-varying parameter detection.
171**When to use:**
172- Parameters vary over time
173- Want to detect structural changes
174- Time series with evolving relationships
176**Usage:**
177```python
178from statsmodels.regression.rolling import RollingOLS, RollingWLS
180# Rolling OLS with 60-period window
181rolling_model = RollingOLS(y, X, window=60)
182rolling_results = rolling_model.fit()
184# Extract time-varying parameters
185rolling_params = pd.DataFrame(rolling_results.params) # Also handles NumPy inputs
186rolling_rsquared = rolling_results.rsquared
188# Plot parameter evolution
189import matplotlib.pyplot as plt
190rolling_params.plot()
191plt.title('Time-Varying Coefficients')
192plt.show()
193```

Rolling Regressions

Compute estimates across moving windows for time-varying parameter detection.

When to use:

  • Parameters vary over time
  • Want to detect structural changes
  • Time series with evolving relationships

Usage:

from statsmodels.regression.rolling import RollingOLS, RollingWLS

# Rolling OLS with 60-period window
rolling_model = RollingOLS(y, X, window=60)
rolling_results = rolling_model.fit()

# Extract time-varying parameters
rolling_params = pd.DataFrame(rolling_results.params)  # Also handles NumPy inputs
rolling_rsquared = rolling_results.rsquared

# Plot parameter evolution
import matplotlib.pyplot as plt
rolling_params.plot()
plt.title('Time-Varying Coefficients')
plt.show()
Raw lines 167 to 194
195### Quantile Regression
197Analyzes conditional quantiles rather than conditional mean.
199**When to use:**
200- Interest in quantiles (median, 90th percentile, etc.)
201- Robust to outliers (median regression)
202- Distributional effects across quantiles
203- Heterogeneous effects
205**Usage:**
206```python
207from statsmodels.regression.quantile_regression import QuantReg
209# Median regression (50th percentile)
210model = QuantReg(y, X)
211results_median = model.fit(q=0.5)
213# Multiple quantiles
214quantiles = [0.1, 0.25, 0.5, 0.75, 0.9]
215results_dict = {}
216for q in quantiles:
217 results_dict[q] = model.fit(q=q)
219# Plot quantile-varying effects
220import matplotlib.pyplot as plt
221coef_dict = {q: res.params for q, res in results_dict.items()}
222coef_df = pd.DataFrame(coef_dict).T
223coef_df.plot()
224plt.xlabel('Quantile')
225plt.ylabel('Coefficient')
226plt.show()
227```

Quantile Regression

Analyzes conditional quantiles rather than conditional mean.

When to use:

  • Interest in quantiles (median, 90th percentile, etc.)
  • Robust to outliers (median regression)
  • Distributional effects across quantiles
  • Heterogeneous effects

Usage:

from statsmodels.regression.quantile_regression import QuantReg

# Median regression (50th percentile)
model = QuantReg(y, X)
results_median = model.fit(q=0.5)

# Multiple quantiles
quantiles = [0.1, 0.25, 0.5, 0.75, 0.9]
results_dict = {}
for q in quantiles:
    results_dict[q] = model.fit(q=q)

# Plot quantile-varying effects
import matplotlib.pyplot as plt
coef_dict = {q: res.params for q, res in results_dict.items()}
coef_df = pd.DataFrame(coef_dict).T
coef_df.plot()
plt.xlabel('Quantile')
plt.ylabel('Coefficient')
plt.show()
Raw lines 195 to 228
229## Mixed Effects Models
231For hierarchical/nested data with random effects.
233**When to use:**
234- Clustered/grouped data (students in schools, patients in hospitals)
235- Repeated measures
236- Need random effects to account for grouping
238**Usage:**
239```python
240from statsmodels.regression.mixed_linear_model import MixedLM
242# Random intercept model
243model = MixedLM(y, X, groups=group_ids)
244results = model.fit()
246# Random intercept and slope
247model = MixedLM(y, X, groups=group_ids, exog_re=X_random)
248results = model.fit()
250print(results.summary())
251```
253Use ML (`fit(reml=False)`) when comparing mixed models with different fixed effects; REML likelihoods are not comparable across those designs. Check convergence, singular random-effect covariance and boundary variance estimates before inference.

Mixed Effects Models

For hierarchical/nested data with random effects.

When to use:

  • Clustered/grouped data (students in schools, patients in hospitals)
  • Repeated measures
  • Need random effects to account for grouping

Usage:

from statsmodels.regression.mixed_linear_model import MixedLM

# Random intercept model
model = MixedLM(y, X, groups=group_ids)
results = model.fit()

# Random intercept and slope
model = MixedLM(y, X, groups=group_ids, exog_re=X_random)
results = model.fit()

print(results.summary())

Use ML (fit(reml=False)) when comparing mixed models with different fixed effects; REML likelihoods are not comparable across those designs. Check convergence, singular random-effect covariance and boundary variance estimates before inference.

Raw lines 229 to 254
255## Diagnostics and Model Assessment
257The following diagnostics use an OLS result with its original full-rank design. VIF, Cook's distance and condition-number thresholds are screening heuristics, not automatic exclusion rules; conditioning also depends on predictor units. Robust SEs do not validate classical OLS observation intervals under heteroskedasticity.

Diagnostics and Model Assessment

The following diagnostics use an OLS result with its original full-rank design. VIF, Cook's distance and condition-number thresholds are screening heuristics, not automatic exclusion rules; conditioning also depends on predictor units. Robust SEs do not validate classical OLS observation intervals under heteroskedasticity.

Raw lines 255 to 258
259### Residual Analysis
261```python
262# Basic residual plots
263import matplotlib.pyplot as plt
265# Residuals vs fitted
266plt.scatter(results.fittedvalues, results.resid)
267plt.xlabel('Fitted values')
268plt.ylabel('Residuals')
269plt.axhline(y=0, color='r', linestyle='--')
270plt.title('Residuals vs Fitted')
271plt.show()
273# Q-Q plot for normality
274from statsmodels.graphics.gofplots import qqplot
275qqplot(results.resid, line='s')
276plt.show()
278# Histogram of residuals
279plt.hist(results.resid, bins=30, edgecolor='black')
280plt.xlabel('Residuals')
281plt.ylabel('Frequency')
282plt.title('Distribution of Residuals')
283plt.show()
284```

Residual Analysis

# Basic residual plots
import matplotlib.pyplot as plt

# Residuals vs fitted
plt.scatter(results.fittedvalues, results.resid)
plt.xlabel('Fitted values')
plt.ylabel('Residuals')
plt.axhline(y=0, color='r', linestyle='--')
plt.title('Residuals vs Fitted')
plt.show()

# Q-Q plot for normality
from statsmodels.graphics.gofplots import qqplot
qqplot(results.resid, line='s')
plt.show()

# Histogram of residuals
plt.hist(results.resid, bins=30, edgecolor='black')
plt.xlabel('Residuals')
plt.ylabel('Frequency')
plt.title('Distribution of Residuals')
plt.show()
Raw lines 259 to 285
286### Specification Tests
288```python
289from statsmodels.stats.diagnostic import het_breuschpagan, het_white
290from statsmodels.stats.stattools import durbin_watson, jarque_bera
292# Heteroscedasticity tests
293lm_stat, lm_pval, f_stat, f_pval = het_breuschpagan(results.resid, X)
294print(f"Breusch-Pagan test p-value: {lm_pval}")
296# White test
297white_test = het_white(results.resid, X)
298print(f"White test p-value: {white_test[1]}")
300# Autocorrelation
301dw_stat = durbin_watson(results.resid)
302print(f"Durbin-Watson statistic: {dw_stat}")
303# DW ~ 2 indicates no autocorrelation
304# DW < 2 suggests positive autocorrelation
305# DW > 2 suggests negative autocorrelation
307# Normality test
308jb_stat, jb_pval, skew, kurtosis = jarque_bera(results.resid)
309print(f"Jarque-Bera test p-value: {jb_pval}")
310```

Specification Tests

from statsmodels.stats.diagnostic import het_breuschpagan, het_white
from statsmodels.stats.stattools import durbin_watson, jarque_bera

# Heteroscedasticity tests
lm_stat, lm_pval, f_stat, f_pval = het_breuschpagan(results.resid, X)
print(f"Breusch-Pagan test p-value: {lm_pval}")

# White test
white_test = het_white(results.resid, X)
print(f"White test p-value: {white_test[1]}")

# Autocorrelation
dw_stat = durbin_watson(results.resid)
print(f"Durbin-Watson statistic: {dw_stat}")
# DW ~ 2 indicates no autocorrelation
# DW < 2 suggests positive autocorrelation
# DW > 2 suggests negative autocorrelation

# Normality test
jb_stat, jb_pval, skew, kurtosis = jarque_bera(results.resid)
print(f"Jarque-Bera test p-value: {jb_pval}")
Raw lines 286 to 311
312### Multicollinearity
314```python
315from statsmodels.stats.outliers_influence import variance_inflation_factor
317# Calculate VIF for each variable
318vif_data = pd.DataFrame()
319vif_data["Variable"] = X.columns
320vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
322print(vif_data)
323# VIF > 10 indicates problematic multicollinearity
324# VIF > 5 suggests moderate multicollinearity
326# Condition number (from summary)
327print(f"Condition number: {results.condition_number}")
328# Condition number > 20 suggests multicollinearity
329# Condition number > 30 indicates serious problems
330```

Multicollinearity

from statsmodels.stats.outliers_influence import variance_inflation_factor

# Calculate VIF for each variable
vif_data = pd.DataFrame()
vif_data["Variable"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]

print(vif_data)
# VIF > 10 indicates problematic multicollinearity
# VIF > 5 suggests moderate multicollinearity

# Condition number (from summary)
print(f"Condition number: {results.condition_number}")
# Condition number > 20 suggests multicollinearity
# Condition number > 30 indicates serious problems
Raw lines 312 to 331
332### Influence Statistics
334```python
335from statsmodels.stats.outliers_influence import OLSInfluence
337influence = results.get_influence()
339# Leverage (hat values)
340leverage = influence.hat_matrix_diag
341# High leverage: > 2*p/n (p=predictors, n=observations)
343# Cook's distance
344cooks_d = influence.cooks_distance[0]
345# Influential if Cook's D > 4/n
347# DFFITS
348dffits = influence.dffits[0]
349# Influential if |DFFITS| > 2*sqrt(p/n)
351# Create influence plot
352from statsmodels.graphics.regressionplots import influence_plot
353fig, ax = plt.subplots(figsize=(12, 8))
354influence_plot(results, ax=ax)
355plt.show()
356```

Influence Statistics

from statsmodels.stats.outliers_influence import OLSInfluence

influence = results.get_influence()

# Leverage (hat values)
leverage = influence.hat_matrix_diag
# High leverage: > 2*p/n (p=predictors, n=observations)

# Cook's distance
cooks_d = influence.cooks_distance[0]
# Influential if Cook's D > 4/n

# DFFITS
dffits = influence.dffits[0]
# Influential if |DFFITS| > 2*sqrt(p/n)

# Create influence plot
from statsmodels.graphics.regressionplots import influence_plot
fig, ax = plt.subplots(figsize=(12, 8))
influence_plot(results, ax=ax)
plt.show()
Raw lines 332 to 357
358### Hypothesis Testing
360```python
361# Test single coefficient
362# H0: beta_i = 0 (automatically in summary)
364# Test multiple restrictions using F-test
365# Example: Test beta_1 = beta_2 = 0
366R = [[0, 1, 0, 0], [0, 0, 1, 0]] # Restriction matrix
367f_test = results.f_test(R)
368print(f_test)
370# Formula-based hypothesis testing
371f_test = results.f_test("x1 = x2 = 0")
372print(f_test)
374# Test linear combination: beta_1 + beta_2 = 1
375r_matrix = [[0, 1, 1, 0]]
376q_matrix = [1] # RHS value
377f_test = results.f_test((r_matrix, q_matrix))
378print(f_test)
380# Wald test (equivalent to F-test for linear restrictions)
381wald_test = results.wald_test(R)
382print(wald_test)
383```

Hypothesis Testing

# Test single coefficient
# H0: beta_i = 0 (automatically in summary)

# Test multiple restrictions using F-test
# Example: Test beta_1 = beta_2 = 0
R = [[0, 1, 0, 0], [0, 0, 1, 0]]  # Restriction matrix
f_test = results.f_test(R)
print(f_test)

# Formula-based hypothesis testing
f_test = results.f_test("x1 = x2 = 0")
print(f_test)

# Test linear combination: beta_1 + beta_2 = 1
r_matrix = [[0, 1, 1, 0]]
q_matrix = [1]  # RHS value
f_test = results.f_test((r_matrix, q_matrix))
print(f_test)

# Wald test (equivalent to F-test for linear restrictions)
wald_test = results.wald_test(R)
print(wald_test)
Raw lines 358 to 384
385## Model Comparison
387```python
388# Classical nested OLS F-test: same observations, larger model second
389from statsmodels.stats.anova import anova_lm
391# Fit restricted and unrestricted models
392model_restricted = sm.OLS(y, X_restricted).fit()
393model_full = sm.OLS(y, X_full).fit()
395# ANOVA table for model comparison
396anova_results = anova_lm(model_restricted, model_full)
397print(anova_results)
399# AIC/BIC for non-nested model comparison
400print(f"Model 1 AIC: {model1.aic}, BIC: {model1.bic}")
401print(f"Model 2 AIC: {model2.aic}, BIC: {model2.bic}")
402# Lower AIC/BIC indicates better model
403```

Model Comparison

# Classical nested OLS F-test: same observations, larger model second
from statsmodels.stats.anova import anova_lm

# Fit restricted and unrestricted models
model_restricted = sm.OLS(y, X_restricted).fit()
model_full = sm.OLS(y, X_full).fit()

# ANOVA table for model comparison
anova_results = anova_lm(model_restricted, model_full)
print(anova_results)

# AIC/BIC for non-nested model comparison
print(f"Model 1 AIC: {model1.aic}, BIC: {model1.bic}")
print(f"Model 2 AIC: {model2.aic}, BIC: {model2.bic}")
# Lower AIC/BIC indicates better model
Raw lines 385 to 404
405## Robust Standard Errors
407Handle heteroscedasticity or clustering without reweighting.
409```python
410# Heteroscedasticity-robust (HC) standard errors
411results_hc = results.get_robustcov_results(cov_type='HC0') # White's
412results_hc1 = results.get_robustcov_results(cov_type='HC1')
413results_hc2 = results.get_robustcov_results(cov_type='HC2')
414results_hc3 = results.get_robustcov_results(cov_type='HC3') # Leverage-adjusted
416# Newey-West HAC (Heteroscedasticity and Autocorrelation Consistent)
417results_hac = results.get_robustcov_results(cov_type='HAC', maxlags=4)
419# Cluster-robust standard errors
420results_cluster = results.get_robustcov_results(cov_type='cluster',
421 groups=cluster_ids)
423# View robust results
424print(results_hc3.summary())
425```

Robust Standard Errors

Handle heteroscedasticity or clustering without reweighting.

# Heteroscedasticity-robust (HC) standard errors
results_hc = results.get_robustcov_results(cov_type='HC0')  # White's
results_hc1 = results.get_robustcov_results(cov_type='HC1')
results_hc2 = results.get_robustcov_results(cov_type='HC2')
results_hc3 = results.get_robustcov_results(cov_type='HC3')  # Leverage-adjusted

# Newey-West HAC (Heteroscedasticity and Autocorrelation Consistent)
results_hac = results.get_robustcov_results(cov_type='HAC', maxlags=4)

# Cluster-robust standard errors
results_cluster = results.get_robustcov_results(cov_type='cluster',
                                                groups=cluster_ids)

# View robust results
print(results_hc3.summary())
Raw lines 405 to 426
427## Best Practices
4291. **Specify intercept once**: Array models need it explicitly; formulas include it by default. Keep prediction columns in the training order.
4302. **Check assumptions**: Run diagnostic tests (heteroscedasticity, autocorrelation, normality)
4313. **Use formula API for categorical variables**: `smf.ols()` handles categorical variables automatically
4324. **Robust standard errors**: Use when heteroscedasticity detected but model specification is correct
4335. **Model selection**: Use AIC/BIC for non-nested models, F-test/likelihood ratio for nested models
4346. **Outliers and influence**: Always check Cook's distance and leverage
4357. **Multicollinearity**: Check VIF and condition number before interpretation
4368. **Time series**: Use `GLSAR` or robust HAC standard errors for autocorrelated errors
4379. **Grouped data**: Consider mixed effects models or cluster-robust standard errors
43810. **Quantile regression**: Use for robust estimation or when interested in distributional effects

Best Practices

  1. Specify intercept once: Array models need it explicitly; formulas include it by default. Keep prediction columns in the training order.
  2. Check assumptions: Run diagnostic tests (heteroscedasticity, autocorrelation, normality)
  3. Use formula API for categorical variables: smf.ols() handles categorical variables automatically
  4. Robust standard errors: Use when heteroscedasticity detected but model specification is correct
  5. Model selection: Use AIC/BIC for non-nested models, F-test/likelihood ratio for nested models
  6. Outliers and influence: Always check Cook's distance and leverage
  7. Multicollinearity: Check VIF and condition number before interpretation
  8. Time series: Use GLSAR or robust HAC standard errors for autocorrelated errors
  9. Grouped data: Consider mixed effects models or cluster-robust standard errors
  10. Quantile regression: Use for robust estimation or when interested in distributional effects
Raw lines 427 to 439
440## Common Pitfalls
4421. **Forgetting to add constant**: Results in no-intercept model
4432. **Ignoring heteroscedasticity**: Use WLS or robust standard errors
4443. **Using OLS with autocorrelated errors**: Use GLSAR or HAC standard errors
4454. **Over-interpreting with multicollinearity**: Check VIF first
4465. **Not checking residuals**: Always plot residuals vs fitted values
4476. **Rank deficiency**: Check design rank and singular values; a pseudoinverse fit can return coefficients for a non-identifiable design
4487. **Confusing prediction vs confidence intervals**: Prediction intervals are wider
4498. **Not handling categorical variables properly**: Use formula API or manual dummy coding
4509. **Comparing models with different sample sizes**: Ensure same observations used
45110. **Ignoring influential observations**: Check Cook's distance and DFFITS

Common Pitfalls

  1. Forgetting to add constant: Results in no-intercept model
  2. Ignoring heteroscedasticity: Use WLS or robust standard errors
  3. Using OLS with autocorrelated errors: Use GLSAR or HAC standard errors
  4. Over-interpreting with multicollinearity: Check VIF first
  5. Not checking residuals: Always plot residuals vs fitted values
  6. Rank deficiency: Check design rank and singular values; a pseudoinverse fit can return coefficients for a non-identifiable design
  7. Confusing prediction vs confidence intervals: Prediction intervals are wider
  8. Not handling categorical variables properly: Use formula API or manual dummy coding
  9. Comparing models with different sample sizes: Ensure same observations used
  10. Ignoring influential observations: Check Cook's distance and DFFITS
Raw lines 440 to 451