Real Data Example: Castle Doctrine

In this vignette, we compare lwdidR and endid using the “Castle Doctrine” dataset from Cheng and Hoekstra (2013). States adopted castle doctrine laws in different years (2005–2009), making this a staggered adoption design.

Data Preparation

We load the data and set up the treatment timing variable (gvar). States that never adopted the law during the sample period serve as controls.

library(lwdidR)
library(endid)
library(ggplot2)
set.seed(42)

# Load the Castle Doctrine dataset
castle <- read.csv(system.file("extdata", "castle.csv", package = "lwdidR"))

# Create gvar: first treatment year (NA for never-treated)
castle$gvar <- castle$effyear
castle$gvar[is.na(castle$gvar) | castle$gvar == 0] <- NA

# Time-invariant controls
controls <- c("poverty", "unemployrt", "blackm_15_24", "whitem_15_24")

cat("States by cohort:\n")
#> States by cohort:
print(table(castle$gvar[!duplicated(castle$sid)], useNA = "ifany"))
#> 
#> 2005 2006 2007 2008 2009 <NA> 
#>    1   13    4    2    1   29

Linear Estimation with lwdidR

lwdidR provides a robust, linear estimate of the Average Treatment Effect on the Treated (ATT) using the staggered design.

fit_lwdid <- lwdid(
  data = castle,
  y = "lhomicide",
  ivar = "sid",
  tvar = "year",
  gvar = "gvar",
  controls = controls,
  vce = "hc3"
)
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=4,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=4,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=4,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=4,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=2,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=2,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=2,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
#> Warning in prepare_controls(samp, d, controls): Controls not applied: N1=1,
#> N0=29, K+1=5. Controls ignored.
summary(fit_lwdid)
#> 
#> Lee-Wooldridge DiD (lwdidR)
#> Design:      staggered
#> Transf.:     demean
#> VCE:         HC3
#> --------------------------------------------------
#> Overall ATT:   0.0917
#> SE:            0.0612
#> t-stat:        1.4997
#> p-value:       0.1402
#> --------------------------------------------------
#> 
#> Cohort-specific effects:
#>  cohort     att      se tstat    pvalue
#>    2005 0.08017 0.03215 2.493 1.884e-02
#>    2006 0.06824 0.08920 0.765 4.488e-01
#>    2007 0.11406 0.09838 1.159 2.552e-01
#>    2008 0.14605 0.08203 1.780 8.548e-02
#>    2009 0.21108 0.03550 5.946 2.115e-06
#> 
#> 
#> (g, r)-specific effects (first 10 rows):
#>  cohort period event_time      att      se    pvalue
#>    2005   2005          0 -0.13318 0.02826 6.089e-05
#>    2005   2006          1  0.08609 0.03449 1.873e-02
#>    2005   2007          2  0.16398 0.04686 1.580e-03
#>    2005   2008          3  0.13671 0.05067 1.168e-02
#>    2005   2009          4  0.12836 0.04484 7.861e-03
#>    2005   2010          5  0.09904 0.04880 5.200e-02
#>    2006   2006          0  0.05482 0.11626 6.405e-01
#>    2006   2007          1  0.03590 0.18718 8.491e-01
#>    2006   2008          2  0.04571 0.09609 6.375e-01
#>    2006   2009          3 -0.01218 0.09328 8.970e-01

Distributional Estimation with endid

endid uses engression to estimate how the law affects different parts of the homicide rate distribution, aggregating across all treatment cohorts.

# Reduced epochs and bootstrap for faster rendering
fit_endid <- endid(
  data = castle,
  y = "lhomicide",
  ivar = "sid",
  tvar = "year",
  gvar = "gvar",
  controls = controls,
  rolling = "demean",
  num_epochs = 500,
  nboot = 20,
  silent = TRUE
)
#> Warning in endid_staggered(data = data, y = y, ivar = ivar, tvar = tvar, :
#> Cohort 2005: skipping (n_treated=1, n_control=29).
#> Warning in endid_staggered(data = data, y = y, ivar = ivar, tvar = tvar, :
#> Cohort 2009: skipping (n_treated=1, n_control=29).
summary(fit_endid)
#> Engression-Based Distributional DiD
#> Design: staggered | Transformation: demean
#> 
#> --- ATT ---
#>    Estimate         SE    CI_Lower  CI_Upper
#>  0.08925857 0.05076003 -0.01259232 0.1439078
#> 
#> --- QTE ---
#>  quantile     effect         se    ci_lower  ci_upper
#>       0.1 0.09150443 0.04809860  0.02296326 0.1525395
#>       0.2 0.07997083 0.04971266 -0.01173930 0.1394781
#>       0.3 0.07479644 0.05023085 -0.02084289 0.1392026
#>       0.4 0.07130147 0.05161743 -0.02918606 0.1312922
#>       0.5 0.06646400 0.05159184 -0.03637839 0.1292239
#>       0.6 0.05978210 0.05381595 -0.04902323 0.1257005
#>       0.7 0.05320906 0.06019162 -0.05815953 0.1185912
#>       0.8 0.04015630 0.06425507 -0.08350026 0.1164800
#>       0.9 0.07905778 0.16469970 -0.12483970 0.4156563
#> 
#> --- Cohort-level Results ---
#>  Cohort        ATT         SE    CI_Lower  CI_Upper N_Treated N_Control
#>    2006 0.05878217 0.06309549 -0.06511310 0.1440374        13        29
#>    2007 0.13247597 0.08839441 -0.02519448 0.2467897         4        29
#>    2008 0.20092036 0.07702016  0.06828795 0.2893615         2        29

Comparing the Results

While lwdidR provides a single point estimate (the average effect), endid allows us to visualize the Quantile Treatment Effects (QTE).

plot(fit_endid)

Interpretation

In this example, lwdidR shows the average effect of castle doctrine laws on log homicide rates. The endid results complement this by showing how the effect varies across quantiles of the distribution. This distributional perspective can be crucial for policy analysis where the impact on the “tails” of the distribution is as important as the average impact.