Files
stack/dev/scripts/build_skin_subs_geo_setting.py
kert 16f3b43974
Some checks failed
CI / skinny-install (aco) (push) Successful in 1m12s
CI / skinny-install (api) (push) Successful in 30s
CI / skinny-install (bcda) (push) Successful in 36s
CI / skinny-install (bib) (push) Successful in 35s
CI / skinny-install (bls) (push) Successful in 27s
CI / skinny-install (ccw) (push) Successful in 32s
CI / skinny-install (cli) (push) Successful in 41s
CI / skinny-install (cms) (push) Successful in 37s
CI / skinny-install (conf) (push) Successful in 38s
CI / skinny-install (opps) (push) Successful in 33s
CI / skinny-install (perf) (push) Successful in 38s
CI / skinny-install (pfs) (push) Successful in 38s
CI / skinny-install (rex) (push) Successful in 34s
Deploy / build-scan-report (push) Failing after 46s
Infra CI / notebooks (push) Failing after 25s
Infra CI / zotero (push) Successful in 12s
Infra CI / docs (push) Failing after 16s
CI / lint-test (push) Failing after 11m2s
Infra CI / mc (push) Successful in 21s
Infra CI / api (push) Successful in 29s
Package Supply Chain / pkg-supply-chain (push) Failing after 41s
feat: full session — mail servers, comment pipeline, PRISMA fetch, email ingest
Mail: Maddy on DO (corwins.media+Resend, fhirworx.io+Postmark),
touchless/stateless/idempotent. Gitea SMTP via env_file. CMS inbox
at cmsupdates@mail.fhirworx.io with IMAP→bib poller.

Bib: regulations.gov v4 client, Federal Register discovery, 164K
comment backfill (running), IMAP email ingest, Zotero sync routing.

PRISMA: altcha PoW solver, CrossRef DOI resolution, 83/129 PDFs.
Zotero: schema parity, ops module, CLI, fail-fast guard.
CI: docs.Dockerfile COPY glob fix (tracks #341).
Infra: Gitea+marimo fhirworx themes, IOM/OIG modules.
2026-04-16 09:04:38 -04:00

256 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Build geographic and setting analysis tables for skin substitutes.
Addresses #240 (geographic analysis) and #241 (setting analysis).
Usage:
uv run python dev/scripts/build_skin_subs_geo_setting.py
"""
from __future__ import annotations
from pathlib import Path
import duckdb
ROOT = Path(__file__).resolve().parents[2]
DUCKDB_PATH = ROOT / "data" / "aco.duckdb"
# MAC jurisdiction → states mapping (approximate, some states split)
MAC_JURISDICTIONS = {
"Novitas (JH/JL)": ["AR", "CO", "LA", "MS", "NM", "OK", "TX"],
"First Coast (JN)": ["FL"],
"Palmetto (JJ/JM)": ["AL", "GA", "NC", "SC", "TN", "VA", "WV"],
"CGS (J15)": ["KY", "OH"],
"WPS (J5/J8)": ["IA", "IN", "KS", "MI", "MO", "NE"],
"NGS (J6/JK)": ["CT", "IL", "MA", "ME", "MN", "NH", "NY", "RI", "VT", "WI"],
"Noridian (JE/JF)": [
"AK",
"AZ",
"CA",
"HI",
"ID",
"MT",
"ND",
"NV",
"OR",
"SD",
"UT",
"WA",
"WY",
],
}
# Invert: state → MAC
STATE_TO_MAC = {}
for mac, states in MAC_JURISDICTIONS.items():
for st in states:
STATE_TO_MAC[st] = mac
def build_geographic_summary(con: duckdb.DuckDBPyConnection) -> None:
"""#240 — State and MAC jurisdiction analysis."""
print("\n=== Geographic Analysis (#240) ===")
con.execute("DROP TABLE IF EXISTS skin_subs.geographic_summary")
con.execute("""
CREATE TABLE skin_subs.geographic_summary AS
SELECT
state,
count(*) AS claim_lines,
count(*) FILTER (WHERE claim_type = 'product') AS product_lines,
count(DISTINCT person_id) AS unique_benes,
count(DISTINCT rendering_npi) AS unique_providers,
round(sum(paid_amount), 2) AS total_paid,
round(avg(paid_amount), 2) AS avg_paid_per_line,
round(sum(paid_amount) / nullif(count(DISTINCT person_id), 0), 2)
AS paid_per_bene,
round(sum(paid_amount) / nullif(count(DISTINCT rendering_npi), 0), 2)
AS paid_per_provider,
count(DISTINCT hcpcs_code) AS product_variety,
-- Setting mix
round(count(*) FILTER (WHERE place_of_service = '11') * 100.0
/ count(*), 1) AS pct_office,
round(count(*) FILTER (WHERE place_of_service = '22') * 100.0
/ count(*), 1) AS pct_hopd,
round(count(*) FILTER (WHERE place_of_service = '24') * 100.0
/ count(*), 1) AS pct_asc,
round(count(*) FILTER (WHERE place_of_service IN ('31','32')) * 100.0
/ count(*), 1) AS pct_snf,
-- Specialty mix
round(count(*) FILTER (WHERE provider_specialty = 'podiatry') * 100.0
/ count(*), 1) AS pct_podiatry,
round(count(*) FILTER (WHERE provider_specialty = 'dermatology') * 100.0
/ count(*), 1) AS pct_dermatology
FROM skin_subs.claims_synthetic
GROUP BY state
ORDER BY total_paid DESC
""")
count = con.execute("SELECT count(*) FROM skin_subs.geographic_summary").fetchone()[
0
]
print(f" States: {count}")
# Add MAC jurisdiction column
mac_cases = " ".join(
f"WHEN state = '{st}' THEN '{mac}'" for st, mac in STATE_TO_MAC.items()
)
con.execute(f"""
ALTER TABLE skin_subs.geographic_summary
ADD COLUMN mac_jurisdiction VARCHAR;
UPDATE skin_subs.geographic_summary
SET mac_jurisdiction = CASE {mac_cases} ELSE 'Other' END;
""")
# MAC-level rollup
con.execute("DROP TABLE IF EXISTS skin_subs.mac_summary")
con.execute("""
CREATE TABLE skin_subs.mac_summary AS
SELECT
mac_jurisdiction,
count(DISTINCT state) AS states,
sum(claim_lines) AS claim_lines,
sum(unique_benes) AS unique_benes,
sum(unique_providers) AS unique_providers,
round(sum(total_paid), 2) AS total_paid,
round(avg(paid_per_bene), 2) AS avg_paid_per_bene,
round(avg(pct_office), 1) AS avg_pct_office,
round(avg(pct_podiatry), 1) AS avg_pct_podiatry
FROM skin_subs.geographic_summary
GROUP BY mac_jurisdiction
ORDER BY total_paid DESC
""")
print("\n By MAC jurisdiction:")
for r in con.execute("SELECT * FROM skin_subs.mac_summary").fetchall():
print(
f" {r[0]:25s} states={r[1]:2d} lines={r[2]:5d} "
f"paid=${r[5]:>10,.2f} per_bene=${r[6]:>8,.2f} "
f"office={r[7]}% podiatry={r[8]}%"
)
print("\n Top 5 states by spend per beneficiary:")
for r in con.execute("""
SELECT state, mac_jurisdiction, unique_benes, total_paid,
paid_per_bene, pct_office, pct_podiatry
FROM skin_subs.geographic_summary
ORDER BY paid_per_bene DESC LIMIT 5
""").fetchall():
print(
f" {r[0]} {r[1]:25s} benes={r[2]:4d} "
f"per_bene=${r[4]:>8,.2f} office={r[5]}% podiatry={r[6]}%"
)
def build_setting_analysis(con: duckdb.DuckDBPyConnection) -> None:
"""#241 — Care setting analysis."""
print("\n=== Setting Analysis (#241) ===")
con.execute("DROP TABLE IF EXISTS skin_subs.setting_analysis")
con.execute("""
CREATE TABLE skin_subs.setting_analysis AS
WITH setting_product AS (
SELECT
place_of_service_description AS setting,
provider_specialty,
hcpcs_code,
count(*) AS claim_lines,
count(DISTINCT person_id) AS unique_benes,
count(DISTINCT rendering_npi) AS unique_providers,
round(sum(paid_amount), 2) AS total_paid,
round(avg(paid_amount), 2) AS avg_paid,
round(avg(units), 1) AS avg_units,
round(sum(paid_amount) / nullif(count(DISTINCT person_id), 0), 2)
AS paid_per_bene,
-- Provider concentration (HHI proxy)
round(sum(paid_amount) / nullif(
count(DISTINCT rendering_npi), 0), 2) AS paid_per_provider
FROM skin_subs.claims_synthetic
WHERE claim_type = 'product'
GROUP BY setting, provider_specialty, hcpcs_code
)
SELECT
setting,
provider_specialty,
sp.hcpcs_code,
h.product_name,
h.manufacturer,
h.category,
claim_lines,
unique_benes,
unique_providers,
total_paid,
avg_paid,
avg_units,
paid_per_bene,
paid_per_provider,
-- Flag: high per-provider concentration
CASE WHEN paid_per_provider > 10000 THEN true
ELSE false END AS high_concentration
FROM setting_product sp
LEFT JOIN skin_subs.hcpcs_universe h ON sp.hcpcs_code = h.hcpcs_code
ORDER BY total_paid DESC
""")
count = con.execute("SELECT count(*) FROM skin_subs.setting_analysis").fetchone()[0]
print(f" Setting×specialty×product combinations: {count}")
# Setting summary
print("\n By setting:")
for r in con.execute("""
SELECT setting, sum(claim_lines) as lines,
sum(unique_providers) as provs,
round(sum(total_paid), 2) as paid,
round(avg(avg_units), 1) as avg_units,
round(avg(paid_per_bene), 2) as per_bene
FROM skin_subs.setting_analysis
GROUP BY setting ORDER BY paid DESC
""").fetchall():
print(
f" {r[0]:15s} lines={r[1]:5d} provs={r[2]:4d} "
f"paid=${r[3]:>12,.2f} units={r[4]:4.1f} per_bene=${r[5]:>8,.2f}"
)
# Specialty×setting cross-tab
print("\n Specialty × setting (total paid):")
for r in con.execute("""
SELECT provider_specialty, setting,
round(sum(total_paid), 2) as paid,
sum(claim_lines) as lines
FROM skin_subs.setting_analysis
GROUP BY provider_specialty, setting
ORDER BY paid DESC LIMIT 10
""").fetchall():
print(f" {r[0]:20s} {r[1]:10s} lines={r[3]:5d} paid=${r[2]:>10,.2f}")
# High-concentration flag
high_conc = con.execute("""
SELECT count(DISTINCT provider_specialty || setting || hcpcs_code)
FROM skin_subs.setting_analysis WHERE high_concentration
""").fetchone()[0]
print(f"\n High-concentration combos (>$10K/provider): {high_conc}")
def main() -> None:
print("Building geographic and setting analysis ...")
con = duckdb.connect(str(DUCKDB_PATH))
build_geographic_summary(con)
build_setting_analysis(con)
# Updated table inventory
print("\n=== skin_subs tables ===")
for r in con.execute("""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'skin_subs' ORDER BY table_name
""").fetchall():
cnt = con.execute(f"SELECT count(*) FROM skin_subs.{r[0]}").fetchone()[0]
print(f" skin_subs.{r[0]:30s}: {cnt:>6} rows")
con.close()
print("\nDone.")
if __name__ == "__main__":
main()