Evan and I have released an R package bacon to perform the Goodman-Bacon decomposition. The paper decomposes diff-in-diff estimates with time-varying treatment into a set of “two-by-twos” comparing treated and untreated groups at each timing stage. Evan did 99% of the work and it was great fun collaborating on an open source project with him. Below is a very quick example of how the package works.
Installing the package from GitHub (we’re not on CRAN just yet) and loading libraries:
Perform the decomposition and plot estimates vs weights, using the default
math_reform dataset included in the package:
df_bacon <- bacon(incearn_ln ~ reform_math, data = bacon::math_reform, id_var = "state", time_var = "class") ggplot(df_bacon) + aes(x = weight, y = estimate, shape = factor(type), colour = factor(type)) + geom_point(size = 3) + geom_hline(yintercept = 0, linetype = "longdash") + labs(x = "Weight", y = "Estimate", shape = "Type", colour = "Type", title = "Goodman-Bacon decomposition of diff-in-diff estimates vs weights") + theme_minimal() + theme(legend.position = "bottom")
Plotting estimates from largest to smallest alongside the traditional two-way FE estimate:
df_bacon %>% mutate(subgroup = paste0(treated, "_", untreated), subgroup = factor(subgroup), subgroup = forcats::fct_reorder(subgroup, estimate)) %>% ggplot(aes(x = estimate, y = subgroup, size = weight)) + geom_point() + geom_vline(xintercept = weighted.mean(df_bacon$estimate, df_bacon$weight), linetype = "longdash") + theme_minimal() + labs(size = "Weight", y = "Subgroup", x = "Estimate", title = "Goodman-Bacon diff in diff decomposition", subtitle = "Dotted line indicates two-way FE estimate.", caption = "Subgroups 99999 correspond to never treated groups")
We’re always open to feedback and suggestions - just open a GitHub issue.