Examples
The examples below use the merit.csv dataset included in the package's test suite (test/data/merit.csv). It contains individual-level survey data on college attendance (coll), state identifiers (state), survey year (year), and demographic covariates (male, asian, black). The dataset covers U.S. states over the period 1989–2000 and is used to study the effect of merit scholarships on college attendance.
using DiDInt, CSV, DataFrames
merit = CSV.read(joinpath(@__DIR__, "../../test/data/merit.csv"), DataFrame);
first(merit, 5)| Row | coll | merit | male | black | asian | year | state | chst | styr | eta | alpha | stch | stchid | ind | stnchid | stid |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Float64 | Float64 | Float64 | Float64 | Float64 | Int64 | Int64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64? | Float64 | Float64? | Float64 | |
| 1 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1993 | 11 | 0.0 | 12993.0 | -0.383748 | 0.0337465 | 0.0 | missing | 52.0 | 11.0 | 1.0 |
| 2 | 1.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1997 | 11 | 0.0 | 12997.0 | 0.627288 | 0.0337465 | 0.0 | missing | 52.0 | 11.0 | 1.0 |
| 3 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1992 | 11 | 0.0 | 12992.0 | 0.547032 | 0.0337465 | 0.0 | missing | 52.0 | 11.0 | 1.0 |
| 4 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1989 | 11 | 0.0 | 12989.0 | -0.363045 | 0.0337465 | 0.0 | missing | 52.0 | 11.0 | 1.0 |
| 5 | 0.0 | 0.0 | 1.0 | 0.0 | 0.0 | 1991 | 11 | 0.0 | 12991.0 | -0.372118 | 0.0337465 | 0.0 | missing | 52.0 | 11.0 | 1.0 |
Staggered Adoption
This example illustrates a staggered adoption setting in which ten states adopted merit-aid programmes at different points between 1991 and 2000.
Running didint()
# Note that treated_states & state column in the merit data could alternatively
# be string vectors, here they just happen to be numeric codes instead of state names
treated_states = [34, 57, 58, 59, 61, 64, 71, 72, 85, 88];
treated_times = [2000, 1998, 1993, 1997, 1999, 1996, 1991, 1998, 1997, 2000];
result = DiDInt.didint(
"coll", "state", "year", merit;
treatment_times = treated_times,
treated_states = treated_states,
seed = 1234,
ccc = "state",
covariates = [:male, :asian, :black],
agg = "cohort",
nperm = 399
);| Row | treatment_time | att_cohort | agg_att | se_agg_att | pval_agg_att | jknifese_agg_att | jknifepval_agg_att | ri_pval_agg_att | nperm | se_att_cohort | pval_att_cohort | jknifese_att_cohort | jknifepval_att_cohort | ri_pval_att_cohort | weights | period | start_date | end_date |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64 | String | String | String | |
| 1 | 1991-01-01 | 0.0541082 | 0.0458172 | 0.00762722 | 0.000958593 | 0.0124565 | 0.000574488 | 0.100251 | 399.0 | 0.0215304 | 0.0123425 | missing | missing | 0.438596 | 0.201796 | 1 year | 1989 | 2000 |
| 2 | 1993-01-01 | 0.0501653 | missing | missing | missing | missing | missing | missing | missing | 0.0177872 | 0.00508455 | missing | missing | 0.503759 | 0.191535 | 1 year | 1989 | 2000 |
| 3 | 1996-01-01 | 0.0377853 | missing | missing | missing | missing | missing | missing | missing | 0.0236315 | 0.111352 | missing | missing | 0.606516 | 0.0756734 | 1 year | 1989 | 2000 |
| 4 | 1997-01-01 | 0.060813 | missing | missing | missing | missing | missing | missing | missing | 0.0220919 | 0.00655222 | 0.0328113 | 0.0697273 | 0.270677 | 0.321077 | 1 year | 1989 | 2000 |
| 5 | 1998-01-01 | 0.0393736 | missing | missing | missing | missing | missing | missing | missing | 0.0386538 | 0.310319 | 0.0866108 | 0.651362 | 0.561404 | 0.108593 | 1 year | 1989 | 2000 |
| 6 | 1999-01-01 | 0.00217049 | missing | missing | missing | missing | missing | missing | missing | 0.0125215 | 0.862811 | missing | missing | 0.992481 | 0.0354853 | 1 year | 1989 | 2000 |
| 7 | 2000-01-01 | -0.0219891 | missing | missing | missing | missing | missing | missing | missing | 0.0319559 | 0.495261 | 0.0952693 | 0.818405 | 0.822055 | 0.0658401 | 1 year | 1989 | 2000 |
Inspect the aggregate-level ATT and inference columns:
result[:, 3:9]| Row | agg_att | se_agg_att | pval_agg_att | jknifese_agg_att | jknifepval_agg_att | ri_pval_agg_att | nperm |
|---|---|---|---|---|---|---|---|
| Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | |
| 1 | 0.0458172 | 0.00762722 | 0.000958593 | 0.0124565 | 0.000574488 | 0.100251 | 399.0 |
| 2 | missing | missing | missing | missing | missing | missing | missing |
| 3 | missing | missing | missing | missing | missing | missing | missing |
| 4 | missing | missing | missing | missing | missing | missing | missing |
| 5 | missing | missing | missing | missing | missing | missing | missing |
| 6 | missing | missing | missing | missing | missing | missing | missing |
| 7 | missing | missing | missing | missing | missing | missing | missing |
And the cohort-level ATT and inference columns:
result[:, vcat([1, 2], 10:ncol(result))]| Row | treatment_time | att_cohort | se_att_cohort | pval_att_cohort | jknifese_att_cohort | jknifepval_att_cohort | ri_pval_att_cohort | weights | period | start_date | end_date |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64 | String | String | String | |
| 1 | 1991-01-01 | 0.0541082 | 0.0215304 | 0.0123425 | missing | missing | 0.438596 | 0.201796 | 1 year | 1989 | 2000 |
| 2 | 1993-01-01 | 0.0501653 | 0.0177872 | 0.00508455 | missing | missing | 0.503759 | 0.191535 | 1 year | 1989 | 2000 |
| 3 | 1996-01-01 | 0.0377853 | 0.0236315 | 0.111352 | missing | missing | 0.606516 | 0.0756734 | 1 year | 1989 | 2000 |
| 4 | 1997-01-01 | 0.060813 | 0.0220919 | 0.00655222 | 0.0328113 | 0.0697273 | 0.270677 | 0.321077 | 1 year | 1989 | 2000 |
| 5 | 1998-01-01 | 0.0393736 | 0.0386538 | 0.310319 | 0.0866108 | 0.651362 | 0.561404 | 0.108593 | 1 year | 1989 | 2000 |
| 6 | 1999-01-01 | 0.00217049 | 0.0125215 | 0.862811 | missing | missing | 0.992481 | 0.0354853 | 1 year | 1989 | 2000 |
| 7 | 2000-01-01 | -0.0219891 | 0.0319559 | 0.495261 | 0.0952693 | 0.818405 | 0.822055 | 0.0658401 | 1 year | 1989 | 2000 |
Running didint_plot()
didint_plot() can produce data for two types of plots depending on the event argument.
By default, the outputted dataset will include the parallel trends (or event study) data for each ccc option ("hom", "time", "state", "add", "int") as well as the data estimated without covariates ("none"). You can specify which ccc options you want to plot by inputting a vector of strings to the ccc argument.
Parallel trends plot data
Setting event = false returns a long-format DataFrame of residualised outcome means by state and period, suitable for plotting parallel trends across CCC specifications.
plot_parallel = DiDInt.didint_plot(
"coll", "state", "year", merit;
treatment_times = treated_times,
treated_states = treated_states,
event = false,
covariates = ["asian", "black", "male"]
);
plot_parallel[1:4, :]| Row | state | time | lambda | ccc | period | start_date | treat_period | period_length |
|---|---|---|---|---|---|---|---|---|
| String? | String? | Float64? | String? | Int64? | String? | Int64? | String | |
| 1 | 11 | 1989 | 0.467895 | hom | 0 | 1989 | missing | 1 year |
| 2 | 11 | 1990 | 0.316912 | hom | 1 | 1989 | missing | 1 year |
| 3 | 11 | 1991 | 0.445936 | hom | 2 | 1989 | missing | 1 year |
| 4 | 11 | 1992 | 0.579997 | hom | 3 | 1989 | missing | 1 year |
Event study plot data
Setting event = true returns a DataFrame of treated-state means by period relative to treatment, along with standard errors and confidence interval bounds, suitable for plotting an event study.
plot_event = DiDInt.didint_plot(
"coll", "state", "year", merit;
treatment_times = treated_times,
treated_states = treated_states,
event = true,
covariates = ["asian", "black", "male"]
);
plot_event[1:4, :]| Row | ccc | time_since_treatment | y | se | ci_lower | ci_upper | ngroup | period_length |
|---|---|---|---|---|---|---|---|---|
| String | Int64 | Float64 | Float64? | Float64? | Float64? | Int64 | String | |
| 1 | hom | -11 | 0.528292 | 0.0964386 | -0.697077 | 1.75366 | 2 | 1 year |
| 2 | hom | -10 | 0.447675 | 0.0554866 | 0.208936 | 0.686415 | 3 | 1 year |
| 3 | hom | -9 | 0.470806 | 0.0223596 | 0.408725 | 0.532886 | 5 | 1 year |
| 4 | hom | -8 | 0.452972 | 0.0248907 | 0.392067 | 0.513878 | 7 | 1 year |
The lambda column in the parallel trends output contains the residualised outcome for each state-period cell. The ccc column indicates which model was used ("hom", "time", "state", "add", or "int"). In the event study output, time_since_treatment = 0 marks the treatment period; negative values are pre-treatment and positive values are post-treatment.
Common Adoption
This example illustrates a common adoption setting: a single treated state (state "71", treated in 1991) compared against a single untreated control state (state "73"). The data are filtered to just these two states before calling didint().
merit_common = filter(row -> row.state ∈ [71, 73], merit);
result_common = DiDInt.didint(
"coll", "state", "year", merit_common;
treated_states = [71],
treatment_times = [1991],
seed = 1234,
ccc = "hom",
covariates = [:male, :asian, :black],
agg = "cohort",
edgecase = true
)
# Note here that I am setting `edgecase = true` in order to recover the standard error
# For more information, see the section "Computation of Edge Case Standard Errors" on the
# "Details" page of the docoumentation site| Row | agg_att | se_agg_att | pval_agg_att | jknifese_agg_att | jknifepval_agg_att | ri_pval_agg_att | nperm |
|---|---|---|---|---|---|---|---|
| Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | |
| 1 | -0.0140535 | 0.0721721 | missing | missing | missing | 1.0 | 1.0 |
Trying a different CCC assumption
The ccc argument controls the assumption made about how covariate effects vary across states and time. Swapping ccc = "hom" for any of "time", "state", "add", or "int" changes whether covariate effects are allowed to vary by time, by state, additively by both, or interactively by both, respectively. For example:
result_common_int = DiDInt.didint(
"coll", "state", "year", merit_common;
treated_states = [71],
treatment_times = [1991],
seed = 1234,
ccc = "int",
covariates = [:male, :asian, :black],
agg = "cohort",
edgecase = true
)| Row | agg_att | se_agg_att | pval_agg_att | jknifese_agg_att | jknifepval_agg_att | ri_pval_agg_att | nperm |
|---|---|---|---|---|---|---|---|
| Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | |
| 1 | 0.0273322 | 0.113821 | missing | missing | missing | 0.0 | 1.0 |
See the Functions for function syntax and the Under the Hood page for more details.