Trimming and overlap

With covariates, method(dr) and method(ipw) weight comparison units by a propensity score (the estimated probability of belonging to the treated cohort). A unit with a score near 1 gets an enormous weight, and a handful of such units can dominate the estimate and inflate its variance at the same time. That is why the problem is easy to miss in a table of point estimates.

That is an overlap problem: the data contain treated units with nothing comparable to compare them against. csdid guards against it in two ways, a warning and a trimming rule, both visible in the output.

The data

Let’s build the sample once and vary only the trimming threshold (everything else about the specification is held fixed below).

import delimited using ///
    "https://raw.githubusercontent.com/pedrohcgs/JEL-DiD/50f4f18/data/county_mortality_data.csv", ///
    clear varnames(1) bindquote(strict) stringcols(_all)
destring deaths population_20_64 year yaca county_code stfips unemp_rate poverty_rate, ///
    replace force
generate double mrate = 100000 * deaths / population_20_64
drop if missing(mrate) | population_20_64 <= 0
generate int gvar = yaca
replace gvar = 0 if missing(gvar) | gvar > 2016
bysort county_code: generate byte nyears = _N
keep if nyears == 11
save "jel_overlap.dta", replace

The overlap warning

Every dr and ipw cell is checked, cell by cell rather than once for the sample as a whole. If the fitted propensity scores get too close to 1, csdid warns for that cohort–period cell and keeps going. You learn which particular comparison is fragile, and it is not averaged silently into the cohort total:

use "jel_overlap.dta", clear
csdid mrate unemp_rate poverty_rate, ivar(county_code) time(year) gvar(gvar) ///
    method(dr) analytical
estat event

No warning here means overlap held in every cell for these covariates. This is good news! However, add enough covariates and it will not hold. The more you condition on, the easier it becomes to predict cohort membership perfectly.

Trimming

pscoretrim() caps how extreme a propensity score may get, and the default is a deliberately mild .995:

use "jel_overlap.dta", clear
csdid mrate unemp_rate poverty_rate, ivar(county_code) time(year) gvar(gvar) ///
    method(dr) analytical
display "trim: " e(pscoretrim)
estat simple

Tighten it to see how sensitive the estimate is to the most extreme weights (a cheap robustness check):

use "jel_overlap.dta", clear
csdid mrate unemp_rate poverty_rate, ivar(county_code) time(year) gvar(gvar) ///
    method(dr) pscoretrim(0.95) analytical
display "trim: " e(pscoretrim)
estat simple

Turn it off with pscoretrim(1), which permits any score (including scores that ought to worry you):

use "jel_overlap.dta", clear
csdid mrate unemp_rate poverty_rate, ivar(county_code) time(year) gvar(gvar) ///
    method(dr) pscoretrim(1) analytical
estat simple

A value of zero or below is refused (the run stops), since it would trim away every observation in the sample:

use "jel_overlap.dta", clear
capture noisily csdid mrate unemp_rate, ivar(county_code) time(year) gvar(gvar) ///
    method(dr) pscoretrim(0) analytical
display "return code: " _rc

Reading the sensitivity

If the estimate moves a lot between pscoretrim(1) and a tighter bound, then a small number of extreme-weight units are driving the result. We would report that. It tells a reader that the estimate rests on units with very few counterparts in the comparison group, and that a different trimming rule would have given a different answer.

Trimming changes the estimand slightly. It reweights toward the region of common support, the region where comparison units actually exist. That is usually preferable to an estimate dominated by units with no real comparison. It is still a choice, so state the value you used.

Avoiding the problem

It is easier to keep overlap at the design stage than to repair it afterwards, so we think about it when choosing the specification.

method(dr), the default, is doubly robust: it is consistent if either the outcome model or the propensity model is correct. That is why we made it the default, and it is the reason overlap matters less here than it does for plain ipw. Double robustness still needs overlap, though, and we would read the warnings before trusting any individual cell. See Covariates and estimators.

capture erase "jel_overlap.dta"