# ============================================================ # DiD moderno + Event Study — código mínimo viable reproducible # Post-2020 DiD revolution: TWFE, Callaway-Sant'Anna, dCdH, Sun-Abraham # ============================================================ # Roth et al. 2023 · Goodman-Bacon 2021 · de Chaisemartin & D'Haultfœuille 2020 # Callaway & Sant'Anna 2021 · Sun & Abraham 2021 # install.packages(c("fixest", "did", "didimputation", "DIDmultiplegt", "ggplot2")) library(fixest) # TWFE estándar + corrección Sun-Abraham library(did) # Callaway-Sant'Anna library(didimputation) # Borusyak-Jaravel-Spiess library(DIDmultiplegt) # de Chaisemartin-D'Haultfœuille library(dplyr) library(ggplot2) # ============================================================ # 1. Datos panel: unidad, tiempo, tratamiento staggered # ============================================================ # df debe tener: id, t, treat_time (NA si nunca tratado), y, X (controles opcionales) # Crear variable de "tiempo desde tratamiento" df <- df %>% mutate( rel_time = if_else(is.na(treat_time), NA_real_, t - treat_time), treated = !is.na(treat_time), post = !is.na(rel_time) & rel_time >= 0 ) # ============================================================ # 2. TWFE clásico (referencia, NO RECOMENDADO para staggered) # ============================================================ # Goodman-Bacon 2021: TWFE en staggered es un weighted avg con pesos perversos twfe <- feols(y ~ post | id + t, data = df, cluster = ~id) summary(twfe) # ============================================================ # 3. Goodman-Bacon decomposition — ¿qué tan dañado está el TWFE? # ============================================================ # install.packages("bacondecomp") library(bacondecomp) bd <- bacon(y ~ post, data = df, id_var = "id", time_var = "t") print(bd) # Si pesos negativos > 10%, TWFE no es confiable → ir a CS o dCdH # ============================================================ # 4. Callaway-Sant'Anna (recomendado para staggered) # ============================================================ cs <- att_gt(yname = "y", tname = "t", idname = "id", gname = "treat_time", data = df, control_group = "notyettreated", est_method = "dr") # Agregación group-time → ATT promedio cs_agg <- aggte(cs, type = "group") summary(cs_agg) # Event study dinámico cs_es <- aggte(cs, type = "dynamic") ggdid(cs_es) + ggtitle("Callaway-Sant'Anna event study") # ============================================================ # 5. Sun-Abraham via fixest::sunab # ============================================================ sa <- feols(y ~ sunab(treat_time, t) | id + t, data = df, cluster = ~id) summary(sa) iplot(sa, main = "Sun-Abraham event study") # ============================================================ # 6. de Chaisemartin & D'Haultfœuille # ============================================================ dcdh <- did_multiplegt(df, Y = "y", G = "id", T = "t", D = "post", dynamic = 4, placebo = 3, brep = 100) # Interpretar: efectos dinámicos + placebos pre-tratamiento # ============================================================ # 7. Pre-trend test (clave para validar diseño) # ============================================================ # Los placebos pre-tratamiento deben ser ~0 # Si hay tendencias diferenciales → diseño invalidado # ============================================================ # 8. Reportar todo # ============================================================ # Tabla 1: TWFE vs CS vs Sun-Abraham vs dCdH # Si convergen → resultado robusto # Si divergen → declarar honestamente el problema de identificación # ============================================================ # Pregunta IA² (Lab L4): # ¿La IA me ayudó a auditar el código o lo escribió ella sola y yo # solo lo copié? Si no podés explicar línea por línea qué hace # `aggte(cs, type="dynamic")`, eso no es tu análisis. # ============================================================