fix(pfs): PSPS counts survive suppression — services_reported/n_suppressed beside the strict sum, money flagged as a lower bound

This commit is contained in:
kert
2026-11-12 19:12:28 -05:00
parent 5ab1aad74b
commit 79743fea4d
7 changed files with 123 additions and 13 deletions

View File

@@ -1150,7 +1150,12 @@ def psps(
con.close()
if not write:
for r in rows:
svc = f"{r.services:,.0f}" if r.services is not None else "-"
if r.services is not None:
svc = f"{r.services:,.0f}"
elif r.services_reported is not None:
svc = f">={r.services_reported:,.0f}" # suppressed lines omitted
else:
svc = "-"
typer.echo(
f"{r.year} {r.hcpcs} {r.specialty_cd} {r.specialty:<32} {svc:>12}"
)

View File

@@ -515,6 +515,14 @@ class PspsRow:
paid: float | None
dataset_id: str
item_key: str
#: Sum of the lines whose service count was reported (a lower bound on
#: ``services`` when ``n_suppressed`` > 0). In the real file two thirds
#: of the lines of a code are suppressed (< 11 services), so the strict
#: ``services`` is ``None`` for nearly every specialty; this is what the
#: file does state. ``None`` when no line reported a count.
services_reported: float | None = None
#: How many lines had a suppressed service count (``"*"``).
n_suppressed: int = 0
@dataclass(frozen=True)
@@ -548,7 +556,7 @@ CREATE SCHEMA IF NOT EXISTS pfs;
CREATE TABLE IF NOT EXISTS pfs.utilization_specialty (
year INTEGER, hcpcs VARCHAR, specialty_cd VARCHAR, specialty VARCHAR,
services DOUBLE, allowed DOUBLE, paid DOUBLE, dataset_id VARCHAR,
item_key VARCHAR);
item_key VARCHAR, services_reported DOUBLE, n_suppressed INTEGER);
CREATE TABLE IF NOT EXISTS pfs.specialty_impact (
rule_year INTEGER, vintage VARCHAR, specialty VARCHAR, setting VARCHAR,
allowed_mil DOUBLE, impact_work DOUBLE, impact_pe DOUBLE, impact_mp DOUBLE,
@@ -709,6 +717,15 @@ def ensure_tables(con: Any) -> None:
"ALTER TABLE pfs.comment_stance ADD COLUMN IF NOT EXISTS reviewed BOOLEAN "
"DEFAULT false"
)
# T10: PSPS suppression-aware service counts
con.execute(
"ALTER TABLE pfs.utilization_specialty "
"ADD COLUMN IF NOT EXISTS services_reported DOUBLE"
)
con.execute(
"ALTER TABLE pfs.utilization_specialty "
"ADD COLUMN IF NOT EXISTS n_suppressed INTEGER"
)
con.execute("UPDATE pfs.commenter SET family = 'HOME-VISITS' WHERE family IS NULL")
con.execute(
"UPDATE pfs.commenter SET submitter_name = '' WHERE submitter_name IS NULL"

View File

@@ -175,7 +175,10 @@ def to_rows(
"""National totals by ``PROVIDER_SPEC_CD`` for *code*: submitted services,
allowed charges and NCH payment summed over every line (carrier,
locality, place of service, modifier). Error-flagged lines are skipped;
a suppressed (``"*"``) or blank cell makes that sum ``None``."""
a suppressed (``"*"``) or blank cell makes that sum ``None``. Because
CMS suppresses every line under 11 services (about two thirds of a
code's lines), ``services_reported`` and ``n_suppressed`` also carry the
sum of the lines that did report and how many did not."""
code = code.upper()
buckets: dict[str, list[dict]] = {}
for rec in records:
@@ -187,6 +190,11 @@ def to_rows(
out: list[PspsRow] = []
for cd in sorted(buckets):
recs = buckets[cd]
reported = [
n
for n in (_num(r.get("PSPS_SUBMITTED_SERVICE_CNT")) for r in recs)
if n is not None
]
out.append(
PspsRow(
year=year,
@@ -200,6 +208,8 @@ def to_rows(
paid=_sum([_num(r.get("PSPS_NCH_PAYMENT_AMT")) for r in recs]),
dataset_id=dataset_id,
item_key=item_key,
services_reported=round(sum(reported), 2) if reported else None,
n_suppressed=len(recs) - len(reported),
)
)
return out

View File

@@ -259,7 +259,8 @@ class SpecialtyMoney:
``delta_pct`` is ``delta_total`` over the same codes' year-1 payments,
as a fraction. ``proposed_*`` are the same figures from the NPRM's
proposed payment (``rvu_proposed``, CY2026-27) against the year-1 final,
kept apart from the final figures. ``new_payment`` is True when a code
kept apart from the final figures. ``lower_bound`` marks a specialty whose
PSPS counts include suppressed lines. ``new_payment`` is True when a code
the specialty bills first became payable in *year*."""
specialty: str
@@ -272,6 +273,9 @@ class SpecialtyMoney:
proposed_delta_per_service: float | None = None
proposed_delta_total: float | None = None
proposed_delta_pct: float | None = None
#: True when some contributing PSPS row had suppressed lines, so
#: ``services`` (and everything scaled by it) is a lower bound.
lower_bound: bool = False
def _latest_services(psps: Sequence[PspsRow], year: int) -> dict[str, dict[str, float]]:
@@ -281,13 +285,17 @@ def _latest_services(psps: Sequence[PspsRow], year: int) -> dict[str, dict[str,
for r in psps:
if r.year <= year - 1:
latest[r.hcpcs] = max(latest.get(r.hcpcs, 0), r.year)
out: dict[str, dict[str, float]] = {}
out: dict[str, dict[str, tuple[float, bool]]] = {}
for r in psps:
if latest.get(r.hcpcs) != r.year or r.services is None:
if latest.get(r.hcpcs) != r.year:
continue
n = r.services if r.services is not None else r.services_reported
if n is None:
continue
key = specialty_key(r.specialty) or f"CODE {r.specialty_cd}"
d = out.setdefault(r.hcpcs, {})
d[key] = d.get(key, 0.0) + r.services
prior, lb = d.get(key, (0.0, False))
d[key] = (prior + n, lb or r.services is None)
return out
@@ -296,7 +304,7 @@ def billing_share(psps: Sequence[PspsRow], *, year: int) -> dict[str, float]:
services, latest PSPS year <= year-1 per code."""
totals: dict[str, float] = {}
for per in _latest_services(psps, year).values():
for k, v in per.items():
for k, (v, _lb) in per.items():
totals[k] = totals.get(k, 0.0) + v
whole = sum(totals.values())
if whole <= 0:
@@ -328,12 +336,15 @@ def money_by_specialty(
is_new = now is not None and not any(
pay is not None for (c, y), pay in final.items() if c == code and y < year
)
for spec, n in per.items():
for spec, (n, lb) in per.items():
a = acc.setdefault(
spec,
dict(svc=0.0, d=None, base=None, pd=None, pbase=None, new=False),
dict(
svc=0.0, d=None, base=None, pd=None, pbase=None, new=False, lb=False
),
)
a["svc"] += n
a["lb"] = a["lb"] or lb
a["new"] = a["new"] or is_new
if now is not None and before is not None:
a["d"] = (a["d"] or 0.0) + (now - before) * n
@@ -352,7 +363,7 @@ def money_by_specialty(
d_svc = sum(
n
for code, per in services.items()
for s, n in per.items()
for s, (n, _lb) in per.items()
if s == spec
and final.get((code, year)) is not None
and final.get((code, year - 1)) is not None
@@ -360,7 +371,7 @@ def money_by_specialty(
p_svc = sum(
n
for code, per in services.items()
for s, n in per.items()
for s, (n, _lb) in per.items()
if s == spec
and proposed.get(code) is not None
and final.get((code, year - 1)) is not None
@@ -374,6 +385,7 @@ def money_by_specialty(
share=a["svc"] / whole if whole else 0.0,
delta_pct=ratio(a["d"], a["base"]),
new_payment=a["new"],
lower_bound=a["lb"],
proposed_delta_per_service=ratio(a["pd"], p_svc),
proposed_delta_total=a["pd"],
proposed_delta_pct=ratio(a["pd"], a["pbase"]),

View File

@@ -2943,10 +2943,12 @@ class TestPsps:
def test_read_prints_dash_for_suppressed_and_reports_missing_table(self, con, env):
con.execute(
"INSERT INTO pfs.utilization_specialty VALUES "
"(2023, '99350', '50', 'NP', NULL, NULL, NULL, 'd', 'K')"
"(2023, '99350', '50', 'NP', NULL, NULL, NULL, 'd', 'K', NULL, 2), "
"(2023, '99350', '08', 'FP', NULL, NULL, NULL, 'd', 'K', 1234, 2)"
)
res = runner.invoke(app, ["pfs", "psps", "--family", "HOME-VISITS"])
assert res.exit_code == 0 and " -" in res.output
assert ">=1,234" in res.output
con.execute("DROP TABLE pfs.utilization_specialty")
res = runner.invoke(app, ["pfs", "psps", "--family", "HOME-VISITS"])
assert "no PSPS table yet" in res.output

View File

@@ -75,6 +75,34 @@ class TestToRows:
assert fp.services == 20.0
assert fp.allowed is None # a suppressed part makes the sum None
def test_reported_lines_and_suppression_count_kept_beside_the_strict_sum(self):
records = [
rec(),
rec(PSPS_SUBMITTED_SERVICE_CNT="*", PSPS_ALLOWED_CHARGE_AMT="*"),
rec(PROVIDER_SPEC_CD="08", PSPS_SUBMITTED_SERVICE_CNT="*"),
]
by = {
r.specialty_cd: r
for r in to_rows(
2023, "99350", records, dataset_id="d", item_key="K", names=NAMES
)
}
np_, fp = by["50"], by["08"]
assert np_.services is None and np_.services_reported == 10.0
assert np_.n_suppressed == 1
assert fp.services is None and fp.services_reported is None
assert fp.n_suppressed == 1
clean = to_rows(2023, "99350", [rec()], dataset_id="d", item_key="K", names={})
assert (
clean[0].services,
clean[0].services_reported,
clean[0].n_suppressed,
) == (
10.0,
10.0,
0,
)
def test_unknown_specialty_name_blank_and_bad_numbers_none(self):
rows = to_rows(
2023,
@@ -235,6 +263,23 @@ class TestWriteRead:
(2023, "99350", 9.0),
]
def test_old_shape_table_gains_the_suppression_columns(self):
c = duckdb.connect(":memory:")
c.execute("CREATE SCHEMA pfs")
c.execute(
"CREATE TABLE pfs.utilization_specialty (year INTEGER, hcpcs VARCHAR, "
"specialty_cd VARCHAR, specialty VARCHAR, services DOUBLE, allowed DOUBLE, "
"paid DOUBLE, dataset_id VARCHAR, item_key VARCHAR)"
)
c.execute(
"INSERT INTO pfs.utilization_specialty VALUES (2020,'A','1','X',1,2,3,'d','K')"
)
row = PspsRow(2021, "A", "1", "X", None, None, 3.0, "d", "K", 4.0, 2)
assert write(c, 2021, "A", [row]) == 1
got = read_by_specialty(c, ["A"])
assert got[0].services_reported is None and got[1] == row
c.close()
def test_read_filters_years_and_handles_empty(self, con):
write(con, 2023, "99350", [self._row(2023, "99350")])
write(con, 2022, "99350", [self._row(2022, "99350")])

View File

@@ -354,6 +354,25 @@ def test_proposed_vs_final_delta_kept_apart():
assert m.proposed_delta_pct == pytest.approx(-0.2)
def test_suppressed_lines_make_a_lower_bound_not_a_none():
payment = [val("A", 2022, 100.0), val("A", 2023, 90.0)]
psps = [
PspsRow(2022, "A", "08", "Family Practice", None, None, 5.0, "d", "K", 12.0, 3),
PspsRow(2022, "A", "50", "Nurse Practitioner", 8.0, 1.0, 5.0, "d", "K", 8.0, 0),
PspsRow(2022, "A", "06", "Cardiology", None, None, 5.0, "d", "K", None, 2),
]
by = {m.specialty: m for m in money_by_specialty(payment, psps, year=2023)}
assert set(by) == {"FAMILY PRACTICE", "NURSE PRACTITIONER"}
assert by["FAMILY PRACTICE"].services == 12.0
assert by["FAMILY PRACTICE"].lower_bound is True
assert by["FAMILY PRACTICE"].delta_total == -120.0
assert by["NURSE PRACTITIONER"].lower_bound is False
assert billing_share(psps, year=2023) == {
"FAMILY PRACTICE": 0.6,
"NURSE PRACTITIONER": 0.4,
}
def test_unnamed_specialty_falls_back_to_code_and_empty_inputs():
(m,) = money_by_specialty(
[val("A", 2022, 1.0), val("A", 2023, 2.0)],