Castle Doctrine Example

This vignette applies Endid.jl to the Castle Doctrine dataset with staggered adoption timing.

Data Preparation

using Endid
using DataFrames
using CSV
using Statistics
using Plots
using Random

Random.seed!(42)

castle = CSV.read(joinpath(pkgdir(Endid), "vignettes", "data", "castle.csv"), DataFrame)
castle.gvar = [ismissing(y) || y == 0 ? missing : y for y in castle.effyear]
controls = ["poverty", "unemployrt", "blackm_15_24", "whitem_15_24"]

cohorts = combine(groupby(castle, :sid), :gvar => first => :gvar)
combine(groupby(cohorts, :gvar), :sid => length => :count)
6×2 DataFrame
Rowgvarcount
Int64?Int64
120051
2200613
320074
420082
520091
6missing29

Distributional Estimation

fit_endid = endid_staggered(
    castle,
    :lhomicide,
    :sid,
    :year,
    :gvar;
    controls = controls,
    rolling = "demean",
    num_epochs = 100,
    nboot = 5,
    seed = 42,
)

println(fit_endid)
┌ Warning: Cohort 2005: skipping (degenerate cross-section).
└ @ Endid ~/work/Endid.jl/Endid.jl/src/Endid.jl:339

Training Engression...  23%|██████▌                     |  ETA: 0:00:02
Training Engression... 100%|████████████████████████████| Time: 0:00:01
   loss: 0.36839837
┌ Warning: Cohort 2009: skipping (degenerate cross-section).
└ @ Endid ~/work/Endid.jl/Endid.jl/src/Endid.jl:339
Endid Result (staggered design)
------------------------------
ATT Estimate: 0.1007
Std. Error  : 0.034
95% CI      : (0.0659, 0.1433)

Quantile Treatment Effects (QTE):
9×3 DataFrame
 Row │ quantile  effect     se
     │ Float64   Float64    Float64
─────┼────────────────────────────────
   1 │      0.1  0.0502398  0.0235922
   2 │      0.2  0.0484497  0.0203099
   3 │      0.3  0.0501444  0.0169832
   4 │      0.4  0.0518725  0.0136774
   5 │      0.5  0.0559127  0.0134986
   6 │      0.6  0.060126   0.0146324
   7 │      0.7  0.0718326  0.023724
   8 │      0.8  0.12527    0.0774524
   9 │      0.9  0.314252   0.161099

Quantile Treatment Effects

p = plot(fit_endid)
savefig(p, "castle_qte.svg")
nothing