Upgrading from csdid Version 1.82
Throughout this page, Version 1.82 means the release that SSC distributes via
ssc install csdid (dated 2025-10-05).
The command surface is deliberately unchanged, so most do-files run exactly as they are and we expect the upgrade to be uneventful. However, three things can need your attention: results that move, commands that are deprecated, and option spellings that have been renamed.
Results that move
Two omitted-option defaults changed, and on unbalanced panels the balancing default changed too (see Unbalanced panels below). Those are the only changes that alter a number you were already getting.
| Version 1.82 | 2.0.0 | To keep the old behavior | |
|---|---|---|---|
| comparison group | never-treated | not-yet-treated | nevertreated |
| base period | varying | universal | base_period(varying) |
Both are also departures from R did 2.5.1, which shares Version 1.82’s two
defaults. State either option explicitly and csdid and R agree to machine
precision, which is a check we run before every release.
A third change concerns inference and leaves the estimand alone: standard errors
are now the multiplier bootstrap with simultaneous confidence bands by default,
where Version 1.82 reported pointwise analytical standard errors, though the point
estimates are unaffected by this (the change is in the standard errors alone) and
analytical restores the old ones for anyone who wants them back.
To reproduce a Version 1.82 run, state all three options explicitly (none of them is implied by the others). Let’s load the county mortality panel used throughout this site:
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, 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_upgrade.dta", replace
use "jel_upgrade.dta", clear
csdid mrate, ivar(county_code) time(year) gvar(gvar) ///
nevertreated base_period(varying) analytical
display "comparison group: " e(control_group)
display "base period: " e(base_period)
estat event
Those three options give you Version 1.82’s estimand together with Version 1.82’s standard errors. Drop them and you get the 2.0.0 defaults instead:
use "jel_upgrade.dta", clear
csdid mrate, ivar(county_code) time(year) gvar(gvar) rseed(20250101)
display "comparison group: " e(control_group)
display "base period: " e(base_period)
estat event
Unbalanced panels
Version 1.82 dropped the units missing from either period of each 2×2 comparison,
and it did so silently (without a message of any kind). csdid 2.0.0 makes the choice explicit with bal() and
reports whatever it drops (units and observations both):
bal(full) |
drop units not observed in every period, once, for all comparisons. Default, matching R did. |
bal(none) |
keep every unit and use the repeated-cross-section computation. |
bal(pair) |
Version 1.82’s per-comparison balancing: each 2x2 keeps the units observed in both of its periods. |
bal(pair) reproduces Version 1.82’s estimand exactly, so an unbalanced-panel
result from that version can be reproduced here by asking for it.
Deprecated commands
These still ship and still run, so nothing in an old do-file breaks, though each of them now prints a notice (a notice, not an error) saying what to use instead.
| Deprecated | Use instead |
|---|---|
csdid_rif |
estat attgt, saving(results) replace then use results, clear |
csdid_table |
the table csdid prints, or estat tidy, saving() for the numbers |
dipt |
never documented; no replacement |
tsvmat |
never documented; no replacement |
csgvar is not deprecated, since it builds the gvar() cohort variable from a
treatment indicator and is fully supported.
The saved-RIF workflow itself is also still supported, in that csdid_stats using
filename reads a saved RIF file, and only the table-building command around it
is deprecated.
help csdid_legacy documents all of this inside Stata (offline), so you don’t have to
keep this page open beside you.
Renamed options
Every Version 1.82 spelling below is accepted and says what to use instead, so you find out by running your do-file. Nothing on the list will stop a do-file from running.
| Old (warns) | Current |
|---|---|
long, long2 |
base_period(universal), which is now the default, so usually nothing |
method(dripw) |
method(dr) |
method(stdipw) |
method(ipw) |
asinr |
no-op; use notyet |
never |
nevertreated. Not a no-op: the not-yet-treated comparison group is the default now, so this changes which units the treated are compared against |
Spellings that are not renames
These are alternative names for current options, so they are not deprecated, nothing warns, and there is nothing at all to change in a do-file that uses them.
| Spelling | Same as |
|---|---|
baseperiod() |
base_period() |
id() |
ivar() |
vce(cluster var) |
cluster(var) |
fixweights(base) |
fix_weights(base_period) |
balance() |
bal(), the same option unabbreviated |
unbalanced |
bal(none). Typed in full – unbal is not an option |
allowunbalanced, allow_unbalanced |
bal(none) as well; the R-style longhand, also typed in full |
Spellings that were never options
You may have seen these somewhere, but they have never been options in any release. They are not in Version 1.82, and 2.0.0 is the first release of the rewrite. csdid refuses them the way it refuses any other name it does not know.
| Not an option | Type instead |
|---|---|
bal(unbal), bal(unbalanced), bal(allow_unbalanced) |
bal(none), or the unbalanced option |
balanceall, bal(all) |
bal(full) |
balancepair |
bal(pair) |
lean, performance(...) |
nothing: influence functions stay internal at every size, and storeall is the one switch that copies them into e() |
Options that now refuse
Version 1.82 accepted these and then did something else instead. Refusing them is the change we made, on the grounds that a silently substituted setting is a result you did not ask for and would not know you had. We would rather stop a run than return a number we cannot vouch for.
| Option | What happens now |
|---|---|
wboot(wtype(mammen)), gaussian, normal |
Errors. Only the Rademacher multiplier is supported; these used to be coerced to it |
wboot(reps(#)) with # ≤ 20 |
Errors. Too few iterations to support a simultaneous band |
pscoretrim(#) with # ≤ 0 |
Errors. Omit for the default .995, or pass 1 for no trimming |
gvar() with negative values |
Errors. Use 0 for never-treated |
from() |
Removed. It set a lower event-time bound on the simple, group and calendar aggregations, and R fixes that bound at event time 0, so there is no equivalent. from(0) was the legacy default and is already what csdid does, so most uses were no-ops. For event-time windows use estat event, window(# #) |
dryrun |
Rejected; it was an internal option |
What you gain
There is no external dependency (no drdid, and nothing else). Version 1.82’s SSC
entry reads Requires: Stata version 14 and drdid from SSC, while 2.0.0 needs
nothing beyond Stata itself. It is also 5× to 28× faster on every workload we measured, and never
slower, with the per-workload benchmark table in the package README; the gains by
sample size, periods, cohorts, and sampling scheme (16× to 208×) are on
their own page.
capture erase "jel_upgrade.dta"
Back to the guides.