Skip to content

Commit bb8edfc

Browse files
igerberclaude
andcommitted
Clear _replicate_df on refit, add CS zero-mass guards, document IF df
- Reset _replicate_df = None at start of LinearRegression.fit() so reused model instances don't carry stale replicate df - Add zero-effective-mass guards in CS _att_gt_* for survey-weighted treated/control groups — return NaN for cells with zero weight - Document in REGISTRY that IF-based replicate contrasts cannot fail (weighted sums are always finite), so n_valid == R and df propagation is not needed for IF paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 84809cd commit bb8edfc

3 files changed

Lines changed: 14 additions & 3 deletions

File tree

diff_diff/linalg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,9 @@ def fit(
16591659
X = np.asarray(X, dtype=np.float64)
16601660
y = np.asarray(y, dtype=np.float64)
16611661

1662+
# Reset replicate df from any previous fit
1663+
self._replicate_df = None
1664+
16621665
# Add intercept if requested
16631666
if self.include_intercept:
16641667
X = np.column_stack([np.ones(X.shape[0]), X])

diff_diff/staggered.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,12 @@ def _compute_att_gt_fast(
604604
sw_treated = survey_w[treated_valid] if survey_w is not None else None
605605
sw_control = survey_w[control_valid] if survey_w is not None else None
606606

607+
# Guard against zero effective mass after subpopulation filtering
608+
if sw_treated is not None and np.sum(sw_treated) <= 0:
609+
return {"att": np.nan, "se": np.nan}
610+
if sw_control is not None and np.sum(sw_control) <= 0:
611+
return {"att": np.nan, "se": np.nan}
612+
607613
# Get covariates if specified (from the base period)
608614
X_treated = None
609615
X_control = None

docs/methodology/REGISTRY.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,9 +2029,11 @@ variance from the distribution of replicate estimates.
20292029
- **Note:** CallawaySantAnna, ContinuousDiD, and EfficientDiD reject
20302030
replicate weights with `n_bootstrap > 0`. Replicate weights provide
20312031
analytical variance; bootstrap is a separate inference mechanism.
2032-
- **Note:** When invalid replicates are dropped, `n_replicates` is updated
2033-
to the valid count so that `df_survey = n_valid - 1` and downstream
2034-
t-based inference uses the correct degrees of freedom.
2032+
- **Note:** When invalid replicates are dropped in `compute_replicate_vcov`
2033+
(OLS path), `n_valid` is returned and used for `df_survey = n_valid - 1`
2034+
in `LinearRegression.fit()`. For IF-based replicate paths, replicates
2035+
essentially never fail (weighted sums cannot be singular), so `n_valid`
2036+
equals `R` in practice and df propagation is not needed.
20352037

20362038
### DEFF Diagnostics (Phase 6)
20372039

0 commit comments

Comments
 (0)