get_bartik_data(N=300, seed=1234)
Synthetic data for a Bartik (shift-share) IV application on immigration and wages.
DGP
Unobserved confounder: local_demand ~ N(0, 1)
First stage (immigration on bartik_instrument, conditional on log_population): immigration = 0.5 + 0.7bartik_instrument + 0.9local_demand + N(0, 0.5) → bartik_instrument is relevant; bartik ⊥ local_demand (exogenous)
Outcome (structural equation): wages = 8 + 0.5local_demand + TRUE_EFFECTimmigration + 0.2*log_population + N(0, 1) TRUE_EFFECT = -0.3
OVB for naive OLS (wages ~ immigration + log_population): Partial bias from local_demand ≈ 0.5 * 0.9/Var(immigration|log_pop) > 0 β_OLS on immigration ≈ -0.3 + positive_bias → attenuated (less negative or positive) β_IV on immigration ≈ -0.3 (recovers the true effect)
Parameters
| N |
int |
Number of observations (regions). Default is 300. |
300 |
| seed |
int |
Random seed. Default is 1234. |
1234 |
Returns
|
pandas.DataFrame |
Columns: wages, immigration, log_population, bartik_instrument. |
Examples
import pyfixest as pf
data = pf.get_bartik_data()
# OLS is attenuated by unobserved local demand, IV recovers the true -0.3
pf.etable(
[
pf.feols("wages ~ immigration + log_population", data),
pf.feols("wages ~ log_population | immigration ~ bartik_instrument", data),
]
)
| (1) |
(2) |
| coef |
| immigration |
0.002
(0.053) |
-0.384***
(0.097) |
| log_population |
0.726***
(0.201) |
1.158***
(0.235) |
| Intercept |
6.713***
(0.399) |
6.092***
(0.451) |
| stats |
| Observations |
300 |
300 |
| R2 |
0.046 |
- |
| Significance levels: * p < 0.05, ** p < 0.01, *** p < 0.001. Format of coefficient cell: Coefficient (Std. Error) |