Instrumental variables (2SLS / LIML / GMM)

linearmodels.IV2SLS, IVLIML, IVGMM, and friends are supported through the linearmodels IV adapter. The fitted second-stage coefficients enter pymargins just like an OLS fit, with the IV covariance carried through to delta-method SEs.

import numpy as np
import pandas as pd
from linearmodels.iv import IV2SLS

from pymargins import Margins

rng = np.random.default_rng(2)
n = 2000
z = rng.normal(0, 1, n)
u = rng.normal(0, 1, n)
x = 0.5 * z + 0.3 * u + rng.normal(0, 1, n)
y = 1.0 + 0.8 * x + 0.5 * rng.normal(0, 1, n) + 0.7 * u

df = pd.DataFrame({"y": y, "x": x, "z": z})
df["const"] = 1.0

fit = IV2SLS(df["y"], df[["const"]], df[["x"]], df[["z"]]).fit()

AME of the endogenous regressor

m = Margins.linear_scale(fit, at="overall")
print(m.dydx("x").summary())
======================================================
          Margins Result (delta, level=0.95)          
======================================================
   estimate  std err        z  P>|z|  [95% Conf. Int.]
------------------------------------------------------
x    0.7819   0.0383  20.4227  0.000    0.7069, 0.8569
======================================================

n = 2000
κ: 0.000
Delta-vs-sim disagreement: 0.254%

Predictions at counterfactual x

print(m.predict(atexog={"x": [-1.0, 0.0, 1.0]}).summary())
===========================================================
             Margins Result (delta, level=0.95)            
===========================================================
        estimate  std err        z  P>|z|  [95% Conf. Int.]
-----------------------------------------------------------
x=-1.0    0.2603   0.0455   5.7226  0.000    0.1711, 0.3494
x=0.0     1.0418   0.0193  54.0131  0.000    1.0040, 1.0796
x=1.0     1.8233   0.0424  42.9824  0.000    1.7402, 1.9064
===========================================================

n = 2000
κ: max=0.000
Delta-vs-sim disagreement: 2.152%

Contrast: endogenous regressor +1 vs baseline

print(m.contrasts(
    scenarios=[
        {"atexog": {"x": 1.0}, "label": "x=1"},
        {"atexog": {"x": 0.0}, "label": "x=0"},
    ],
    contrasts=[+1, -1],
).summary())
========================================================
           Margins Result (delta, level=0.95)           
========================================================
     estimate  std err        z  P>|z|  [95% Conf. Int.]
--------------------------------------------------------
x=1    0.7815   0.0395  19.7743  0.000    0.7041, 0.8590
========================================================

n = 2000
κ: 0.000
Delta-vs-sim disagreement: 0.418%