# PBP partial hospitalization (cleaned view)

`medicare_semantic.vw_pbp_partial_hospitalization` · Medicare Advantage, Part D, Medicaid · status live

Cleaned source-faithful PBP partial hospitalization rows: era-normalized, CMS codes decoded, with offered_status/numeric_amount_observability/null_amount_reason/annualization_policy/combined_limit_policy/confidence_tier. The agent reasoning

**Grain.** One row per MA plan-segment per PBP report snapshot, keyed by (contract_id, plan_id, segment_id, report_year, quarter). Verified: those 5 columns yield exactly 193,172 distinct combinations = the full row_count of 193,172, and within every (report_year, quarter) the distinct-plan count equals the row count. Exactly one in-network PHP row per plan per snapshot (no network or sub-benefit fan-out). A single plan recurs across multiple year/quarter snapshots (full historical/snapshot table, not current-only).

**How to use it.** Latest PHP cost-share for a plan: SELECT cost_share_string, copay_amount, coinsurance_pct, auth_required, referral_required FROM medicare_semantic.vw_pbp_partial_hospitalization WHERE contract_id='H0028' AND plan_id='008' AND report_year=2026. To benchmark across plans for a year, filter report_year=2026 AND quarter=1 and aggregate copay_amount (median ~$55) / coinsurance_pct (median 20%). No is_current flag — for 'newest' take MAX(report_year) and the latest quarter within it.

**Traps.**
- No is_current / snapshot-dedup column. This is a full historical table: the same (contract_id, plan_id, segment_id) recurs across many report_year/quarter snapshots. Always filter by report_year (and quarter) — querying unfiltered double-counts a plan ~20x over the years. For 'latest', take MAX(report_year) then the latest quarter within it.
- The grain needs report_year AND quarter, not just contract/plan/segment (only 23,191 distinct). quarter is the PUF release quarter, not a fiscal quarter, and is sparse: most pre-2018 years have only Q1, while some recent benefit years were re-released in 2–4 quarters. The profiler also mis-picked 'quarter' as the time column — the real time axis is report_year.
- Many columns are entirely NULL or entirely constant and carry NO information here. 100% NULL: all limit_* and all benefit_amount_* columns, plus normalization_status/basis and aggregation_policy. Constant-for-all-rows: benefit_category, network_type='in_network', partial_hospitalization_axis='php', source_pbp_section='B5', offered_status='offered', numeric_amount_observability, null_amount_reason, annualization_policy, combined_limit_policy, confidence_tier='source_faithful', consumer_document_reconciliation_status='not_checked', source_silver_table, lmi_authorities. Don't filter or pivot on these expecting variation.
- numeric_amount_observability='offered_without_numeric_amounts' and null_amount_reason='unknown_requires_crosswalk' are view-level labels about the (always-null) benefit_amount family — they do NOT mean copay/coinsurance are missing. 90,606 rows have a copay and 46,175 have coinsurance. Read cost from copay_amount / coinsurance_pct, not from these labels.
- Cost-share is sparse and nearly mutually exclusive: 56,400 rows (29%) have NO copay, NO coinsurance and NULL cost_share_string (PHP offered but cost-share not captured); only 9 rows have both a copay and coinsurance. copay_amount=$0 is a REAL value ('$0 copay'), distinct from NULL=not captured — don't coalesce them. The min/max copay & coinsurance columns are exact duplicates of the point values here (no range).
- 'version' looks like a key/snapshot version but is the PBP plan FILING version (1–27); exactly one version per plan-snapshot, so it is functionally dependent and must not be added to the grain or used as is_current. network_type and partial_hospitalization_axis are likewise constant single-values the profiler may suggest as grain columns but they add nothing.

| column | type | meaning |
|---|---|---|
| contract_id | string | CMS MA contract ID (e.g. 'H0028'); 1,838 distinct. Part of the grain key. Join key to other PBP / Part C plan tables. |
| plan_id | string | Plan ID (PBP number) within the contract, zero-padded string (e.g. '008'); 656 distinct values but unique only WITHIN a contract. Part of the grain key. |
| segment_id | string | Plan segment (service-area split), string (e.g. '0','1','2'); 26 distinct. '0' = no segmentation. Part of the grain key — a segment can carry its own cost-share. |
| version | double | PBP plan filing version number (double, 1–27, median ~4). An attribute of the plan filing, NOT a snapshot/dedup version and NOT part of the grain — verified exactly one version per (contract,plan,segment,year,quarter). Do not treat as is_current. |
| report_year | double | PBP contract/benefit year (double, 2007–2026). Part of the grain key; the real time dimension. Note: the profiler mislabeled 'quarter' as the time column. |
| quarter | double | PBP PUF release quarter within report_year (double, 1–4). Part of the grain key. Sparse/irregular: most older years have only Q1; some recent years have 2–4 quarterly re-releases of the same benefit year. |
| benefit_category | string | Constant = 'partial_hospitalization' for every row (view is pre-filtered to that category). No discriminating info. |
| cost_share_string | string | Human-readable cost-share rendering, e.g. '$55.0 copay', '20.0%', '$0 copay'. NULL on 56,400 rows (29%) where no numeric cost-share was captured. Top values: '$55.0 copay' (45.4k), '20.0%' (45.4k). |
| copay_amount | double | Member copay in dollars (double). Populated on 90,606 rows; range $0–$750, median $55. $0 is a real value (distinct from NULL = not captured). |
| coinsurance_pct | double | Member coinsurance percentage (double). Populated on 46,175 rows; range 0–45%, median 20%. Only 9 rows carry BOTH a copay and coinsurance. |
| limit_text | string | 100% NULL in this view — PHP has no captured quantity-limit text. Dead column here. |
| auth_required | boolean | Boolean: prior authorization required for PHP. TRUE ~122k rows, FALSE ~17k, NULL ~53k (auth status not stated). NULL is meaningful = 'unknown', not False. |
| referral_required | boolean | Boolean: referral required. Mostly FALSE; TRUE on ~41k rows; some NULL. NULL = not stated. |
| network_type | varchar(10) | Constant = 'in_network' for every row. This view contains ONLY in-network PHP cost-share — no out-of-network rows. |
| max_oop_applies | boolean | Boolean: does this benefit accrue to the plan's MOOP. TRUE on essentially all rows. Near-constant. |
| limit_quantity | double | 100% NULL in this view (no PHP day/visit limits captured). Dead column. |
| limit_unit | string | 100% NULL. Dead column for PHP. |
| limit_period_code | string | 100% NULL. (Offered as a candidate grain column by the profiler but entirely null and adds nothing.) |
| limit_period_label | string | 100% NULL. Dead column. |
| limit_quantity_per_year | double | 100% NULL. Dead column. |
| benefit_amount | double | 100% NULL — PHP is a cost-share benefit, not a benefit-dollar/allowance benefit, so no amount is captured. Dead column. |
| benefit_amount_kind | string | 100% NULL. Dead column. |
| benefit_amount_period_code | string | 100% NULL. (Profiler listed it as a candidate key but it is always null.) |
| benefit_amount_period_label | string | 100% NULL. Dead column. |
| benefit_amount_annualized | double | 100% NULL. Dead column. |
| benefit_amount_pmpm | double | 100% NULL. Dead column. |
| normalization_status | string | 100% NULL in this view. Placeholder from the shared PBP semantic schema; not populated for PHP. |
| normalization_basis | string | 100% NULL. Placeholder. |
| aggregation_policy | string | 100% NULL. Placeholder. |
| offered_status | varchar(15) | Constant = 'offered' for every row — every row represents a plan that offers PHP. No 'not_offered' rows. No discriminating info. |
| numeric_amount_observability | varchar(31) | Constant = 'offered_without_numeric_amounts' for every row. A view-level label (do NOT read row-by-row): it does NOT mean copay/coinsurance are absent — 90,606 rows still carry a copay and 46,175 a coinsurance. |
| null_amount_reason | varchar(30) | Constant = 'unknown_requires_crosswalk' for every row. Refers to the (always-null) benefit_amount family, not to copay/coinsurance. Non-discriminating. |
| annualization_policy | varchar(17) | Constant = 'not_numeric'. Non-discriminating. |
| combined_limit_policy | varchar(29) | Constant = 'none_detected'. Non-discriminating. |
| consumer_document_reconciliation_status | varchar(11) | Constant = 'not_checked' — these rows have not been reconciled against consumer-facing documents (SB/EOC). Non-discriminating. |
| confidence_tier | varchar(23) | Constant = 'source_faithful' — values taken faithfully from the PBP source with no inference. Non-discriminating. |
| coinsurance_pct_max | double | Upper bound of coinsurance. For PHP equals coinsurance_pct exactly (same 46,175 rows); no range info beyond the point value. |
| coinsurance_pct_min | double | Lower bound; equals coinsurance_pct exactly for PHP. Redundant with coinsurance_pct here. |
| copay_amount_max | double | Upper bound of copay; equals copay_amount exactly for PHP (same 90,606 rows). No extra range info. |
| copay_amount_min | double | Lower bound of copay; equals copay_amount exactly for PHP. Redundant with copay_amount. |
| has_coinsurance | boolean | Boolean convenience flag = coinsurance present. TRUE on ~57k rows. NULL (~9.3k rows) where there is no cost-share signal at all (overlaps the all-null cost-share rows). NULL means 'no signal', not False. |
| has_copay | boolean | Boolean convenience flag = copay present. TRUE on ~108k rows. NULL on ~9.3k all-blank rows. Use copay_amount IS NOT NULL for a hard test rather than relying solely on this flag. |
| has_deductible | boolean | Boolean: a benefit-specific deductible applies. Almost always FALSE/NULL — only ~13 rows TRUE. PHP rarely carries its own deductible. |
| iop_auth_required | boolean |  |
| iop_coinsurance_pct_max | double |  |
| iop_coinsurance_pct_min | double |  |
| iop_copay_amount_max | double |  |
| iop_copay_amount_min | double |  |
| iop_has_coinsurance | boolean |  |
| iop_has_copay | boolean |  |
| iop_referral_required | boolean |  |
| partial_hospitalization_axis | varchar(3) | Constant = 'php' for every row. A discriminator inherited from the shared view family; here it never varies. |
| source_pbp_section | varchar(2) | Constant = 'B5' — PBP section B5 (Partial Hospitalization). Identifies the source PBP form section. |
| source_silver_table | varchar(43) | Constant = 'medicare_silver.pbp_section_b5_partial_hosp' — the upstream silver table this view is built from. Lineage pointer. |
| lmi_authorities | string | Constant = a single LMI MA Benefits Mailbox URL (CY2026 PBP Service Category Descriptions PDF) — the authoritative benefit-category definition reference. Same for all rows. |
| source_url | string | Provenance: the CMS PBP PUF zip the snapshot came from — exactly one URL per (report_year, quarter). Recent: cms.gov/files/zip/pbp-benefits-YYYY[-quarter-Q].zip; older years use the legacy mcradvpartdenroldata path. |
| sha256_content_hash | string | SHA-256 content hash of the source file for that snapshot. One distinct hash per (report_year, quarter); changes every release (the SAME plan has a different hash each snapshot). Provenance/integrity, not a row identifier. |

Tools that read it: get_pbp_benefit

Query it: `query_substrate` with `SELECT ... FROM medicare_semantic.vw_pbp_partial_hospitalization WHERE ... LIMIT 100`; read `describe_data_source("medicare_semantic.vw_pbp_partial_hospitalization")` first.
