SKILL.md
Instrumental Variables Regression Guide
A skill for applying instrumental variables (IV) estimation to address endogeneity in regression models. Covers the logic of IV, two-stage least squares (2SLS), instrument validity tests, weak instrument diagnostics, and reporting standards.
The Endogeneity Problem
Why OLS Fails
Ordinary Least Squares assumes: E[u | X] = 0
(Regressors are uncorrelated with the error term)
This assumption is violated when:
- Omitted variable bias: A confound affects both X and Y
- Simultaneity: X affects Y and Y affects X
- Measurement error: X is measured with noise
Consequence: OLS estimates are biased and inconsistent.
No amount of data will fix this.
The IV Solution
An instrumental variable Z satisfies two conditions:
1. Relevance: Z is correlated with the endogenous regressor X
Cov(Z, X) != 0
2. Exclusion: Z affects Y ONLY through X (not directly)
Cov(Z, u) = 0
Z --> X --> Y
Z -/-> Y (no direct path)
Two-Stage Least Squares (2SLS)
How 2SLS Works
Stage 1: Regress the endogenous variable on the instrument(s)
X = gamma_0 + gamma_1 * Z + controls + v
Save the fitted values: X_hat
Stage 2: Regress the outcome on the fitted values
Y = beta_0 + beta_1 * X_hat + controls + e
The coefficient beta_1 is the IV estimate of the causal effect.
Implementation in Python
from linearmodels.iv import IV2SLS
import pandas as pd
def run_2sls(data: pd.DataFrame, dependent: str,
endogenous: str, instruments: list[str],
controls: list[str] = None) -> dict:
"""
Run a 2SLS instrumental variables regression.
Args:
data: DataFrame with all variables
dependent: Name of the dependent variable (Y)
endogenous: Name of the endogenous regressor (X)
instruments: List of instrument variable names (Z)
controls: List of exogenous control variable names
"""
controls = controls or []
exog_str = " + ".join(["1"] + controls) if controls else "1"
endog_str = endogenous
instr_str = " + ".join(instruments)
formula = f"{dependent} ~ {exog_str} + [{endog_str} ~ {instr_str}]"
model = IV2SLS.from_formula(formula, data)
result = model.fit(cov_type="robust")
return {
"coefficients": dict(result.params),
"std_errors": dict(result.std_errors),
"p_values": dict(result.pvalues),
"f_statistic_first_stage": result.first_stage.diagnostics,
"summary": str(result.summary)
}
