Commit 9dfcb52
feat(did): etwfe final three — multi-xvar + repeated CS + cgroup
Completes R-etwfe parity by adding the last three features:
1. Multi-xvar heterogeneity: etwfe(xvar=['x1','x2',...])
- xvar now accepts a str (backward-compat) or list of strs
- For each cohort g and each x_j, fits a slope coefficient
- detail DataFrame gains per-xvar columns:
slope_<x>, slope_<x>_se, slope_<x>_pvalue
- Backward-compat: when exactly one xvar is given, adds aliases
slope_wrt_x / slope_se / slope_pvalue so existing callers keep working
- model_info['xvar'] is always a list; model_info['xvar_means']
is a dict of the centering means
2. Repeated cross-section: etwfe(panel=False)
- Replaces unit FE with explicit cohort + time dummies
- Used for repeated cross-section data where the same unit is not
observed across periods (R etwfe's ivar=NULL mode)
- Works with xvar, controls, and cluster as expected
- model_info['panel'] = False; method string flags the mode
3. cgroup='nevertreated' — restricted control group
- Fits one ETWFE regression per cohort, each using only that cohort
plus never-treated units (matches R etwfe(cgroup='never'))
- Cohort ATTs combined with cohort-size weighting
- SE from independent-regression aggregation (conservative)
- Validates cgroup ∈ {'notyet','nevertreated'} with clear error
- Errors helpfully if no never-treated units exist in the data
All three options compose: etwfe(xvar=['x1','x2'], cgroup='nevertreated')
and etwfe(xvar='x1', panel=False) both verified to work.
Tests: 4 new pytest cases (tests/test_did_summary.py):
- test_etwfe_multiple_xvars — slope_x1/slope_x2 columns present
- test_etwfe_repeated_cross_section — panel=False labelled + SE differs
- test_etwfe_cgroup_nevertreated — per-cohort fit path verified
- test_etwfe_cgroup_invalid — invalid cgroup raises
Total: 15 passed in 1.43s (was 11).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 8ff253a commit 9dfcb52
4 files changed
Lines changed: 373 additions & 47 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
177 | 177 | | |
178 | 178 | | |
179 | 179 | | |
180 | | - | |
| 180 | + | |
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | | - | |
| 97 | + | |
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| |||
0 commit comments