notebooks: event-study — pre/post windows around code-family exposures with control sets, plus market/industry series #696

Closed
opened 2026-09-09 14:30:05 +00:00 by kert · 1 comment
Owner

Spec docs/superpowers/specs/2026-09-09-code-family-longitudinal-design.md (§P50).

Notebook that, for each exposure in the calendar, builds pre/post windows on utilization, wages/employment, and industry series (sector ETFs; telehealth/RPM/care-management tickers ingested into market.series with provenance), with never-treated control sets; reports effect sizes with confidence bands and always links back to the FR anchors that define the exposure. The chat never states causal conclusions from this; it links to the notebook.

Done when the notebook runs in the nightly notebooks-integration job on the fixture exposures and the CCM 2015 / APCM 2025 panels render with anchors.

Spec docs/superpowers/specs/2026-09-09-code-family-longitudinal-design.md (§P50). Notebook that, for each exposure in the calendar, builds pre/post windows on utilization, wages/employment, and industry series (sector ETFs; telehealth/RPM/care-management tickers ingested into `market.series` with provenance), with never-treated control sets; reports effect sizes with confidence bands and always links back to the FR anchors that define the exposure. The chat never states causal conclusions from this; it links to the notebook. Done when the notebook runs in the nightly notebooks-integration job on the fixture exposures and the CCM 2015 / APCM 2025 panels render with anchors.
kert added this to the P50: Legal epidemiology — outcome series around code-family events (utilization, BLS wages, industry markets) milestone 2026-09-09 14:30:05 +00:00
kert added the notebooksdata labels 2026-09-09 14:30:05 +00:00
Author
Owner

Merged to main as 8a0bedc (feature 92ae58a). The notebook lives at notebooks/event_study.py, so the nightly notebooks-integration run picks it up with everything else in notebooks/.

Method (src/pfs/event_study.py): for an exposure in year y (effective 1 January, so y is the first treated year) the window is the two file years before and the two from y on; the estimate is the treated code's log ratio of post-window to pre-window mean services minus the mean of the same quantity over its never-treated controls (pfs.exposure.control_codes: same RVU status and decile band, no exposure within ±2 years); the 95% interval is a percentile bootstrap over the control set with a fixed seed. A code with no pre-period of its own (a new code, or a successor) is measured through its family's summed series (scope="family"), so G2058 → 99439 is one continuous line rather than a birth. Wages and employment have no code-level control set, so they get pre_post window means only. Descriptive contrasts, stated as such; the chat never states causal conclusions from this and only links here.

Live contrasts on the fixture exposures (services billed, 2-year windows):

  • 99439 becomes-payable 2021 (CCM family series): +19.9% treated vs −13.3% controls → +38.3% [+31.6%, +46.6%], 56 controls, anchor YBM4IZUS ¶686.
  • 99490 revalued 2022 (+56.8% RVU): +14.7% vs −0.9% → +15.7% [+8.7%, +22.2%], 59 controls.
  • 99487 becomes-payable 2017 (CCM family): +149.9% [+131.9%, +174.1%], 58 controls, anchor P8SNUR46 ¶1145.
  • G2211 becomes-payable 2024 (family series, one post year): +3,312% — the family total is dominated by G2211's own first-year volume (79M services); read as uptake, not a contrast.
  • 99490 becomes-payable 2015: no pre-period on file (the CCM family had no billed predecessor in 2013–2014) — stated, not filled; anchor DE2VH9PD ¶1244.
  • G0556 becomes-payable 2025 and 99441 ends 2025: no post-period until CMS publishes the 2025 Geography-and-Service file — stated; anchors JJ6AM5HJ ¶1526 / VXSUDWCR ¶489.

Notebook sections: 1 exposure picker (nine fixtures), 2 utilization — treated series with the exposure rule, indexed control band (10th–90th pct) and the contrast table with anchor, 3 clinical-staff wages (31-9092, 29-2061, 29-1141, 29-1171) and CES employment window means, 4 every fixture contrast with anchors, 5 provenance (row counts + cms.ingest_log).

Not done here: the market/industry panel (sector ETFs, telehealth/RPM/care-management tickers) — no keyless quote source is reachable from the rack (Yahoo chart endpoint empty, stooq times out). Split to #797 with the market.series design.

Tests: 14 unit (tests/pfs/test_event_study.py), notebook structure + headless + live CCM 2015 / APCM 2025 panels (tests/notebooks/test_event_study_nb.py); pre-commit suites (pfs, notebooks) green.

**Merged to main as 8a0bedc** (feature 92ae58a). The notebook lives at `notebooks/event_study.py`, so the nightly notebooks-integration run picks it up with everything else in `notebooks/`. **Method** (`src/pfs/event_study.py`): for an exposure in year *y* (effective 1 January, so *y* is the first treated year) the window is the two file years before and the two from *y* on; the estimate is the treated code's log ratio of post-window to pre-window mean services minus the mean of the same quantity over its never-treated controls (`pfs.exposure.control_codes`: same RVU status and decile band, no exposure within ±2 years); the 95% interval is a percentile bootstrap over the control set with a fixed seed. A code with no pre-period of its own (a new code, or a successor) is measured through its family's summed series (`scope="family"`), so G2058 → 99439 is one continuous line rather than a birth. Wages and employment have no code-level control set, so they get `pre_post` window means only. Descriptive contrasts, stated as such; the chat never states causal conclusions from this and only links here. **Live contrasts on the fixture exposures** (services billed, 2-year windows): - 99439 becomes-payable 2021 (CCM family series): +19.9% treated vs −13.3% controls → **+38.3% [+31.6%, +46.6%]**, 56 controls, anchor YBM4IZUS ¶686. - 99490 revalued 2022 (+56.8% RVU): +14.7% vs −0.9% → **+15.7% [+8.7%, +22.2%]**, 59 controls. - 99487 becomes-payable 2017 (CCM family): **+149.9% [+131.9%, +174.1%]**, 58 controls, anchor P8SNUR46 ¶1145. - G2211 becomes-payable 2024 (family series, one post year): +3,312% — the family total is dominated by G2211's own first-year volume (79M services); read as uptake, not a contrast. - 99490 becomes-payable 2015: no pre-period on file (the CCM family had no billed predecessor in 2013–2014) — stated, not filled; anchor DE2VH9PD ¶1244. - G0556 becomes-payable 2025 and 99441 ends 2025: no post-period until CMS publishes the 2025 Geography-and-Service file — stated; anchors JJ6AM5HJ ¶1526 / VXSUDWCR ¶489. **Notebook sections**: 1 exposure picker (nine fixtures), 2 utilization — treated series with the exposure rule, indexed control band (10th–90th pct) and the contrast table with anchor, 3 clinical-staff wages (31-9092, 29-2061, 29-1141, 29-1171) and CES employment window means, 4 every fixture contrast with anchors, 5 provenance (row counts + `cms.ingest_log`). **Not done here**: the market/industry panel (sector ETFs, telehealth/RPM/care-management tickers) — no keyless quote source is reachable from the rack (Yahoo chart endpoint empty, stooq times out). Split to #797 with the `market.series` design. Tests: 14 unit (`tests/pfs/test_event_study.py`), notebook structure + headless + live CCM 2015 / APCM 2025 panels (`tests/notebooks/test_event_study_nb.py`); pre-commit suites (pfs, notebooks) green.
kert closed this issue 2026-09-22 20:41:57 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: homelab/stack#696