Skip to content

Commit e4e7989

Browse files
Fix 3 bugs found in code review: stacked_did, drdid, jackknife_se
- dgp_did: reserve ~20% units as never-treated for stacked_did compatibility - drdid: accept `seed` as alias for `random_state` parameter - jackknife_se: fix dependent variable lookup using `dependent_var` key Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e59badf commit e4e7989

3 files changed

Lines changed: 9 additions & 5 deletions

File tree

src/statspai/did/wooldridge_did.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ def drdid(
363363
alpha: float = 0.05,
364364
n_boot: int = 500,
365365
random_state: Optional[int] = None,
366+
seed: Optional[int] = None,
366367
) -> CausalResult:
367368
"""
368369
Doubly Robust Difference-in-Differences (Sant'Anna & Zhao 2020).
@@ -416,7 +417,7 @@ def drdid(
416417
True
417418
"""
418419
df = data.copy()
419-
rng = np.random.default_rng(random_state)
420+
rng = np.random.default_rng(random_state if random_state is not None else seed)
420421

421422
# ── Validate 2×2 design ─────────────────────────────────────────
422423
g_vals = sorted(df[group].dropna().unique())

src/statspai/inference/jackknife.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,9 @@ def _parse_formula(
492492

493493
# Fallback: use result parameter names
494494
param_names = list(result.params.index)
495-
y_var = result.model_info.get('depvar', result.data_info.get('y_var', 'y'))
495+
y_var = result.data_info.get('dependent_var',
496+
result.model_info.get('depvar',
497+
result.data_info.get('y_var', 'y')))
496498
x_vars = [p for p in param_names
497499
if p.lower() not in ('intercept', '_const', 'const', '(intercept)')]
498500
return y_var, x_vars

src/statspai/utils/dgp.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,14 @@ def dgp_did(
7171
first_treat = np.full(n_units, np.inf)
7272

7373
if staggered:
74-
group = rng.integers(0, n_groups, size=n_units)
75-
# Spread adoption across the last 60 % of periods
74+
# Group 0 = never-treated (~20% of units); groups 1..n_groups = treated
75+
group = rng.integers(0, n_groups + 1, size=n_units)
7676
treat_times = np.linspace(
7777
int(n_periods * 0.4), n_periods - 1, n_groups, dtype=int
7878
)
7979
for g in range(n_groups):
80-
first_treat[group == g] = treat_times[g]
80+
first_treat[group == g + 1] = treat_times[g]
81+
# group 0 stays never-treated (first_treat = inf)
8182
else:
8283
# Classic 2x2: half treated at mid-point
8384
treated_mask = rng.choice(n_units, size=n_units // 2, replace=False)

0 commit comments

Comments
 (0)