---
title: "Bartik Instrument and Rotemberg weights"
date: "2024-10-26"
---
## Bartik Instrument
The Bartik instrument, or shift-share IV (Bartik 1991), combines initial local industry shares with national industry growth rates to construct an instrument for local economic conditions.
We follow Goldsmith-Pinkham, Sorkin, and Swift (2021) (GPSS). Consider the labor-supply elasticity:
$$ y_{lt} = D_{lt} \rho + \beta_0 x_{lt} + \epsilon_{lt}$$ {#eq-bartik-instrument-1}
Here $y_{lt}$ is wage growth in location $l$, $x_{lt}$ is employment growth, and $\beta_0$ is the inverse labor-supply elasticity. The challenge is that $x_{lt}$ is endogenous — unobserved factors may drive both wage and employment growth.
Two accounting identities underlie the instrument:
$$ x_{lt} = Z_{lt} G_{lt} = \sum_{k=1}^K z_{lkt} g_{lkt} $$ {#eq-bartik-instrument-2}
and
$$ g_{lkt} = g_{kt} + \tilde{g}_{lkt} $$ {#eq-bartik-instrument-3}
With $K$ industries in location $l$, employment growth decomposes into industry-level growth rates $g_{lkt}$ weighted by industry shares $z_{lkt}$. The second identity separates each location-industry growth rate into a national component and a location-specific residual. The Bartik instrument uses initial shares and national growth rates:
$$ B_{lt} = Z_{l0} G_{t} = \sum_{k=1}^K z_{lk0} g_{kt} $$ {#eq-bartik-instrument-4}
The identifying argument is that initial shares are predetermined (a level, not a growth rate) and national industry growth is not driven by any single location.
## Bartik Instrument in special cases
### Two industries and one period
GPSS goes through the simplest case: two industries and one period.
$$ B_l = z_{l1} g_1 + z_{l2} g_2 $$ {#eq-bartik-instrument-5}
Since there are only two industries, $z_{l1} = 1 - z_{l2}$. Then we can rewrite the Bartik instrument as
$$ B_l = g_2 + (g_1 - g_2) z_{l1} $$ {#eq-bartik-instrument-6}
From this simplest case, we see that the instrument has only $z_{l1}$, that is related to location. If the industry share can be considered exogenous, then the instrument is valid. We have to argue that the industry share is exogenous. In this case, the instrument is a linear function of the industry share. Therefore, the Bartik instrument is the same as using the industry share as an instrument.
GPSS shows that just-identified two-stage least squares using a Bartik instrument can in fact be written as GMM with an overidentified set of instruments, where the weight matrix is the outer product of national growth rates. That is, Bartik estimator is equivalent to a GMM estimator, with each location share as one instrument, and the weight matrix is the outer product of national growth rates.
With $K$ industries and $T$ time periods, it's basically using $K \times T$ instruments. Still, Bartik instrument is the same as GMM with all these instruments.
## Rotemberg weights
GPSS decompose the Bartik estimator:
$$ \beta_{Bartik} = \sum_{k} \hat \alpha_k \hat \beta_k$$ {#eq-bartik-instrument-7}
where
$$ \hat \beta_k = (Z'_k X^{\perp})^{-1} Z'_k Y^{\perp}$$ {#eq-bartik-instrument-8}
and
$$ \hat \alpha_k = \frac{g_k Z'_k X^{\perp}}{\sum_{k'} g_{k'} Z'_{k'} X^{\perp}}$$ {#eq-bartik-instrument-9}
Here $X^{\perp}$ and $Y^{\perp}$ are the residualized $X$ and $Y$, from the regression of $X$ and $Y$ on all other variables (controls).
Rotemberg weights provide a measure of how important each share is. Read the direction carefully: if industry $k$'s own just-identified estimate $\hat\beta_k$ is biased by 1 percent, the overall Bartik estimate inherits $\alpha_k$ percent of that bias. So a large $\alpha_k$ means the overall answer is *sensitive* to that industry, not that the industry is itself badly biased. This is what makes the weights useful: they tell you for which instruments the exogeneity assumption has to be defended hardest. (The formal statement is GPSS Corollary D.1, given below.)
## Simulations
Simulations codes are adopted from Kohei Kawaguchi's lecture notes. I modified slightly to fit different scenarios.
Here we simulate a case with 4 industries and 4 periods. We have 1000 locations, 4 periods, and 4 industries. The true $\beta$ (effect of $x$ on $y$) is 1.
```{r}
#| message: false
#| warning: false
library(ggplot2)
library(tidyverse)
library(modelsummary)
library(kableExtra)
library(estimatr)
set.seed(6)
L <- 1000
T <- 4
K <- 4
beta <- 1
sigma <-c(1, 1, 1)
z_lk0 <- expand_grid(
l = 1:L,
k = 1:K
) |>
mutate(z_lk0 = runif(length(l)))
g_kt <- expand_grid(
t = 1:T,
k = 1:K
) |>
mutate(g_kt = rnorm(n(), 0, .1))
df <- expand_grid(
l = 1:L,
t = 1:T,
k = 1:K
) |>
left_join(z_lk0, by = c("l", "k")) |>
left_join(g_kt, by = c("k", "t")) |>
mutate(z_lkt = z_lk0 + sigma[1] * runif(length(l)),
g_lkt = g_kt + sigma[2] * rnorm(length(l),0, .1)
)
df <- df %>%
group_by(t, l) |>
mutate(
z_lk0 = z_lk0 / sum(z_lk0),
z_lkt = (z_lkt / sum(z_lkt)),
x_lt = sum(z_lkt * g_lkt)
)
df
```
The simulation has $L = 1000$ locations, $T = 4$ periods and $K = 4$
industries, 16,000 location-industry-period rows. Baseline shares $z_{lk0}$ are
uniform draws normalised to sum to one within each location, national industry
growth is $g_{kt} \sim N(0, 0.1^2)$, current shares add a uniform perturbation
and are renormalised, and local industry growth is
$g_{lkt} = g_{kt} + N(0, 0.1^2)$. Local employment growth is
$x_{lt} = \sum_k z_{lkt} g_{lkt}$. The true $\beta$ is 1.
This assumes we observe the industry shares at time 0 ($z_{lk0}$), and the industry growth rates ($g_{kt}$). We also observe the industry shares at time $t$ ($z_{lkt}$). The employment growth rate at location $l$ and time $t$ ($g_{lkt}$) is a function of $g_{kt}$ plus some noise. We can calculate the $x_{lt}$, which is the weighted sum of industry growth rates.
Next we generate the outcome at $l$, $t$ level. Outcome (wage growth) is a linear function of $x$ (employment growth) at $l$, $t$ level.
```{r}
#| message: false
#| warning: false
df_lt <- df |>
mutate(g_tilde_lkt = g_lkt - g_kt) |>
group_by(l, t) |>
summarise(across(c(x_lt, g_tilde_lkt), mean), .groups = "drop") |>
ungroup() |>
mutate(y_lt = beta * x_lt + sigma[3] * g_tilde_lkt)
```
The outcome is $y_{lt} = \beta x_{lt} + \sigma_3 \tilde g_{lt}$, where
$\tilde g_{lt}$ is the location's mean deviation of local from national industry
growth. That deviation also enters $x_{lt}$, which is the endogeneity: local
shocks drive both employment growth and wage growth.
If we run OLS on raw data, we'll get biased results.
```{r}
#| message: false
#| warning: false
result_ols <-
df_lt |>
lm(formula = y_lt ~ x_lt)
modelsummary(result_ols)
```
OLS gives 1.336 with a standard error of 0.008, against a true $\beta$ of 1 —
biased upward by a third, and far outside any confidence interval. The
endogenous $\tilde g_{lt}$ is doing the damage.
Now we generate the Bartik instrument. We calculate the Bartik instrument as the weighted sum of $z_{lk0}$ and $g_{kt}$.
```{r}
#| message: false
#| warning: false
b_lt <- df |>
group_by(l, t) |>
summarise(b_lt = sum(z_lk0 * g_kt)) |>
ungroup()
df_lt <- df_lt |>
left_join(b_lt, by = c("l", "t"))
result_first_stage <- lm(formula = x_lt ~ b_lt, data = df_lt)
modelsummary(result_first_stage)
result_tsls <- df_lt |>
estimatr::iv_robust(formula = y_lt ~ x_lt | b_lt)
modelsummary::modelsummary(result_tsls)
```
The Bartik instrument recovers the truth: 2SLS gives 0.990 with a standard error
of 0.012, against $\beta = 1$, where OLS gave 1.336. The instrument is built
from baseline shares and *national* growth rates, neither of which contains the
local shock $\tilde g_{lt}$, which is what breaks the endogeneity.
### Calculate Rotemberg weights
Let's do IV using Bartik instruments. No controls here. We use matrix operation to recover the IV estimates, which is the same as the tsls function in R.
```{r}
#| message: false
#| warning: false
B = as.matrix(cbind(1,df_lt$b_lt))
Y = as.matrix(df_lt$y_lt)
X = as.matrix(cbind(1,df_lt$x_lt))
iv = round(solve(t(B)%*%X)%*%t(B)%*%Y,3)
## Label and organize results into a data frame
beta.hat = as.data.frame(cbind(c("Intercept","x"),iv))
names(beta.hat) = c("Coeff.","Est")
beta.hat
```
Now we calculate the Rotemberg weights.
```{r}
#| message: false
#| warning: false
# This code is the R version of Paul Goldsmith-Pinkham's R code for Rotemberg weights on
# github; which is actually a call from a C++ code by jjchern.
# bw.R calls ComputeAlphaBeta.cpp
# dimensions: G is KTx1, Z is LTxKT, Y is LTx1, X is LTx1
# so B=ZG is LTx1
# Here I learned the dimensions from the ADH example in Paul's github.
# in ADH example, czone(L) is 722, year(T) is 2, ind(K) is 390.
# so G is 780x1, Z is 1444x780, Y is 1444x1, X is 1444x1
y_lt <- df_lt |>
select(l,t,y_lt)
Y <- as.matrix(y_lt[,3])
x_lt <- df_lt |>
select(l,t,x_lt)
X <- as.matrix(x_lt[,3])
#X <- as.matrix(cbind(1,x_lt[,3]))
# global is kt level data set
# # g_data <- df |>
# # group_by(k,t) |>
# # summarise(g_kt=sum(g_lkt*z_lkt))
#
# G <- as.matrix(g_data[,3])
G <- as.matrix(g_kt[,3])
# generate wide Z matrix, LTxKT from df
# z_data <- df |>
# select(l,t,k,z_lkt) |>
# pivot_wider(names_from = k, values_from = z_lkt) |>
# select(-l, -t)
# this makes a wide Z data, which is LTxKT, KT variables are generated by the spread function.
# basically expand the shares to KT variables, filled with 0 if there is no data.
# Use z_lk0 (the INITIAL shares), not z_lkt: the Bartik instrument is B = Z_0 G,
# so the Rotemberg decomposition has to be taken with respect to the same Z that
# builds the instrument. Using z_lkt here would decompose a different estimator.
# z_data <- df |>
# select(l,t,k,z_lkt) |>
# mutate(k = str_glue("t{t}_sh_ind_{k}") ) |>
# spread(k, z_lkt, fill = 0)
z_data <- df |>
select(l,t,k,z_lk0) |>
mutate(k = str_glue("t{t}_sh_ind_{k}") ) |>
pivot_wider(names_from = k, values_from = z_lk0, values_fill = 0)
Z <- as.matrix(z_data[,-c(1,2)])
# Residualize Y and X on the controls before forming the weights. There are
# no controls here other than the constant, so the residualization is just
# demeaning; this is what makes the Rotemberg decomposition exact (GPSS).
Yp <- Y - mean(Y)
Xp <- X - mean(X)
beta = round((t(Z) %*% Yp)/(t(Z) %*% Xp), digits=3)
alpha=(diag(as.numeric(G)) %*% t(Z) %*% Xp) / as.numeric((t(G) %*% t(Z) %*% Xp))
bw <- tibble(g_kt, alpha=as.numeric(alpha), beta=as.numeric(beta)) |>
mutate(product=alpha*beta)
bw
sum(bw$product)
```
The sum of the product of $\alpha$ and $\beta$ should be the same as the Bartik estimator, and it is: 0.9894 against the 2SLS estimate of 0.990. The
decomposition is exact, not approximate.
Reading the table, the weights are far from uniform across the sixteen
industry-period cells. Period 3 carries most of the weight — its four $\alpha$
values are 0.138, 0.222, 0.076 and 0.136, summing to 0.57 — while period 2
contributes almost nothing and even goes slightly negative
($\alpha = -0.008$ and $-0.0004$ for its first two industries). The
just-identified $\beta_k$ estimates are all near the true 1, ranging from 0.74
to 1.19, which is what we should see in a simulation with a homogeneous effect.
Note there is no restriction for $\alpha$, other than the sum is 1. Therefore it can be negative or positive, and individual weights can exceed 1, as long as they sum to 1. In GPSS the Rotemberg weights routinely take negative values and values greater than 1; this is a feature used to flag problematic instruments.
## Why is good to have Rotemberg weights?
GPSS Corollary D.1. shows the interpretation of the Rotemberg weights.
The percentage of bias can be written as:
$$ \frac{E(\tilde{\beta})}{\beta_0} = \sum_k \alpha_k \frac{E(\tilde \beta_k)}{\beta_0}$$ {#eq-bartik-instrument-10}
This is to say the $\alpha_k$ is the share of the bias of the estimate of $\beta$ that comes from the bias of the estimate of $\beta_k$. That is, how sensitive it is to the bias from industry $k$.
In my simulations, all the industries are random, therefore the weights are random too. But in empirical studies, they are not random, most likely dominated by some industries. This is why it is important to have Rotemberg weights. It tells us which industries are important in the estimation of the effect of interest.
## What if you are using a reduced form?
If you use the reduced form, something like:
$$ y_{lt} = D_{lt} \rho + \beta_0 B_{lt} + \epsilon_{lt}$$ {#eq-bartik-instrument-11}
where $B_{lt}$ is the Bartik instrument, constructed in such as a way:
$$ B_{lt} = Z_{l0} G_{t} = \sum_{k=1}^K z_{lk0} g_{kt} $$ {#eq-bartik-instrument-12}
Then I think the Rotemberg weights should be calculated as:
$$ \beta_{Bartik} = \sum_{k} \hat \alpha_k \hat \beta_k$$ {#eq-bartik-instrument-13}
where
$$ \hat \beta_k = (Z'_k B^{\perp})^{-1} Z'_k Y^{\perp}$$ {#eq-bartik-instrument-14}
and
$$ \hat \alpha_k = \frac{g_k Z'_k B^{\perp}}{\sum_{k'} g_{k'} Z'_{k'} B^{\perp}}$$ {#eq-bartik-instrument-15}
where $B^{\perp}$ and $Y^{\perp}$ are residualized $B$ and $Y$.
```{r}
#| message: false
#| warning: false
result_reduced <- df_lt |>
lm(formula = y_lt ~ b_lt)
modelsummary::modelsummary(result_reduced)
```
Now we calculate the Rotemberg weights for the reduced form, for our simulated data.
```{r}
#| message: false
#| warning: false
B <- as.matrix(b_lt[,3])
# Residualize Y and B on the controls (here only a constant ⇒ demean), as in
# the GPSS reduced-form Rotemberg formula above, so the decomposition is exact.
Yp <- Y - mean(Y)
Bp <- B - mean(B)
beta = (t(Z) %*% Yp)/(t(Z) %*% Bp)
alpha=(diag(as.numeric(G)) %*% t(Z) %*% Bp) / as.numeric((t(G) %*% t(Z) %*% Bp))
bw <- tibble(g_kt, alpha=as.numeric(alpha), beta=as.numeric(beta)) |>
mutate(product = alpha * beta)
bw
sum(bw$product)
```
---
<!-- see-also-footer -->
*Systematic treatment: [R](https://xiangao.github.io/causal_econometrics_guide/shift-share-iv.html) · [Julia](https://xiangao.github.io/causal_econometrics_julia/shift-share-iv.html).*
The reduced-form decomposition sums to 0.9091 rather than 0.9894, and that is
not an error: it decomposes the reduced-form coefficient (the effect of the
instrument on the outcome), not the 2SLS coefficient. The two differ by the
first stage, which is not exactly 1.