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.