Abstract
This notebook demonstrates how to apply the “Significance Bands for Local Projections” methodology, as detailed by Inoue, Jordà, and Kuersteiner (2023), henceforth referred to as IJK, to examine the impact of monetary policy shocks (Romer and Romer, 2023) on inflation. The importance of utilizing significance bands, as opposed to traditional confidence bands, is underscored by IJK, especially when evaluating the dynamic influence of a treatment on an outcome variable through local projections. I show how to implement these methods using the R programming language and provide a comprehensive, step-by-step guide aimed at assisting researchers who wish to apply these techniques to their own analyses.1Keywords: Local projections, significance bands, monetary policy shocks, R.
Inoue, Jordà, and Kuersteiner (2023) propose employing significance bands with confidence bands to enhance impulse response function visualization. This approach allows for a more nuanced interpretation of statistical estimates. Confidence bands provide information on the uncertainty of each impulse response coefficient. In contrast, significance bands are better suited for visually assessing the null hypothesis, which posits that the entire impulse response is zero.
Standard statistical software and the Lagrange multiplier principle make constructing significance bands straightforward under the null hypothesis. This ease of construction is a noteworthy advantage. The bands’ calculations do not depend on the impulse response horizon, and the paper provides approximate formulas. The authors also discuss bootstrap methods as a less assumption-reliant approach.
Monte Carlo simulations show that IJK’s proposed significance bands outperform traditional confidence bands. The simulations reveal that these bands have favorable size and power properties compared to solely considering whether confidence bands include zero. Thus, the authors argue that significance bands should be a standard graphical representation for impulse response functions.
In this paper, I replicate key findings from IJK’s study. Specifically, I focus on estimating local projections (LPs) for the Romer and Romer monetary policy shock (Romer and Romer, 2023.) I then test these estimates using the significance bands method.
Start by loading the necessary packages
library(tidyverse) # for data processing and plotting
library(broom) # for tidying regression output
library(readxl) # for reading .xls files
library(dynlm) # for running dynamic regressions with leads and lags
library(sandwich) # for Newey-West standard errors
library(lmtest) # for coefficient tests
library(car) # for linear hypothesis tests
library(patchwork) # for combing figuresSet the basic parameters
H <- 20 # LP horizon (in quarters)
pval <- 0.05 # p-value
lags <- 4 # lags to be used in LP regressions
nwlag <- H # lags to be used in Newey-West
block <- H # bootstrap block sizeSet the sample
Set seed for replication
Load the Romer and Romer (2023) dataset (link to source)
Some preprocessing steps including computing the logarithm of the GDP
price index (GDPPI) and renaming it as y, and
renaming the monetary policy shock variable (RRDUMY) to
s:
df <- df_raw %>%
rename(
date = 1,
s = RRDUMMY
) %>%
mutate(
year = as.integer(substr(date, 1, 4)),
quarter = as.integer(substr(date, 6, 6)),
month = (quarter - 1) * 3 + 1,
date = make_date(year, month, 1),
y = 100 * log(GDPPI),
dy = c(NA, 400 * diff(log(GDPPI))),
) %>%
filter(between(date, smpl_start, smpl_end)) %>%
select(date, s, y, dy) %>%
drop_na()Compute the long differences of \(y_t\), i.e., \(\Delta^h y_{t+h} \equiv y_{t+h} - y_{t-1}\) for \(h=1,…,H\):
n this section, we orthogonalize the response and treatment (shock) variables with respect to the control variables, specifically the lags of both and a constant. This step offers a clear advantage by simplifying the regression process, making it easier to understand and run. The primary motivation behind this approach is the Frisch-Waugh-Lowell theorem, which suggests that the coefficient of interest remains numerically identical, whether we use the full model or the orthogonalized model for estimation. However, it’s essential to correct for the degrees of freedom when calculating the standard errors to ensure our results are accurate and robust.
Define the portion of the regression formula that includes both the
constant and the control variables (4 lags of s and
y):
Orthogonalize \(z_t\) with respect
to the controls and store the residual in a variable named
e_s:
formula_s <- paste0("s ~ ", formula_x) %>% as.formula()
model_s <- dynlm(formula_s, data = df_fwd %>% ts())
df_e_s <- model_s$residuals %>%
as_tibble() %>%
rename(e_s = 1)Orthogonalize \(\Delta_h y_{t+h}\)
for \(h=0,...,H\) with respect to the
controls and store the residuals in
e_fy0,…,e_fyH:
mat_e_y <- matrix(NA, length(df_e_s$e_s), H+1)
for (i in 0:H) {
formula_y <- paste0("fy", i, "~ ", formula_x) %>% as.formula()
model_y <- dynlm(formula_y, data = df_fwd %>% ts())
if (i == 0) {
mat_e_y[ , i+1] <- model_y$residuals %>% as.vector()
} else{
mat_e_y[ , i+1] <- c(model_y$residuals %>% as.vector(), rep(NA, i))
}
}
df_e_y <- mat_e_y %>%
as_tibble()
colnames(df_e_y) <- paste0("e_fy", 0:H)Combine the orthogonalized residuals into a single dataframe:
Run local projections using the orthogonalized series. Specifically, estimate the given regression model3:
\[ e^y_{t+h}=e^s_t \beta_h+u_{t+h} \quad \text { for } h=0,1, \ldots, H-1 ; \quad t=1, \ldots, T \]
and store the \(\beta_h\) coefficients and their corresponding standard errors for each horizon.
mat_lp <- matrix(data = NA, ncol = 7, nrow = H+1) # matrix to store estimation results
for (i in 0:H) {
formula_e_y <- paste0("e_fy", i, " ~ e_s -1") %>% as.formula()
lp_fit <- dynlm(formula_e_y, data = df_e_ys %>% ts())
nw_se <- sqrt(diag(NeweyWest(lp_fit, lag = i, prewhite = FALSE)))
n <- length(lp_fit$residuals)
nw_se_adj <- nw_se * sqrt((n - 1) / (n - 1 - 8)) # adjust for degrees of freedom
beta_i <- lp_fit$coefficients["e_s"]
se_beta_i <- nw_se_adj["e_s"]
mat_lp[i+1, 1] <- i
mat_lp[i+1, 2] <- beta_i
mat_lp[i+1, 3] <- se_beta_i
mat_lp[i+1, 4] <- beta_i + 1 * se_beta_i
mat_lp[i+1, 5] <- beta_i - 1 * se_beta_i
mat_lp[i+1, 6] <- beta_i + 2 * se_beta_i
mat_lp[i+1, 7] <- beta_i - 2 * se_beta_i
}
df_lp <- mat_lp %>%
as_tibble()
colnames(df_lp) <-
c("horizon", "mid", "se", "up1sd", "down1sd", "up2sd", "down2sd")set.seed(12345) # for replication
n <- length(df_e_ys$e_fy0)
block_id <- floor((1:n + block - 1) / block)
model_eta <- lm(eta ~ 1)
nw_se_eta <- sqrt(diag(vcovBS(model_eta, cluster = block_id, R = 1000)))
seta <- nw_se_eta["(Intercept)"]
sbeta <- seta/gamma_mean
bootju <- qnorm(1 - (pval / (2 * (H + 1)))) * sbeta
bootjd <- -bootjuCombine the estimation results of significance bands into a single dataframe:
df_sb <- tibble(
bju = rep(bju, H+1),
bjd = rep(bjd, H+1),
bootju = rep(bootju, H+1),
bootjd = rep(bootjd, H+1)
)Next, plot the LP with the confidence bands (one and two standard deviations) and significance bands (Newey-West and bootstrap):
p1 <- df_lp %>%
bind_cols(df_sb) %>%
ggplot(aes(x = horizon)) +
geom_line(aes(y = mid), color = 'darkgreen', linewidth = 1.5) +
geom_line(aes(y = bju), color = "red", linetype = 2, size = 1) +
geom_line(aes(y = bjd), color = "red", linetype = 2, size = 1) +
geom_line(aes(y = bootju), color = "blue", linetype = 2, size = 1) +
geom_line(aes(y = bootjd), color = "blue", linetype = 2, size = 1) +
geom_ribbon(aes(ymin = down1sd, ymax = up1sd),
fill = "darkgreen", linetype = 0, alpha = 0.2) +
geom_ribbon(aes(ymin = down2sd, ymax = up2sd),
fill = "darkgreen", linetype = 0, alpha = 0.1) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, H, 1)) +
# scale_y_continuous(expand = c(0,0), limits = c(-4, 4)) +
labs(
x = "Quarters after the shock",
y = "Percentage points",
title = "Cumulative response of GDP Price Index to a monetary policy shock",
) +
geom_hline(aes(yintercept = 0)) +
theme_light(12) +
theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank()
)
p1The figure illustrates the impulse response of the long difference, calculated from 100 times the logarithm of the GDP Price Index, after a monetary policy shock as described by Romer and Romer (2023). The model incorporates four lagged variables both for the GDP Price Index and the monetary policy shock. Dark and light green shaded areas represent confidence bands corresponding to one and two standard errors, respectively. Additionally, dashed blue lines indicate classical Newey-West significance bands, while dashed red lines represent bootstrapped significance bands.
In examining the graphical representation, it is evident that the pointwise null hypothesis positing no effect remains unchallenged within the bounds of 2 standard deviations, persisting even at an extended horizon of 20 quarters. Conversely, when assessing a broader perspective, the overarching hypothesis of an absolute absence of effect finds little support in the observed data. The impulse response distinctly falls beneath the significance bands, a conclusion consistent with both the Newey-West and the bootstrapped versions. This delineation underscores the nuanced distinctions between point-specific and overarching effects in the context of economic implications.
In this section, we employ a technique proposed by Jordà, where we estimate the \(h\) local projection regression simultaneously as a panel anduse Driscoll and Kraay (1998) HAC robust standard errors for inference. The advantage of this estimation method is that it provides us with the coefficients’ variance-covariance matrix, thereby enabling us to test the hypothesis that \(\beta_h=0\) for all \(h\).
We start by pivoting the data to long format
df_e_ys_long <-
df_e_ys %>%
pivot_longer(-c(date, e_s), names_to = "horizon", values_to = "e_fy") %>%
mutate(
horizon = as.factor(gsub("^e_fy", "", horizon))
) %>%
select(date, horizon, e_fy, e_s)Next, we estimate the data stacked as a panel using the
plm package:
plm_model <- plm::plm(e_fy ~ factor(horizon):e_s, data = df_e_ys_long, index = c("horizon", "date"), model = "within")Finally, we conduct the joint hypothesis test, namely: \[ H_0: \beta_h=0, \text{for } h=0,\ldots,H \]
hypothesis <- sapply(0:H, function(i) paste0("factor(horizon)", i, ":e_s = 0"))
linearHypothesis(plm_model, hypothesis, vcov. = function(x) plm::vcovSCC(x, maxlag=nwlag))## Linear hypothesis test
##
## Hypothesis:
## factor(horizon)0:e_s = 0
## factor(horizon)1:e_s = 0
## factor(horizon)2:e_s = 0
## factor(horizon)3:e_s = 0
## factor(horizon)4:e_s = 0
## factor(horizon)5:e_s = 0
## factor(horizon)6:e_s = 0
## factor(horizon)7:e_s = 0
## factor(horizon)8:e_s = 0
## factor(horizon)9:e_s = 0
## factor(horizon)10:e_s = 0
## factor(horizon)11:e_s = 0
## factor(horizon)12:e_s = 0
## factor(horizon)13:e_s = 0
## factor(horizon)14:e_s = 0
## factor(horizon)15:e_s = 0
## factor(horizon)16:e_s = 0
## factor(horizon)17:e_s = 0
## factor(horizon)18:e_s = 0
## factor(horizon)19:e_s = 0
## factor(horizon)20:e_s = 0
##
## Model 1: restricted model
## Model 2: e_fy ~ factor(horizon):e_s
##
## Note: Coefficient covariance matrix supplied.
##
## Res.Df Df Chisq Pr(>Chisq)
## 1 5544
## 2 5523 21 704.75 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
In Romer and Romer (2023), the researchers did not explore the effects on the price level as we did. Instead, they focused on directly assessing the implications for inflation. Specifically, they quantified their response as 400 times the log difference in the quarterly GDP Price Index. For context, we replicated their results.4
The left-hand side of the figure presents the LP and bands related to the price level. In contrast, the right-hand side highlights the LP and bands relevant to inflation. Significantly, we cannot reject the null hypothesis that there is no effect, based on the significance bands approach.
This seeming “discrepancy” arises from the distinct nature of the measurements. Analyzing the cumulative price level version raises the issue of whether the price level, over time, deviates either above or below its initial treatment point. On the other hand, individual quarterly inflation rates might seem negligible, but their combined effect over time can lead to a notable shift.
Driscoll, J. C., & Kraay, A. C. (1998). Consistent covariance matrix estimation with spatially dependent panel data. Review of economics and statistics, 80(4), 549-560.
Inoue, A., Jordà, Ò., & Kuersteiner, G. M. (2023). Significance Bands for Local Projections. arXiv preprint arXiv:2306.03073.
Romer, C. D., & Romer, D. H. (2023). Presidential Address: Does Monetary Policy Matter? The Narrative Approach after 35 Years. American Economic Review, 113(6), 1395-1423.
Here’s a link to the GitHub repository that contains both the code and dataset utilized in this notebook.↩︎
The views expressed in this notebook are my own and do not represent the views of the Bank of Israel or any of its staff members. I would like to thank Oscar Jordà for his most helpful feedback and for sharing his code. Any remaining errors are mine.↩︎
The lpirf
R package is excellent for estimating and visualizing both linear and
nonlinear impulse responses through local projections. While I haven’t
utilized lpirf in this analysis due to the need for greater flexibility,
I highly recommend it!↩︎
For those curious about the details of our replication process, the code is accessible in the notebook’s GitHub repository.↩︎