Skip to content

Commit 471b1ea

Browse files
igerberclaude
andcommitted
Address PR igerber#192 review (Round 7): fix REGISTRY labels, remove unused params, add inline comments
Relabel REGISTRY.md PT-All and bootstrap entries with **Note:** prefix so the review prompt's deviation-detection logic recognizes them as documented choices. Remove unused parameters (target_t from enumerate_valid_triples, att_gt from compute_eif_nocov) and all call sites. Expand inline comments at flagged code locations explaining g'=∞ telescoping and period_1 degenerate-term exclusion. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9dcd753 commit 471b1ea

4 files changed

Lines changed: 13 additions & 24 deletions

File tree

diff_diff/efficient_did.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ def fit(
333333
# Enumerate valid comparison pairs
334334
pairs = enumerate_valid_triples(
335335
target_g=g,
336-
target_t=t,
337336
treatment_groups=treatment_groups,
338337
time_periods=time_periods,
339338
period_1=period_1,
@@ -398,7 +397,6 @@ def fit(
398397
eif_vals = compute_eif_nocov(
399398
target_g=g,
400399
target_t=t,
401-
att_gt=att_gt,
402400
weights=weights,
403401
valid_pairs=pairs,
404402
outcome_wide=outcome_wide,

diff_diff/efficient_did_weights.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
def enumerate_valid_triples(
1919
target_g: float,
20-
target_t: float,
2120
treatment_groups: List[float],
2221
time_periods: List[float],
2322
period_1: float,
@@ -39,8 +38,6 @@ def enumerate_valid_triples(
3938
----------
4039
target_g : float
4140
Treatment cohort of the target group.
42-
target_t : float
43-
Time period of the target parameter.
4441
treatment_groups : list of float
4542
All treatment cohort identifiers (finite values only).
4643
time_periods : list of float
@@ -69,9 +66,13 @@ def enumerate_valid_triples(
6966
# PT-All: overidentified
7067
pairs: List[Tuple[float, float]] = []
7168

72-
# Candidate comparison groups: never-treated + all treatment cohorts
73-
# (including g'=g — same-cohort pairs are valid under PT-All and
74-
# contribute overidentifying moments; see Eq 3.9).
69+
# Candidate comparison groups: never-treated + all treatment cohorts.
70+
# Including g'=g (same-cohort) is valid under PT-All (Eq 3.9).
71+
# Including g'=∞ (never-treated) produces moments where the second
72+
# and third terms telescope: y_hat = E[Y_t-Y_1|G=g] - E[Y_t-Y_1|G=∞]
73+
# regardless of t_pre. These redundant moments add no information
74+
# beyond the basic 2x2 DiD; Omega*'s pseudoinverse assigns them
75+
# zero effective weight. Retained for implementation simplicity.
7576
candidate_groups: List[float] = [never_treated_val]
7677
for gp in treatment_groups:
7778
candidate_groups.append(gp)
@@ -85,8 +86,10 @@ def enumerate_valid_triples(
8586

8687
for t_pre in time_periods:
8788
if t_pre == period_1:
88-
# period_1 is the universal reference — used as Y_1 in
89-
# differencing, not as a selectable baseline t_pre
89+
# period_1 is the universal reference — used as Y_1 in the
90+
# differencing (Eq 3.9 first term). Including t_pre = period_1
91+
# would make the third term Y_1 - Y_1 = 0 (degenerate), so it
92+
# adds no information to Omega* regardless of which g' is used.
9093
continue
9194
# Only require t_pre < g' (pre-treatment for comparison group).
9295
# No constraint on t_pre vs g: the target group appears only in
@@ -419,7 +422,6 @@ def compute_generated_outcomes_nocov(
419422
def compute_eif_nocov(
420423
target_g: float,
421424
target_t: float,
422-
att_gt: float,
423425
weights: np.ndarray,
424426
valid_pairs: List[Tuple[float, float]],
425427
outcome_wide: np.ndarray,
@@ -454,8 +456,6 @@ def compute_eif_nocov(
454456
----------
455457
target_g, target_t : float
456458
Target group-time.
457-
att_gt : float
458-
Estimated ATT(g, t).
459459
weights : ndarray, shape (H,)
460460
Efficient weights.
461461
valid_pairs : list of (g', t_pre)

docs/methodology/REGISTRY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,8 @@ where `q_{g,e} = pi_g / sum_{g' in G_{trt,e}} pi_{g'}`.
587587
- **Negative weights**: Explicitly stated as harmless for bias and beneficial for precision; arise from efficiency optimization under overidentification (Section 5.2)
588588
- **PT-Post regime (just-identified)**: Under PT-Post, EDiD automatically reduces to standard single-baseline estimator (Corollary 3.2). No downside to using EDiD -- it subsumes standard estimators
589589
- **Duplicate rows**: Duplicate `(unit, time)` entries are rejected with `ValueError`. The estimator requires exactly one observation per unit-period
590-
- **PT-All index set**: Under PT-All, valid (g', t_pre) pairs require only t_pre < g' (pre-treatment for the comparison group), not t_pre < g. Same-group pairs (g'=g) are valid and contribute overidentifying moments. This follows from Equation 3.9: the target group g appears only in the first term (Y_t - Y_1), which is independent of t_pre
591-
- **Bootstrap aggregation**: Multiplier bootstrap uses fixed cohort-size weights for overall/event-study aggregation, matching the CallawaySantAnna bootstrap pattern (CallawaySantAnnaBootstrapMixin._run_multiplier_bootstrap). The analytical path includes a WIF correction; the bootstrap captures sampling variability through per-cell EIF perturbation without re-estimating aggregation weights, consistent with both the library's CS implementation and the R `did` package approach
590+
- **Note:** PT-All index set includes g'=∞ (never-treated) as a candidate comparison group and excludes period_1 for all g'. When g'=∞, the second and third Eq 3.9 terms telescope so all (∞, t_pre) moments produce the same 2x2 DiD value; these redundant moments are handled by Omega*'s pseudoinverse. When t_pre = period_1, the third term degenerates to E[Y_1 - Y_1 | G=g'] = 0 for any g', adding no information. Valid pairs require only t_pre < g' (pre-treatment for comparison group), not t_pre < g. Same-group pairs (g'=g) are valid and contribute overidentifying moments (Equation 3.9).
591+
- **Note:** Bootstrap aggregation uses fixed cohort-size weights for overall/event-study reaggregation, matching the CallawaySantAnna bootstrap pattern (staggered_bootstrap.py:281 computes `bootstrap_overall = bootstrap_atts_gt[:, post_indices] @ weights`; L297 uses the same fixed-weight pattern for event study). The analytical path includes a WIF correction; fixed-weight bootstrap captures the same sampling variability through per-cell EIF perturbation without re-estimating aggregation weights, consistent with both the library's CS implementation and the R `did` package.
592592
- **Overall ATT convention**: The library's `overall_att` uses cohort-size-weighted averaging of post-treatment (g,t) cells, matching the CallawaySantAnna simple aggregation. This differs from the paper's ES_avg (Eq 2.3), which uniformly averages over event-time horizons. ES_avg can be computed from event study output as `mean(event_study_effects[e]["effect"] for e >= 0)`
593593

594594
*Algorithm (two-step semiparametric estimation, Section 4):*

tests/test_efficient_did.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,6 @@ def test_pt_all_simple(self):
540540
Total: 5 pairs."""
541541
pairs = enumerate_valid_triples(
542542
target_g=3,
543-
target_t=4,
544543
treatment_groups=[3],
545544
time_periods=[1, 2, 3, 4, 5],
546545
period_1=1,
@@ -558,7 +557,6 @@ def test_pt_all_staggered(self):
558557
Total: 8 pairs."""
559558
pairs = enumerate_valid_triples(
560559
target_g=3,
561-
target_t=4,
562560
treatment_groups=[3, 5],
563561
time_periods=[1, 2, 3, 4, 5],
564562
period_1=1,
@@ -581,7 +579,6 @@ def test_pt_post_single_pair(self):
581579
"""PT-Post: only (inf, g-1)."""
582580
pairs = enumerate_valid_triples(
583581
target_g=3,
584-
target_t=4,
585582
treatment_groups=[3, 5],
586583
time_periods=[1, 2, 3, 4, 5],
587584
period_1=1,
@@ -595,7 +592,6 @@ def test_g2_has_valid_pairs_pt_all(self):
595592
So pairs should be non-empty."""
596593
pairs = enumerate_valid_triples(
597594
target_g=2,
598-
target_t=3,
599595
treatment_groups=[2],
600596
time_periods=[1, 2, 3],
601597
period_1=1,
@@ -611,7 +607,6 @@ def test_anticipation(self):
611607
"""Anticipation shifts effective treatment boundary."""
612608
pairs_no_ant = enumerate_valid_triples(
613609
target_g=4,
614-
target_t=5,
615610
treatment_groups=[4],
616611
time_periods=[1, 2, 3, 4, 5],
617612
period_1=1,
@@ -620,7 +615,6 @@ def test_anticipation(self):
620615
)
621616
pairs_ant1 = enumerate_valid_triples(
622617
target_g=4,
623-
target_t=5,
624618
treatment_groups=[4],
625619
time_periods=[1, 2, 3, 4, 5],
626620
period_1=1,
@@ -1051,15 +1045,13 @@ def test_pt_all_more_moments_than_pt_post(self):
10511045
"""PT-All should produce strictly more moments than PT-Post."""
10521046
pairs_all = enumerate_valid_triples(
10531047
target_g=3,
1054-
target_t=4,
10551048
treatment_groups=[3, 5],
10561049
time_periods=[1, 2, 3, 4, 5, 6],
10571050
period_1=1,
10581051
pt_assumption="all",
10591052
)
10601053
pairs_post = enumerate_valid_triples(
10611054
target_g=3,
1062-
target_t=4,
10631055
treatment_groups=[3, 5],
10641056
time_periods=[1, 2, 3, 4, 5, 6],
10651057
period_1=1,
@@ -1074,7 +1066,6 @@ def test_same_group_pairs_valid(self):
10741066
"""g'=g pairs should be present in PT-All enumeration."""
10751067
pairs = enumerate_valid_triples(
10761068
target_g=3,
1077-
target_t=4,
10781069
treatment_groups=[3, 5],
10791070
time_periods=[1, 2, 3, 4, 5],
10801071
period_1=1,

0 commit comments

Comments
 (0)