How csdid compares: same data, five other estimators
Stata now has several commands for staggered difference-in-differences:
csdid (this package), jwdid, did_imputation, did_multiplegt_dyn,
lpdid, and flexdid. They are often described as interchangeable, in the
sense that you pick whichever syntax you like best and get “the” event
study. In this guide we use simulations (you can re-run them line by line)
to show that they are not interchangeable. We also say where the
differences show up, and why.
The short version is this. On a balanced panel with equal-sized periods, most of these estimators agree and speed looks like the only difference (with one exception, as the first table below shows). Once you move away from that ideal, by (i) letting the sampling vary across periods, (ii) unbalancing the panel, or (iii) moving to repeated cross sections, the commands stop estimating the same parameter, and the difference shows up in the target, not in the precision.
A target parameter is an estimand. It should be a fixed feature of the population, and it should not move because your sample did.
Everything below uses one data-generating process (1,000 units per draw, seven periods), fixed effect sizes, and 500 simulation draws per setting. We report bias against the population targets (never against a sample-dependent quantity), along with the standard deviation across draws and the coverage of nominal 95% intervals.
What each command is actually computing
Before any comparison can be fair, the commands have to be placed on a common map. We built that map by controlled experiment, not by reading documentation. Each claim below was confirmed against a quantitative prediction (usually to four decimal places).
Two ways to build the counterfactual. csdid, did_multiplegt_dyn, and
lpdid difference each cohort against its base period (the period right
before treatment). jwdid and did_imputation instead fit unit and time
fixed effects on all untreated observations and impute Y(0) from the fit,
and on a balanced panel those two produce identical point estimates (with
and without covariates; we verified agreement to six decimals with each
command’s documented covariate specification). flexdid’s default
specification joins them. One domain note before any table: flexdid
implements Deb, Norton, Wooldridge and Zabel (2025), whose title scopes the
estimator to repeated cross sections, so this guide compares it there, and
only there.
Identical point estimates are not identical inference. When effects vary
with covariates, did_imputation’s standard errors are deliberately
conservative under that heterogeneity, while jwdid reports conventional
regression inference (on one of our draws the gap grew from 11% at event
time 0 to 55% at event time 2). The two commands run the same estimator
under two inference philosophies, so a practitioner comparing printed
standard errors across them would conclude that they disagree, at the very
point where their point estimates agree to every decimal we checked in
these runs.
The same lesson holds on the differencing side. On designs where csdid
and did_multiplegt_dyn produce identical point estimates, their standard
errors differ by roughly 8%, because the inference conventions differ in
what they treat as random; ours follows Callaway and Sant’Anna (2021) in
treating cohort membership as sampled, so the estimated cohort shares
contribute uncertainty. Pooling every pre-period buys efficiency when
parallel trends holds exactly in every period, and pays for it with
non-locality (anything that happened five periods before treatment is still
in the estimate).
Three ways to average the cells. Every command reports some average of the group-time effects ATT(g,t), and this is where the estimand question lives:
| command | a cohort’s weight at event time e | the weight moves when… |
|---|---|---|
csdid |
its population share, P(G=g) | the population changes |
jwdid, did_imputation, did_multiplegt_dyn, flexdid |
its treated observations at e | the sampling changes |
lpdid |
its effective 2x2 sample size | the adoption order changes |
The first column is a fixed population quantity. The second moves with the
realized sample, so deleting rows changes the target. The third is a
regression artifact: lpdid’s weights come out of pooling switch events
with time fixed effects, and they are nearly invariant to how large a
cohort is. Neither of the last two columns is wrong as arithmetic, and each
answers a well-defined question. Our reading is that if the question is
“what is the average effect for treated units,” only the first column
answers it regardless of how the sample arrived.
Speed
We should say up front that on an ordinary balanced panel (at ordinary
sizes) every command in this comparison is fast enough for real work. The
differences start to matter once you bootstrap or iterate over
specifications. However, at the sizes and designs in the lower rows of these
tables they matter a great deal. Every cell below is the median of 10 timed
runs with one discarded warmup (the first csdid call in a session pays a
one-time library load that no later call pays), with event-study estimation
and clustered standard errors throughout, and with cells labeled by the
design (units n, periods T, cohorts G) and the row count derived from them.
A dash marks a cell without a measured time, and the note says why.
A balanced panel, and what the default costs
The commands’ own defaults differ in how much inference they buy. These two
columns are the same csdid estimation run twice: once with pointwise
analytical standard errors, and once with the shipped default of 999
multiplier-bootstrap draws with simultaneous confidence bands (the macOS
accelerator that ships with the package is active; every other platform
computes identical numbers in Mata).
| n (T=10, G=4) | rows | analytical | full default: 999 draws + uniform bands |
|---|---|---|---|
| 100 | 1,000 | 0.01 | 0.04 |
| 1,000 | 10,000 | 0.04 | 0.07 |
| 10,000 | 100,000 | 0.26 | 0.32 |
| 100,000 | 1,000,000 | 1.80 | 2.38 |
The premium for complete inference shrinks with size (32% at a million rows), and the full default is still faster than any rival’s pointwise analytical time at that size. Complete inference costs about half a second per million rows here. These times depend on the machine and on the Stata version, so the comparison between columns is more portable than the levels are.
Unbalanced panels
Fifteen percent of rows deleted at random from balanced panels. All three
bal() modes are shown, because they are three different estimands whose
speeds are comparable to the rivals’ but not to each other. bal(none)
keeps every observation (the way the other commands do). That is the
apples-to-apples column. bal(pair) balances each 2x2 comparison
separately (Version 1.82’s estimand, available on request). bal(full),
the default, is fastest because it drops the units not observed in every
period before estimating, so its speed comes from a smaller (and disclosed)
sample and not from a faster algorithm. We keep it in the table for
completeness because it is what users get if they say nothing, and its time should be
read as the time for the default’s sample, not as csdid outrunning the
field.
| n (T=10, G=4) | rows | csdid bal(none) |
csdid bal(pair) |
csdid bal(full) |
jwdid |
lpdid |
did_imputation |
|---|---|---|---|---|---|---|---|
| 1,000 | 8,500 | 0.10 | 0.04 | 0.02 | 0.20 | 0.32 | 0.57 |
| 10,000 | 85,000 | 0.69 | 0.29 | 0.11 | 0.70 | 0.85 | 3.85 |
| 100,000 | 850,000 | 3.79 | 2.07 | 0.89 | 6.28 | 5.83 | 38.0 |
Repeated cross sections
The next table reports times with and without one covariate, at each command’s documented covariate specification.
| n per period (T=10, G=4) | rows | csdid |
flexdid |
jwdid |
did_imputation |
|---|---|---|---|---|---|
| 1,000 | 10,000 | 0.12 | 0.19 | 0.16 | 0.24 |
| 10,000 | 100,000 | 0.86 | 0.71 | 0.76 | 1.48 |
| 100,000 | 1,000,000 | 5.56 | 5.53 | 7.22 | 20.9 |
| with a covariate: | |||||
| 1,000 | 10,000 | 0.18 | 0.33 | 0.35 | 0.27 |
| 10,000 | 100,000 | 1.18 | 1.03 | 1.74 | 1.58 |
| 100,000 | 1,000,000 | 6.70 | 7.41 | 17.96 | 21.5 |
flexdid holds a modest but genuine edge at the middle size without
covariates, and the million-row comparison without covariates is close to
a dead heat. Once the covariate is added, csdid
leads at the largest size, while jwdid’s time grows two and a half times
over its own no-covariate run. These are single-covariate designs, and we
don’t extrapolate the ordering to specifications with many covariates.
More periods, more cohorts
Design richness separates the architectures as sharply as sample size does. We vary the number of periods first, holding n = 10,000 fixed.
| T (n=10,000, G=4) | rows | csdid |
jwdid |
lpdid |
did_imputation |
|---|---|---|---|---|---|
| 5 | 50,000 | 0.13 | 0.26 | 0.55 | 1.44 |
| 10 | 100,000 | 0.26 | 0.67 | 1.05 | 3.44 |
| 20 | 200,000 | 0.52 | 2.76 | 1.92 | 8.99 |
| 40 | 400,000 | 1.07 | 12.1 | 3.66 | 20.0 |
csdid scales linearly in T while jwdid does not (at forty periods the
gap is 11x, and did_imputation’s is 19x). In long panels the difference
is large enough to constrain how many specifications a researcher can
afford to run. Cohorts are milder for everyone: from G=3 to G=18 at 200,000
rows, csdid goes 0.45s to 1.52s (there are simply more cells to estimate
and report), and it stays about 4x ahead of the field throughout.
Aggregation is not in these numbers because estat event takes a fraction
of a second after any of these estimations (a table would add nothing). The
comparison against csdid Version 1.82, where the gains run from 16x to
208x depending on the design, has its own page:
Speed against Version 1.82.
Reliability, part I: no covariates
We simulate one population (three treated cohorts of equal size plus a never-treated group, with effects of 2.0, 2.5, and 3.0 at event times 0, 1, and 2) and vary only how the sample arrives from one setting to the next. Nothing in this section involves covariates, which is deliberate, because the estimand question already bites with the cleanest data you are ever likely to see.
A balanced panel
Let’s start with the cleanest case of all. Every command runs on this
design, and all of them except lpdid agree with the target.
| estimator | bias at e=0 | 95% coverage |
|---|---|---|
csdid |
+0.005 | 0.96 |
jwdid |
+0.005 | 0.96 |
did_imputation |
+0.005 | 0.96 |
did_multiplegt_dyn |
+0.005 | 0.95 |
lpdid |
-0.13 | 0.53 |
lpdid is off by 6.5% of the true effect on a perfectly balanced panel
(with nothing exotic in it). The number is neither a bug nor sampling
noise (the bias is the same at n = 4,000). It is what effective-sample-size
weighting does: later cohorts get less weight (because earlier cohorts are
no longer clean comparisons), so the reported event study averages a
different mix of cohorts than the population has. Note that you do not need
missing data for the estimand question to matter, and none of this says
that the weights are the wrong answer to the question they do answer.
Unequal sampling across periods
Next, we keep the same population but let the probability that an observation is recorded vary by calendar period (think of survey waves of different sizes). Nothing about treatment, outcomes, or composition changes, and the stationarity condition of Callaway and Sant’Anna (2021, Assumption B.1) holds exactly. What fails is the auxiliary condition that every period contributes an equally sized cross-section.
| estimator | bias at e=0 | 95% coverage |
|---|---|---|
csdid (any bal() mode) |
+0.003 | 0.95 |
jwdid |
-0.16 | 0.36 |
did_imputation |
-0.17 | 0.38 |
did_multiplegt_dyn |
-0.29 | 0.16 |
lpdid |
-0.40 | 0.02 |
Every observation-weighted estimator is now targeting a different quantity
(one that moves with the sampling), and its confidence intervals cover the
true effect between 2% and 38% of the time. csdid does not move, because
P(G=g) does not move. Restoring equal period sizes (ordinary uniform
missingness) brings the other estimators right back, so the mechanism is
the unequal weighting, not unbalancedness itself.
In our view this is the most consequential table in the guide, and it comes from a violation so mild that most applied descriptions of the data would not even mention it.
Repeated cross sections
With fresh samples each period and equal period sizes, the commands that
support repeated cross sections (csdid via rcs, jwdid,
did_imputation with group fixed effects, and flexdid, on its design
target and the one place it appears in our comparisons) are all fine. Once
the period sizes are unequal (which is entirely ordinary in survey data),
the drift returns, and this time it flips sign across event times instead
of merely growing with the horizon (-0.16 at e=0, +0.23 at e=2 for the
observation-weighted commands). The estimated
event study does not simply shift up or down; its shape bends toward
whatever the sampling did, so a reader would see effects “growing” that do
not grow. csdid reports 0.004 and 0.001 bias at those event times, with
nominal coverage, and its individual ATT(g,t) cells are within 0.02 of
their known truths in every one of the settings we ran.
did_multiplegt_dyn does not claim repeated cross-section support and
returns nothing here, and lpdid refuses to run at all. We prefer the
refusal (a command that stops is easier to deal with than one that returns
a number it cannot support).
The transparency scorecard
Nothing in the next table is a simulation result. Each entry is a documentation-verified statement about what a command reports and about how it says it computes what it reports.
| reports every ATT(g,t) | weights stated in closed form | target fixed under sampling | balance choice explicit | uniform bands | |
|---|---|---|---|---|---|
csdid |
yes | yes | yes | yes (bal(), disclosed in e(panel_mode)) |
yes |
jwdid |
recoverable from coefficients | no | no | no | no |
flexdid |
recoverable from coefficients | no | no | no | no |
did_imputation |
no | no | no | no (autosample decides) |
no |
did_multiplegt_dyn |
no | no | no | no (imputes treatment paths) | no |
lpdid |
no | no | no | no | no |
csdid reports every ATT(g,t) cell with its standard error, states its
aggregation weights in closed form, discloses the resolved sample in
e(panel_mode), and leaves the balancing rule (bal()) to you. Several of
the other commands never show the cells at all. Thus we treat
decomposability as a requirement: we cannot audit an estimator whose
output we cannot take apart. The
command whose cells are visible is also the one whose target did not move
in these tables, which we do not think is a coincidence (though that is a
judgment about how these commands were designed rather than a simulation
result).
Reliability, part II: covariates, and the case for doubly robust
Now we add covariates: time-invariant unit characteristics (think of gender, or of earnings in a year before the sample starts) whose distribution differs across cohorts, and which shift both the level and the trend of the untreated outcome. This is a conditional parallel trends design, so the unconditional comparison is biased by construction and everything turns on how a command handles the covariates. Treatment effects still do not depend on the covariates (so the targets are the ones we started with).
The first table takes a balanced panel with every model correctly specified (500 reps, bias and coverage at e=0).
| estimator | bias | coverage |
|---|---|---|
csdid dr |
+0.001 | 0.95 |
csdid ipw |
+0.004 | 0.95 |
csdid reg |
-0.001 | 0.95 |
jwdid |
+0.001 | 0.95 |
did_imputation |
+0.000 | 0.96 |
did_multiplegt_dyn |
+0.119 | 0.61 |
lpdid |
-0.139 | 0.46 |
Every command that handles covariates the way its own documentation
describes is fine here. The exception is did_multiplegt_dyn (SSC version
dated January 17, 2026, the current release as we write), whose bias drifts
with the horizon (reaching +0.88 by e=2), and the reason is not a defect in
the code but a covariate adjustment that answers a different question. Its
controls() option imposes parallel trends on outcomes residualized on the
first differences of the covariates (with a single coefficient common to
all groups and periods).
That is a different assumption from conditional parallel trends in Callaway and Sant’Anna (2021), and it is more restrictive in two specific ways. First, for time-invariant covariates (gender, race, baseline earnings, and most of what applied researchers condition on in practice) the first differences are zero, so the residualization leaves the unconditional comparison unchanged and the condition collapses to unconditional parallel trends, which is the assumption the covariates were brought in to relax. Second, giving every group and period one common coefficient restricts how covariates may shift trends heterogeneously across cohorts, which is the heterogeneity that conditional parallel trends is there to allow. Note that this literature began from the observation that imposing homogeneity on treatment effects breaks TWFE, and this covariate scheme re-imposes homogeneity one layer down (on the covariate coefficients rather than on the effects).
On its own terms (genuinely time-varying covariates whose effect on trends
really is common across cohorts) the approach works, and our experiments
confirm that it works. Those terms are narrow, though, and they are not the
terms that most applied covariate stories satisfy. The documentation offers
trends_nonparam(), which does exact matching on discrete covariates, as
the route for time-invariant characteristics; matching on our binary
covariate removes its share of the drift (+0.88 falls to +0.66 at e=2), and
continuous covariates are outside that option’s scope. Conditional parallel
trends as in Callaway and Sant’Anna (2021) asks for none of this, since the
trend may depend on the covariates flexibly and heterogeneously, and
csdid’s doubly robust estimator conditions on the covariates themselves
(which is why its column of this table is clean).
The covariates also change nothing about the conclusion of Part I.
Rerunning the repeated-cross-section comparison with the covariates
included, csdid dr stays on target in both sampling regimes while the
regression-style estimators reproduce their unequal-period drift,
covariates and all, so adjusting for covariates does not repair a weighting
scheme.
We prefer dr among the three csdid methods, even where all three are
clean, because correct specification is not something a researcher gets
to assume. The doubly robust estimator (Sant’Anna and Zhao, 2020) is
consistent if either the outcome model or the treatment-probability
model is correctly specified, which gives two chances instead of one at no
speed cost (see the Speed section above) and, in these simulations, at no
precision cost either. Every other command in this comparison is an
outcome-regression estimator, and so gets one chance at the specification.
Let’s break the models on purpose then, one at a time, using the same population and the same targets as above. Take the outcome model first, and add a nonlinear term to the untreated trend (it now depends on the square of the continuous covariate) while every estimator keeps conditioning on the covariates in levels. This is the case where the outcome model the researcher wrote down is wrong, and it is not an exotic case. Bias and coverage at e=1 are as follows.
| estimator | bias | coverage |
|---|---|---|
csdid dr |
-0.058 | 0.94 |
csdid ipw |
-0.054 | 0.93 |
csdid reg |
+0.300 | 0.46 |
jwdid |
+0.490 | 0.26 |
did_imputation |
+0.189 | 0.72 |
did_multiplegt_dyn |
+0.303 | 0.22 |
lpdid |
-0.162 | 0.68 |
(did_multiplegt_dyn and lpdid arrive at this cell already carrying
their correctly-specified-cell biases; the misspecification compounds on
top.)
Every outcome-regression command breaks together, and the bias grows with
the horizon (jwdid reaches +0.67 by e=2, with coverage 0.29). This is not
a defect in any one implementation; it is the same wrong outcome model
failing everywhere it is the only line of defense. The two csdid
estimators that do not rely on it remain consistent here: ipw because the
treatment-probability model is still correct, and dr because one correct
model is all it requires.
We think of that second chance as basic insurance, and no other command in this comparison offers it.
Next, the propensity score. Our first attempt at this cell is worth
reporting because it failed: we made cohorts differ in the spread of the
continuous covariate (a logit linear in X can’t represent that) and nothing
broke, ipw included. The reason is instructive. A fitted logit projects
the selection it can see onto the covariates you gave it, so the error that
the misspecification leaves behind is close to orthogonal to anything
linear in those covariates, and our trend here is linear in them by
construction. An error
orthogonal to the trend has nothing to load on. We read this as a caution
about misspecification experiments in general, in that they teach you
something only when the error is aimed at the target.
In the redesigned cell, selection is driven by a latent index, and the covariate the researcher observes is a nonlinear (exponential) transform of that index, which is the same device used in Sant’Anna and Zhao (2020). The true propensity score is then a logit in the log of the observed covariate, the fitted logit in the covariate itself is genuinely wrong, and (because the observed covariate is skewed) the error loads on the trend. The outcome stays linear in what the researcher observes, so every outcome model remains correct and only the treatment-probability model is broken. Bias and coverage at e=1 are as follows.
| estimator | bias | coverage |
|---|---|---|
csdid dr |
+0.001 | 0.95 |
csdid ipw |
-0.094 | 0.97 |
csdid reg |
-0.001 | 0.95 |
jwdid |
+0.001 | 0.92 |
did_imputation |
+0.002 | 0.93 |
did_multiplegt_dyn |
+0.229 | 0.28 |
lpdid |
-0.112 | 0.66 |
(As before, did_multiplegt_dyn and lpdid carry their
correctly-specified-cell biases into this one.)
The outcome-regression commands are fine here, since a wrong propensity
score is invisible to an estimator that never uses one. What moves is
inside csdid: ipw’s bias grows with the horizon (−0.04, −0.09, −0.14)
and concentrates where the propensity score does the most work. In the
ATT(5,6) cell, where the never-treated are the only comparison left, ipw
is off by −0.23 and pays more than twice dr’s standard error (0.35
against 0.16). The wrong weights cost it twice (in location and in spread),
and those wide intervals are also why its coverage does not collapse the
way reg’s did in the outcome cell. dr pays neither price here, because
it still has one correctly specified model in hand.
One more thing happened in this cell, and we regard it as a feature: in 36
of the 500 draws, csdid refused to estimate the worst-overlap cell at all
and printed the overlap violation it found. The reported ipw and dr rows
condition on the draws that passed that check, which, if
anything, flatters ipw, since the refused draws are the ones where its
weights are most extreme. No other command in this comparison performs such
a check.
Reading the two sabotage cells together, dr is the only estimator here
that stays consistent in both of them: with a wrong outcome model, every
outcome-regression command is biased and dr is not, and with a wrong
propensity score, ipw drifts and dr is again unaffected. Every other
command in the comparison relies on a single model, so whether it is
consistent depends on whether that model happens to be the broken one. We
recommend dr as the default for that reason (double robustness buys
protection against one wrong model, and it does not cover both being wrong
at once).
Precision
Where the targets coincide (a balanced panel with no covariates), comparing spread is fair. Which estimator is more precise, though, is not a fact about the estimators; it is a fact about the error process, and one simulation setting cannot settle it. We therefore ran two. The population and the 500 draws are the same in both, and only the within-unit error process changes.
| errors | csdid sd |
jwdid/did_imputation sd |
tighter |
|---|---|---|---|
| iid | 0.067 | 0.052 | poolers, by ~22% |
| unit root | 0.047 | 0.062 | csdid, by ~25% |
With iid errors, pooling every pre-period is close to the right thing to do
and the Wooldridge/imputation estimators are tighter (as theory predicts).
With unit-root errors (each unit’s shocks accumulate, which is what much
real panel data looks like), the ranking reverses and base-period
differencing wins by about the same margin. We would therefore not report a
precision ranking without saying which error process produced it. Every
estimator remains unbiased in both settings, and only the variances move.
lpdid is biased in both (for the same weighting reason as above), and no
error process repairs that, which is also why it has no row in a table
about precision.
The general statement is in Chen, Sant’Anna, and Xie (2025): the efficient estimator for these designs depends on the covariance structure of the outcomes, and none of the packaged estimators (ours included, in either error world) attains the semiparametric efficiency bound in general. We would therefore read any cross-package standard-error comparison that does not state the error process as a statement about the setting that was chosen, not about the estimators.
Simultaneous inference
An event study is a family of estimates, and the interval that matters is
the one that covers the whole path at once. csdid reports uniform
confidence bands from the multiplier bootstrap by default, and no other
command in this comparison offers a band option at all. The next table
shows what that is worth on the balanced panel with three post-treatment
event times.
| joint coverage of ES(0), ES(1), ES(2) | |
|---|---|
csdid uniform band |
0.966 |
csdid three pointwise CIs |
0.908 |
jwdid / did_imputation pointwise |
0.888 |
did_multiplegt_dyn pointwise |
0.862 |
lpdid pointwise |
0.444 |
Reading three pointwise 95% intervals as if they jointly covered the path
already costs 4 to 9 points of coverage among the unbiased estimators
(lpdid’s 0.444 is mostly its bias, not its intervals), and with the ten
or fifteen event times of a typical application the arithmetic only gets
worse. We recommend reporting the uniform band whenever the figure will be
read as a statement about the whole path, since that is the interval whose
stated coverage matches the way a plotted band is usually read. Note that
the band is wider than the pointwise intervals by construction, so it is
not a free improvement.
Reproducing everything
Every table above is generated by a script with a fixed seed, with package versions pinned to their current SSC releases, and with population targets computed once, by hand, from the data-generating process (never re-derived from a draw). Every script is in the code appendix, ready to run. Note that a fixed seed makes our numbers reproducible, and it does not make them general beyond the designs we chose to simulate.
What we take from this
The experiment was six commands run against one simulated population, with the population targets computed in advance and held fixed throughout. We want to be clear about what it did and did not show.
It did not show that anyone else’s software is broken. Every command in this comparison was written by researchers whose work moved this literature forward, and the differences we measured are not bugs; they are design choices (made in the open, by people who understood them) that answer slightly different questions.
What the tables do show is that “slightly different questions” stops being
a small difference as soon as the data are less than ideal. Holding the
population fixed and changing only how the sample arrives, commands that
mostly agree on a balanced panel separate by whole coverage points, because
their targets travel with the sample while the truth does not. Break one
model and every estimator that relies on a single model breaks with it,
while the doubly robust default in this comparison still has a second model
to fall back on. Read pointwise intervals as if they were a band and the
plotted figure claims more certainty than the estimates support. A reader
looking only at a printed table would see none of those three failures.
That is why we built csdid to report the things that would reveal them: a
target fixed in the population, every ATT(g,t) cell with its own standard
error, aggregation weights in closed form, sample decisions disclosed in
e(), overlap failures printed rather than absorbed, and uniform bands
whose stated coverage applies to the whole path. Speed matters here because
the reporting has to be cheap enough to leave switched on by default.
We recommend judging any of these commands by whether its output can be
taken apart and recomputed, which is the standard we tried to hold csdid
to, and the tables above are what came of trying. csdid does come out
ahead in most of them, though the reason we are showing them is that every
number in them can be recomputed from a seed by anyone, including the
numbers where csdid loses. Note that none of this establishes that
csdid is the right command for any particular application: these are
simulations from designs we chose, and a design we did not simulate can
reverse any ranking on this page.