Skip to content

Commit 4f04832

Browse files
igerberclaude
andcommitted
Reject EfficientDiD covariates + survey_design combination from PR #226 review (round 11)
The DR covariate path does not thread survey weights through nuisance estimation. Raise NotImplementedError instead of silently returning partial results. Add test and update deferred-work table. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f3d7427 commit 4f04832

3 files changed

Lines changed: 31 additions & 2 deletions

File tree

diff_diff/efficient_did.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,16 @@ def fit(
275275
"survey_design for design-based standard errors."
276276
)
277277

278+
# Guard covariates + survey (DR path does not yet thread survey weights)
279+
if covariates is not None and len(covariates) > 0 and resolved_survey is not None:
280+
raise NotImplementedError(
281+
"Survey weights with covariates are not yet supported for "
282+
"EfficientDiD. The doubly robust covariate path does not "
283+
"thread survey weights through nuisance estimation. "
284+
"Use covariates=None with survey_design, or drop survey_design "
285+
"when using covariates."
286+
)
287+
278288
# Normalize empty covariates list to None (use nocov path)
279289
if covariates is not None and len(covariates) == 0:
280290
covariates = None

docs/survey-roadmap.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ Phase 5 infrastructure (weighted `solve_logit()` or bootstrap+survey interaction
3636
| TripleDifference | IPW and DR methods + survey | Phase 5: weighted `solve_logit()` |
3737
| ContinuousDiD | Multiplier bootstrap + survey | Phase 5: bootstrap+survey interaction |
3838
| EfficientDiD | Multiplier bootstrap + survey | Phase 5: bootstrap+survey interaction |
39+
| EfficientDiD | Covariates (DR path) + survey | DR nuisance estimation needs survey weight threading |
3940

40-
All four estimators raise `NotImplementedError` when the blocked combination is
41-
attempted, with a message pointing to the planned phase.
41+
All blocked combinations raise `NotImplementedError` when attempted, with a
42+
message pointing to the planned phase or describing the limitation.
4243

4344
## Phase 4: Complex Standalone Estimators
4445

tests/test_survey_phase3.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,24 @@ def test_bootstrap_survey_raises(self, staggered_survey_data):
631631
survey_design=sd,
632632
)
633633

634+
def test_covariates_survey_raises(self, staggered_survey_data):
635+
"""Covariates + survey should raise NotImplementedError."""
636+
from diff_diff import EfficientDiD
637+
638+
# Add a covariate column
639+
staggered_survey_data["x1"] = np.random.randn(len(staggered_survey_data))
640+
sd = SurveyDesign(weights="weight")
641+
with pytest.raises(NotImplementedError, match="covariates"):
642+
EfficientDiD(n_bootstrap=0).fit(
643+
staggered_survey_data,
644+
"outcome",
645+
"unit",
646+
"time",
647+
"first_treat",
648+
covariates=["x1"],
649+
survey_design=sd,
650+
)
651+
634652
def test_survey_metadata_fields(self, staggered_survey_data):
635653
"""survey_metadata has correct fields."""
636654
from diff_diff import EfficientDiD

0 commit comments

Comments
 (0)