Skip to content

Commit fddb6c3

Browse files
igerberclaude
andcommitted
Use heterogeneous outcomes instead of noise injection in bootstrap test
Replace random noise approach with naturally heterogeneous outcomes (different y_pre per unit) while keeping identical dose. This exercises the rank-deficient bootstrap path with real sampling variance rather than papering over zero-residual data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2d663d9 commit fddb6c3

1 file changed

Lines changed: 16 additions & 16 deletions

File tree

tests/test_methodology_continuous_did.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -191,32 +191,32 @@ def test_all_same_dose(self):
191191
atol=1e-10,
192192
)
193193

194-
# Verify bootstrap path also produces finite ATT SE (not NaN) for
195-
# rank-deficient cells — regression test for P1 bootstrap fix.
196-
# Use noisy outcomes so residuals are non-zero, giving the bootstrap
197-
# actual variance to measure (identical outcomes → zero residuals →
198-
# zero-variance bootstrap → SE=0 → NaN by design on all platforms).
194+
# Verify bootstrap path produces finite ATT SE for rank-deficient
195+
# cells — regression test for P1 bootstrap fix. Use data with
196+
# heterogeneous outcomes (natural sampling variance) but the same
197+
# dose so the design matrix is still rank-deficient.
198+
# ACRT SE is correctly NaN: zero dose variation → zero-variance
199+
# bootstrap distribution → degenerate SE → NaN by design.
199200
rng = np.random.default_rng(123)
200-
rows_noisy = []
201+
rows_hetero = []
201202
for i in range(n_control):
202-
noise = rng.normal(0, 0.1)
203-
rows_noisy.append({"unit": i, "period": 1, "outcome": noise, "first_treat": 0, "dose": 0.0})
204-
rows_noisy.append({"unit": i, "period": 2, "outcome": noise, "first_treat": 0, "dose": 0.0})
203+
y_pre = rng.normal(0, 0.3)
204+
y_post = rng.normal(0, 0.3)
205+
rows_hetero.append({"unit": i, "period": 1, "outcome": y_pre, "first_treat": 0, "dose": 0.0})
206+
rows_hetero.append({"unit": i, "period": 2, "outcome": y_post, "first_treat": 0, "dose": 0.0})
205207
for j in range(n_treated):
206208
uid = n_control + j
207-
noise = rng.normal(0, 0.1)
208-
rows_noisy.append({"unit": uid, "period": 1, "outcome": noise, "first_treat": 2, "dose": dose_val})
209-
rows_noisy.append({"unit": uid, "period": 2, "outcome": 5.0 + noise, "first_treat": 2, "dose": dose_val})
210-
data_noisy = pd.DataFrame(rows_noisy)
211-
# ACRT SE is correctly NaN: zero dose variation → zero-variance
212-
# bootstrap distribution → degenerate SE → NaN by design.
209+
y_pre = rng.normal(0, 0.3)
210+
rows_hetero.append({"unit": uid, "period": 1, "outcome": y_pre, "first_treat": 2, "dose": dose_val})
211+
rows_hetero.append({"unit": uid, "period": 2, "outcome": y_pre + 5.0, "first_treat": 2, "dose": dose_val})
212+
data_hetero = pd.DataFrame(rows_hetero)
213213
est_boot = ContinuousDiD(
214214
degree=1, num_knots=0, n_bootstrap=199,
215215
rank_deficient_action="silent", seed=42,
216216
)
217217
with pytest.warns(UserWarning, match="[Ii]dentical"):
218218
results_boot = est_boot.fit(
219-
data_noisy, "outcome", "unit", "period", "first_treat", "dose"
219+
data_hetero, "outcome", "unit", "period", "first_treat", "dose"
220220
)
221221
assert np.all(np.isfinite(results_boot.dose_response_att.se))
222222

0 commit comments

Comments
 (0)