You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix not-yet-treated control mask to respect anticipation parameter
The not-yet-treated control group in both ContinuousDiD and
CallawaySantAnna used `G > t` instead of `G > t + anticipation`,
incorrectly including cohorts in the anticipation window as controls.
This matches R's `did::compute.att_gt()` logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Detection: Pivoted QR decomposition with tolerance `1e-07` (R's `qr()` default)
346
348
- Handling: Warns and drops linearly dependent columns, sets NA for dropped coefficients (R-style, matches `lm()`)
@@ -378,7 +380,7 @@ The multiplier bootstrap uses random weights w_i with E[w]=0 and Var(w)=1:
378
380
- Always excludes cohort g from controls when computing ATT(g,t)
379
381
- This applies to both pre-treatment (t < g) and post-treatment (t >= g) periods
380
382
- For pre-treatment periods: even though cohort g hasn't been treated yet at time t, they are the treated group for this ATT(g,t) and cannot serve as their own controls
381
-
- Control mask: `never_treated OR (first_treat > t AND first_treat != g)`
383
+
- Control mask: `never_treated OR (first_treat > t + anticipation AND first_treat != g)`
382
384
383
385
**Reference implementation(s):**
384
386
- R: `did::att_gt()` (Callaway & Sant'Anna's official package)
@@ -427,6 +429,10 @@ This is stronger than standard PT because it conditions on specific dose values.
-**Rank deficiency**: When n_treated <= n_basis, cell is skipped.
429
431
-**Balanced panel required**: Matches R `contdid` v0.1.0.
432
+
-**Anticipation + not-yet-treated**: Control mask uses `G > t + anticipation`
433
+
(not just `G > t`) to exclude cohorts in the anticipation window from
434
+
not-yet-treated controls. When `anticipation=0` (default), behavior is
435
+
unchanged.
430
436
-**Boundary knots**: Knots are built once from all treated doses (global, not per-cell) to ensure a common basis across (g,t) cells for aggregation. Evaluation grid is clamped to training-dose boundary knots (`range(dose)`). R's `contdid` v0.1.0 has an inconsistency where `splines2::bSpline(dvals)` uses `range(dvals)` instead of `range(dose)`, which can produce extrapolation artifacts at dose grid extremes. Our approach avoids extrapolation and is methodologically sound.
0 commit comments