csdid 0.2.8__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. csdid-0.4.2/CHANGELOG_v0.3.0.md +329 -0
  2. {csdid-0.2.8 → csdid-0.4.2}/LICENSE +21 -21
  3. csdid-0.4.2/MANIFEST.in +10 -0
  4. csdid-0.4.2/PKG-INFO +346 -0
  5. csdid-0.4.2/PYTHON_BUGFIXES.md +174 -0
  6. csdid-0.4.2/csdid/_version.py +1 -0
  7. {csdid-0.2.8 → csdid-0.4.2}/csdid/aggte_fnc/aggte.py +113 -113
  8. {csdid-0.2.8 → csdid-0.4.2}/csdid/aggte_fnc/compute_aggte.py +661 -495
  9. {csdid-0.2.8 → csdid-0.4.2}/csdid/aggte_fnc/utils.py +245 -196
  10. csdid-0.4.2/csdid/att_gt.py +668 -0
  11. csdid-0.4.2/csdid/attgt_fnc/compute_att_gt.py +627 -0
  12. csdid-0.4.2/csdid/attgt_fnc/compute_att_gt2.py +322 -0
  13. csdid-0.4.2/csdid/attgt_fnc/compute_att_gt_shared.py +247 -0
  14. csdid-0.4.2/csdid/attgt_fnc/drdid_trim.py +360 -0
  15. csdid-0.4.2/csdid/attgt_fnc/preprocess_did.py +911 -0
  16. {csdid-0.2.8 → csdid-0.4.2}/csdid/plots/gplot.py +128 -128
  17. csdid-0.4.2/csdid/utils/bmisc.py +67 -0
  18. csdid-0.4.2/csdid/utils/mboot.py +187 -0
  19. csdid-0.4.2/csdid.egg-info/PKG-INFO +346 -0
  20. {csdid-0.2.8 → csdid-0.4.2}/csdid.egg-info/SOURCES.txt +9 -2
  21. {csdid-0.2.8 → csdid-0.4.2}/csdid.egg-info/requires.txt +3 -1
  22. csdid-0.4.2/readme.md +311 -0
  23. csdid-0.4.2/requirements.txt +10 -0
  24. {csdid-0.2.8 → csdid-0.4.2}/setup.cfg +4 -4
  25. {csdid-0.2.8 → csdid-0.4.2}/setup.py +51 -38
  26. csdid-0.2.8/PKG-INFO +0 -29
  27. csdid-0.2.8/csdid/_version.py +0 -1
  28. csdid-0.2.8/csdid/att_gt.py +0 -244
  29. csdid-0.2.8/csdid/attgt_fnc/compute_att_gt.py +0 -507
  30. csdid-0.2.8/csdid/attgt_fnc/preprocess_did.py +0 -428
  31. csdid-0.2.8/csdid/utils/bmisc.py +0 -55
  32. csdid-0.2.8/csdid/utils/mboot.py +0 -115
  33. csdid-0.2.8/csdid.egg-info/PKG-INFO +0 -29
  34. csdid-0.2.8/test/test_vs_r.py +0 -61
  35. {csdid-0.2.8 → csdid-0.4.2}/csdid/__init__.py +0 -0
  36. {csdid-0.2.8 → csdid-0.4.2}/csdid/aggte_fnc/__init__.py +0 -0
  37. {csdid-0.2.8 → csdid-0.4.2}/csdid/attgt_fnc/__init__.py +0 -0
  38. {csdid-0.2.8 → csdid-0.4.2}/csdid/plots/__init__.py +0 -0
  39. {csdid-0.2.8 → csdid-0.4.2}/csdid/utils/__init__.py +0 -0
  40. {csdid-0.2.8 → csdid-0.4.2}/csdid.egg-info/dependency_links.txt +0 -0
  41. {csdid-0.2.8 → csdid-0.4.2}/csdid.egg-info/top_level.txt +0 -0
@@ -0,0 +1,329 @@
1
+ # csdid v0.3.0 — Sync with R `did` v2.5.1
2
+
3
+ > **v0.3.1 (patch):** two edge-case fixes from a final GPT-5.5 review of v0.3.0
4
+ > (universal base cell identified by position not by an all-zero influence
5
+ > function; numeric dtype validation robust to pandas nullable integers). See
6
+ > §16. Validated by an independent Opus 4.8 review. 564 tests passing.
7
+
8
+ ## Summary
9
+
10
+ This release brings the Python `csdid` package into full alignment with the R `did` package v2.5.x. Key additions: comprehensive input validation, critical bug fixes, proper clustered inference (bootstrap **and** analytical), a point-estimates-only mode, a ~10× faster bootstrap, the **`fix_weights`** parameter, and a vectorized **`faster_mode`** (~3× faster, bit-identical). Pre-existing Python defects fixed during this work (unrelated to the R sync) are tracked separately in **`PYTHON_BUGFIXES.md`**.
11
+
12
+ **564 tests** (`csdid/test_csdid`, 0 skipped) cover all changes; **65 compare directly against live R `did` 2.5.0** (ATT(g,t) to ~1e-16, analytical SEs within ~0.1–1%, across all aggregations, `fix_weights` modes, and factor covariates) or published JEL values. See §10–§16 and `csdid/test_csdid/test_suite_desc.md`.
13
+
14
+ ---
15
+
16
+ ## Changes Made
17
+
18
+ ### 1. Input Validation (commit `0efb95f`)
19
+
20
+ **Files:** `csdid/attgt_fnc/preprocess_did.py`, `csdid/att_gt.py`
21
+ **Tests:** `test/test_validation.py` (22 tests)
22
+
23
+ Added validation guards matching R `did` v2.5.1:
24
+ - Missing columns → clear error listing available columns
25
+ - Reserved internal column names (`w`, `rowid`, `G_m`, `C`, etc.) → rejected
26
+ - Non-numeric outcome/time/group variables → rejected
27
+ - Negative `gname` values → rejected with explanation
28
+ - Duplicate `(idname, tname)` rows → rejected
29
+ - `alp` must be in (0, 1); `biters` must be positive integer
30
+ - `anticipation` must be non-negative
31
+ - Strict `control_group` matching (only 'nevertreated' or 'notyettreated')
32
+ - Negative/zero-mean weights → rejected
33
+
34
+ ### 2. Bug Fix: `notyettreated` Last-Cohort Retention (commit `5cf446a`)
35
+
36
+ **Files:** `csdid/attgt_fnc/preprocess_did.py`, `csdid/attgt_fnc/compute_att_gt.py`
37
+ **Tests:** `test/test_notyettreated.py` (6 tests)
38
+
39
+ Fixed: when `control_group='notyettreated'` and no never-treated group exists, the last-treated cohort (which serves as the not-yet-treated comparison) was accidentally deleted from the data along with always-treated units. Now retained in data while excluded from estimation groups.
40
+
41
+ Also fixed: panel data path was not passing `post_treat` to the results, causing the post-treatment indicator to always be 0.
42
+
43
+ ### 3. Bug Fix: Per-Cell Estimation Failure → NA (commit `9c5d4a5`)
44
+
45
+ **Files:** `csdid/attgt_fnc/compute_att_gt.py`
46
+ **Tests:** `test/test_percell_failure.py` (4 tests)
47
+
48
+ When a 2×2 DiD estimation fails for a specific (g,t) cell (singular design matrix, too few observations), the package now warns and sets that cell's ATT to `NaN` instead of crashing the entire computation. Both panel and repeated cross-section paths are protected.
49
+
50
+ ### 4. Fix: Cluster Bootstrap Aggregation (commit `8f0ea6f`)
51
+
52
+ **Files:** `csdid/utils/mboot.py`
53
+ **Tests:** `test/test_clustered.py` (5 tests)
54
+
55
+ Rewrote the clustered multiplier bootstrap to match R `did` v2.5.1:
56
+ - Uses cluster **sums** of influence functions (not means) per Callaway & Sant'Anna (2021) Remark 10
57
+ - Fixed `clustervars` normalization (string/list/None handling)
58
+ - Fixed time-varying cluster detection (checks full data, not just first period)
59
+ - Fixed parallel bootstrap chunk calculation (prevents negative chunk sizes when `biters < cores`)
60
+ - Handles 1-d influence function arrays
61
+
62
+ ### 5. Feature: Analytical Cluster-Robust SEs (commit `200054f`)
63
+
64
+ **Files:** `csdid/aggte_fnc/utils.py`
65
+ **Tests:** `test/test_analytical_cluster_se.py` (3 tests)
66
+
67
+ When `clustervars` is set and `bstrap=FALSE`, cluster-robust standard errors are now computed analytically from cluster sums of influence functions. Previously only i.i.d. analytical SEs were available.
68
+
69
+ ### 6. Feature: `compute_inffunc` Parameter (commit `4c3291d`)
70
+
71
+ **Files:** `csdid/att_gt.py`, `csdid/attgt_fnc/compute_att_gt.py`
72
+ **Tests:** `test/test_compute_inffunc.py` (5 tests)
73
+
74
+ New `compute_inffunc=False` parameter for point-estimates-only runs:
75
+ - Returns identical ATT point estimates without influence functions or SEs
76
+ - Uses less memory (no n×k IF matrix)
77
+ - Runs faster (skips bootstrap entirely)
78
+ - `aggte()` is blocked with a clear error message
79
+ - Default remains `True` (backward compatible)
80
+
81
+ ### 7. Performance: Vectorized Multiplier Bootstrap (commit `7bc09e0`)
82
+
83
+ **Files:** `csdid/utils/bmisc.py`
84
+
85
+ Replaced Python for-loop over `biters` iterations with a single matrix multiplication:
86
+ ```python
87
+ # Before: O(biters) Python loop
88
+ for b in range(biters):
89
+ Ub = np.random.choice([1, -1], size=(n, 1))
90
+ outMat[b] = np.mean(inf_func * Ub, axis=0)
91
+
92
+ # After: single vectorized operation
93
+ Ub = np.random.choice([1, -1], size=(biters, n))
94
+ outMat = Ub @ inf_func / n
95
+ ```
96
+ ~10x faster for typical problem sizes.
97
+
98
+ ---
99
+
100
+ ### 8. Code Review Bug Fixes (commit `de8e0b5`)
101
+
102
+ **Files:** `csdid/utils/mboot.py`, `csdid/aggte_fnc/utils.py`, `csdid/attgt_fnc/preprocess_did.py`, `csdid/attgt_fnc/compute_att_gt.py`, `csdid/utils/bmisc.py`
103
+ **Tests:** `test/test_review_fixes.py` (13 tests)
104
+
105
+ GPT-5.5 code review found 10 bugs:
106
+
107
+ | Bug | Severity | Fix |
108
+ |-----|----------|-----|
109
+ | Clustered bootstrap SE scaled by `1/√n_clusters` instead of R's `√n_clusters/n` | **Critical** | Corrected to `bSigma * sqrt(n_clusters) / n` |
110
+ | Analytical cluster SE used `√(mean(S²)/n_c)` instead of `√(sum(S²))/n` | **Critical** | Corrected formula |
111
+ | Panel path overwrote global `n` with cell-specific `len(disdat)` | **Critical** | Renamed to `n_cell` local variable |
112
+ | `asif_nev_treated` threshold missing `+ anticipation` | High | Added `+ anticipation` |
113
+ | `glist` not recomputed after recoding groups to 0 | High | Added recompute step |
114
+ | `treated_fp` threshold missing `+ anticipation` | High | Added `+ anticipation` |
115
+ | Panel/RCS count branches swapped | High | Swapped to match R |
116
+ | Unbalanced panels not switched to RC estimators | High | Detect unbalanced → set `panel=False` |
117
+ | Bad formula silently fell back to intercept | High | Raise `ValueError` on bad user formula |
118
+ | Boolean outcomes rejected (R allows logical) | Medium | Accept `np.bool_` dtype |
119
+ | Bootstrap chunking could produce wrong iteration count | Medium | Use `np.linspace` split |
120
+ | Vectorized bootstrap could OOM on large inputs | Medium | Added chunked allocation |
121
+
122
+ ---
123
+
124
+ ### 9. Second-Round Review Fixes (commit `4ebec68`)
125
+
126
+ **Files:** `csdid\attgt_fnc\preprocess_did.py`, `csdid\utils\mboot.py`
127
+ **Tests:** `test/test_review_fixes.py` (+6), `test/test_notyettreated.py` (updated)
128
+
129
+ A second deep review against the R source (`R/pre_process_did.R`) found behavioral divergences:
130
+
131
+ | Issue | Severity | Fix |
132
+ |-------|----------|-----|
133
+ | `control_group='nevertreated'` with no never-treated group raised an error | **High** (behavior change) | R v2.5.1 warns + coerces the last cohort to never-treated and truncates late periods. Python now matches. |
134
+ | First-period-treated units dropped by cohort membership (`gname in glist_in`) | **High** | R drops by row identity (`~treated_fp`); the old approach could delete the not-yet-treated comparison cohort. Refactored to match R. |
135
+ | Unbalanced-panel detection used `nunique(counts) > 1` | Medium | R uses `nrow != n_units * n_periods`. The old heuristic missed panels where every unit has the same (but incomplete) period count. |
136
+ | `mboot` could crash when all bootstrap dimensions are degenerate | Low | Added `ncol(bres) == 0` guard returning NaN SE/crit_val (matches R). |
137
+
138
+ **Validation against canonical `mpdta` (Callaway & Sant'Anna):**
139
+ - ATT(g,t) point estimates match the R `did` vignette **exactly** (e.g., ATT(2004,2006) = -0.1373).
140
+ - Simple aggregation ATT = **-0.04** (R: -0.0400).
141
+ - Group aggregation overall ATT = **-0.031** (R: -0.0310).
142
+
143
+ **Note (behavior change):** Code relying on the old `ValueError("no available never-treated group")` should now expect a `UserWarning` and a successful run with the last cohort coerced to never-treated.
144
+
145
+ ---
146
+
147
+ ### 10. Test-Suite Consolidation & Live R Parity (commits `…`)
148
+
149
+ **Files:** `csdid/test_csdid/*`, `csdid/att_gt.py`, `csdid/attgt_fnc/preprocess_did.py`
150
+
151
+ The new tests were consolidated into the canonical R-translated suite at `csdid/test_csdid/`, and the package was validated against a **live install of R `did` v2.5.0** (R 4.4.2).
152
+
153
+ **Stale divergence markers removed** (my round-1/2 fixes made these match R — they were previously `xfail`/`xpass`):
154
+ - mboot clustervars-as-string bug → clustered bootstrap now runs
155
+ - unknown formula variables now raise (not silent intercept)
156
+ - anticipation-window coercion of groups treated after `max(t)`
157
+ - truly unbalanced panels route through RC estimators (no `vstack` failure)
158
+ - `nevertreated` with no never-treated group → warns + coerces (not error)
159
+ - first-period-treated drops emit a `UserWarning`
160
+
161
+ **New behavior/fixes:**
162
+ - `weights_name` may equal an internal name like `w` (R stores weights in `.w`); weights are captured before any internal columns are created.
163
+ - **Implemented att_gt-level analytical cluster-robust SE** (`bstrap=False` + `clustervars`), matching R; verified equal to the cluster bootstrap SE.
164
+ - Fixed `plot_attgt()` for a single group (`plt.subplots(..., squeeze=False)`).
165
+
166
+ **Live R parity (`csdid/test_csdid/test_r_parity.py`, 25 tests):**
167
+ Reference values generated by `r_ref/generate_reference.R` from R `did` 2.5.0 across 5 scenarios (mpdta nevertreated/notyettreated/covariates/ipw, simulated data):
168
+ - **ATT(g,t) point estimates match R to ~1e-16** (machine precision).
169
+ - Analytical (`bstrap=FALSE`) SEs match within ~0.1–1%.
170
+ - All four aggregation types (simple/group/dynamic/calendar) — overall and per-event — match R.
171
+ - R's `nevertreated`-coercion warning text matches csdid's wording.
172
+
173
+ **Full `csdid/test_csdid` suite: 460 passed, 31 skipped, 0 failures, 0 xfails.**
174
+
175
+ ---
176
+
177
+ ### 11. est_method / base_period validation, treatment-reversal, custom callable
178
+
179
+ **Files:** `csdid/att_gt.py`, `csdid/attgt_fnc/preprocess_did.py`
180
+
181
+ - `est_method` must be `dr`/`reg`/`ipw` or a callable; `base_period` must be `varying`/`universal` — both now validated with clear errors (matching R).
182
+ - Treatment must be irreversible: a time-varying `gname` within a unit now errors (matching R's message).
183
+ - Confirmed a **custom Python callable** `est_method` reproduces the built-in estimator (already supported; recovered the skipped test).
184
+
185
+ ### 12. `fix_weights` parameter (`NULL`/`varying`/`base_period`/`first_period`)
186
+
187
+ **Files:** `csdid/att_gt.py`, `csdid/attgt_fnc/preprocess_did.py`, `csdid/attgt_fnc/compute_att_gt.py`
188
+ **Tests:** `test_att_gt.py::TestFixWeights`, `test_r_parity.py::test_fix_weights_matches_r`
189
+
190
+ Controls which period's weights each (g,t) cell uses when weights are
191
+ time-varying. `base_period` uses the group's last pre-treatment period (R's
192
+ `pret_g`); `first_period` uses `tlist[0]`; `varying` routes panel data to the RC
193
+ estimators with per-period weights; the RC path drops units not observed in the
194
+ target period (with a warning), like R. A "time-varying weights detected"
195
+ warning is emitted for the `NULL` default. **All four modes match R `did` 2.5.0
196
+ to ~1e-16** on a shared time-varying-weight dataset.
197
+
198
+ ### 13. `faster_mode` — vectorized compute (~3× faster, bit-identical)
199
+
200
+ **Files:** `csdid/attgt_fnc/compute_att_gt2.py` (new), `csdid/att_gt.py`
201
+ **Tests:** `TestFasterMode`, `TestIFConsistency`, `test_faster_mode_consistency.py`
202
+ **Doc:** `csdid/test_csdid/FASTER_MODE.md`
203
+
204
+ `ATTgt(..., faster_mode=True)` builds the covariate design **once per period**
205
+ (panel) / **once globally** (RC) and **pre-pivots** outcomes/weights, removing
206
+ the per-(g,t) `patsy` + `panel2cs2` cost. Results are **identical** to the
207
+ standard path (max |std−fast| = 1.4e-17 for ATT & SE across 72 configs).
208
+ Python(Fast) is ~3.3× faster than Python(Standard), roughly matching
209
+ R(Standard) in absolute time.
210
+
211
+ ### 14. Factor / transformed covariates (R v2.5.1 "factor variables")
212
+
213
+ **Files:** `csdid/attgt_fnc/preprocess_did.py`, `compute_att_gt.py`, `compute_att_gt2.py`
214
+ **Tests:** `test_review_fixes.py::TestFactorCovariates`, `test_r_parity.py::test_factor_covariate_matches_r`
215
+
216
+ Categorical/transformed covariates (`Y~C(cat)`, `Y~C(cat)+x`, `poly(...)`, etc.)
217
+ were **silently dropped**: `preprocess` expands the formula into design columns
218
+ (`C(cat)[T.b]`, …) and drops the raw `cat` column, then the (g,t) loop **re-ran
219
+ patsy with the original formula** on data that no longer had `cat` → it failed
220
+ and fell back to an intercept-only design (numeric `Y~x` only worked because the
221
+ design column is also named `x`). Now the design is **built once** in
222
+ `preprocess` (factor-consistent, global levels) and the loop **selects those
223
+ columns** (`dp['xcov_cols']`) — matching R `did` (which builds `model.matrix()`
224
+ once) and removing `patsy` from the inner loop entirely (a further speed-up for
225
+ the standard path too).
226
+
227
+ **Validated vs R `did` 2.5.0** on a shared factor dataset: ATT(g,t) match to
228
+ **4.4e-15** in both standard and `faster_mode`.
229
+
230
+ ---
231
+
232
+ ### 15. Polish: idname validation, universal-base SE, faster_mode dedup
233
+
234
+ **Files:** `csdid/attgt_fnc/preprocess_did.py`, `csdid/att_gt.py`,
235
+ `csdid/attgt_fnc/compute_att_gt.py`, `compute_att_gt2.py`,
236
+ `compute_att_gt_shared.py` (new)
237
+ **Tests:** `test_review_fixes.py` (`TestIdnameNumericValidation`,
238
+ `TestUniversalBaseNaN`, `TestPostKeyAlias`), `test_r_parity.py` (strengthened)
239
+
240
+ - **`idname` numeric validation (R parity).** R `did` v2.5.1 requires the id
241
+ variable to be numeric (`stop("The id variable '…' must be numeric…")`). csdid
242
+ now validates this in `_validate_inputs` with the same message intent instead
243
+ of failing obscurely later. Removed the stale `#todo: idname must be numeric`.
244
+ - **Universal base-period SE → `NaN` (R parity).** For `base_period="universal"`
245
+ the base cell has `att = 0` by construction and an all-zero influence function,
246
+ so its SE is undefined. R reports `NA`; csdid previously reported a misleading
247
+ `0`. It now reports `NaN`, matching R's `ref_gaps` reference exactly (e.g.
248
+ `universal, g=2004, t=2003 → att=0, se=NA`). `test_r_parity` was tightened to
249
+ assert csdid is non-finite wherever R is `NA`.
250
+ - **`faster_mode` de-duplication (maintainability).** The estimator dispatch and
251
+ the per-(g,t) pretreatment-period / control-flow logic were copy-pasted across
252
+ the standard and `faster_mode` paths. They now live once in
253
+ `compute_att_gt_shared.py` (`select_estimators`, `last_pretreatment_index`,
254
+ `plan_cell`) and are consumed by both, so the cell-selection logic cannot
255
+ drift. Behavior is unchanged — the bit-identical consistency suite (72 configs)
256
+ and all R-parity tests still pass.
257
+
258
+ ---
259
+
260
+ ### 16. Final review (GPT-5.5) follow-ups
261
+
262
+ **Files:** `csdid/att_gt.py`, `csdid/attgt_fnc/preprocess_did.py`
263
+ **Tests:** `test_review_fixes.py` (`TestUniversalBaseNaN`, `TestIdnameNumericValidation`, +3)
264
+
265
+ A final GPT-5.5 code review of §15 surfaced two real edge-case defects, both fixed
266
+ and verified:
267
+
268
+ - **Universal base cell mis-identified by all-zero IF (Medium).** §15 detected the
269
+ universal base cell via `np.all(inffunc == 0, axis=1)`. A *degenerate but valid*
270
+ estimated cell (e.g. a perfectly deterministic outcome with a true zero effect)
271
+ also has an all-zero influence function and was wrongly assigned `SE=NaN` instead
272
+ of `0`. The base cell is now identified by **position** — `year` equal to the
273
+ cohort's last pre-treatment period (`last_pretreatment_index`) — exactly R's
274
+ definition, independent of IF contents.
275
+ - **Numeric dtype check crashed on pandas nullable ints (Medium).** The
276
+ `np.issubdtype(data[col].dtype, np.number)` checks raised
277
+ `TypeError: Cannot interpret 'Int64Dtype()'` for pandas nullable integer columns
278
+ (`Int64`), and pandas extension dtypes generally. Replaced with
279
+ `pandas.api.types.is_numeric_dtype` (excluding `is_bool_dtype` for
280
+ `tname`/`gname`/`idname`), so nullable numeric ids/keys are accepted and boolean
281
+ ones are still rejected (matching R). String ids remain rejected for R parity.
282
+
283
+ ---
284
+
285
+ ## Pre-existing Python bug fixes
286
+
287
+ Defects in the Python port itself (crashes / wrong output, independent of the R
288
+ update) are documented separately in **`PYTHON_BUGFIXES.md`**: `plot_attgt`
289
+ single-group crash, panel post-treatment flag always 0, panel global-`n`
290
+ overwrite, the broken `allow_unbalanced_panel=False` branch, and `raise "<str>"`
291
+ masking error messages.
292
+
293
+ ---
294
+
295
+ ## Remaining Work (Future PRs)
296
+
297
+ > All headline R `did` v2.5.1 items are implemented: input validation,
298
+ > `notyettreated`, clustered inference (bootstrap + analytical), `compute_inffunc`,
299
+ > `fix_weights`, `faster_mode`, and **factor/transformed covariates**. Possible
300
+ > future work: 2-way clustering, additional `est_method` plug-ins, trimming
301
+ > options.
302
+
303
+ ---
304
+
305
+ ## Test suite
306
+
307
+ **`csdid/test_csdid`: 564 passed, 0 skipped, 0 failures.** 65 tests compare
308
+ directly to live/published R values (see `test_csdid/test_suite_desc.md`);
309
+ `faster_mode` details in `test_csdid/FASTER_MODE.md`.
310
+
311
+ ## Release
312
+
313
+ - `csdid/_version.py` bumped `0.2.9` → **`0.3.0`**.
314
+ - `setup.py` now sets `long_description` (from `readme.md`) + `long_description_content_type`
315
+ so PyPI renders the README; `MANIFEST.in` ships the changelog/license and prunes
316
+ test fixtures from the sdist.
317
+
318
+ ## Running Tests
319
+
320
+ ```bash
321
+ # Full R-translated suite (no R needed; R-parity tests skip if references absent)
322
+ pytest csdid/test_csdid
323
+
324
+ # Regenerate R reference values (requires R with did >= 2.5.0):
325
+ Rscript csdid/test_csdid/r_ref/generate_reference.R csdid/test_csdid/r_ref csdid/test_csdid
326
+ Rscript csdid/test_csdid/r_ref/generate_sim_reference.R
327
+ Rscript csdid/test_csdid/r_ref/generate_fixweights_reference.R csdid/test_csdid/r_ref
328
+ ```
329
+
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Pedro Sant’Anna, Brantly Callaway, Alexander Quispe, Carlos Guevara, Jhon Flores
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Pedro Sant’Anna, Brantly Callaway, Alexander Quispe, Carlos Guevara, Jhon Flores
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ include readme.md
2
+ include LICENSE
3
+ include CHANGELOG_v0.3.0.md
4
+ include PYTHON_BUGFIXES.md
5
+ include requirements.txt
6
+
7
+ # Exclude test fixtures / generated data from the sdist (they bloat the package).
8
+ prune csdid/test_csdid
9
+ global-exclude *.pyc
10
+ global-exclude __pycache__