Add pdc calculation to pharmacy models#770
Add pdc calculation to pharmacy models#770saywurdson wants to merge 32 commits intotuva-health:mainfrom
Conversation
|
@saywurdson do you have any published documentation on the process or logic that you followed to develop this? This would help me validate the work as well as provide instruction for future maintenance of this algorithm. |
|
Hi @davidshimamoto I also used some of the logic from the OHDSI drug era table implementation to understand how to consolidate medication records into continuous medicaiton treatment periods while factoring in some real-life medication refill scenarios like gap periods between fills and overlapping fills where a patient gets a refill but still has some medication from the previous fill left. Here is a link to their code: https://github.com/OHDSI/ETL-CMS/blob/master/SQL/create_CDMv5_drug_era_non_stockpile.sql Please let me know if you need anything else! P.S. I also have the python code I used to create the seed file for map NDCs/product-level RXCUIs to the active ingredient level RXCUIs to consolidate different medications that have the same active ingredient. I didn't think it fit into this PR but I'm happy to share it with you if it will help with ongoing maintenance |
|
I noticed that PQA is being used to define adherence. Have any considerations been made for how combination medications are handled in the algorithm? PQA requires that a subsequent claim's supply begin on the first day of no supply from any shared active ingredient. This can result in scenarios where a single-ingredient product can affect a subsequent fill date of a different single-ingredient medication due to the presence of a combination medication, such as A >> AB >> B. This is relevant for quality metric measures such as PQA:PDC-DR and PQA:PDC-ARV. |
Refactor SQL logic to adjust overlapping drug exposures and calculate sub-exposures based on adjusted start and end dates.
This comment was marked as outdated.
This comment was marked as outdated.
Updated SQL script to include therapy grouping key in claim line processing and adjustments.
Refactor claim line processing to include ingredient-based therapy keys and improve data quality checks.
Refactor SQL logic to streamline sub-exposure calculations by removing gap detection and group ID assignment steps.
Updated event_date handling and comments in pharmacy__pdc.sql.
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
models/pharmacy/intermediate/pharmacy__int_calculate_sub_exposures.sql(1 hunks)models/pharmacy/staging/pharmacy__stg_add_ingredient_concepts.sql(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- models/pharmacy/intermediate/pharmacy__int_calculate_sub_exposures.sql
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
models/pharmacy/final/pharmacy__pdc.sql (1)
12-17: 30‑day persistence window fix looks correct; consider parameterizing the window.You retained the padded end_date (no -30) and add +30 at end events. This resolves the earlier cancellation bug and yields correct era bridging at the 30‑day boundary. To improve flexibility, make the 30 a configurable var.
- -- End events padded by 30 days for the grace period - -- This +30 days creates the persistence window + -- End events padded by a configurable grace period (default 30 days) + -- This creates the persistence window select person_id, ingredient_rxcui, ingredient_name, - {{ dbt.dateadd('day', 30, 'drug_sub_exposure_end_date') }} as event_date, + {{ dbt.dateadd('day', var('pdc_persistence_window_days', 30), 'drug_sub_exposure_end_date') }} as event_date, 1 as event_type, null as start_ordinalAlso applies to: 49-56
Refactor SQL logic for processing claim lines and ingredients, including adjustments to date calculations and grouping logic.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
models/pharmacy/final/pharmacy__pdc.sql (1)
121-123: Fix macro namespace for cross-database casting (build blocker).
type_float()is not namespaced; usedbt.type_float()to avoid "macro not found" errors.Apply:
- then (cast(total_days_exposed as {{ type_float() }}) / cast(era_duration_in_days as {{ type_float() }})) * 100 + then ( + cast(total_days_exposed as {{ dbt.type_float() }}) / + cast(era_duration_in_days as {{ dbt.type_float() }}) + ) * 100
🧹 Nitpick comments (1)
models/pharmacy/staging/pharmacy__stg_add_ingredient_concepts.sql (1)
96-123: Nested dateadd expression is correct but brittle; simplify for readability.The Jinja string concatenation inside
dbt.dateaddworks but is hard to maintain. Computeadjusted_start_dateas you do, then deriveadjusted_end_datefrom that in an outer SELECT to avoid repeating the expression.Example pattern:
select *, {{ dbt.dateadd('day', 'days_supply - 1', 'adjusted_start_date') }} as adjusted_end_date from ( -- current recursive select computing adjusted_start_date ) s
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
models/pharmacy/final/pharmacy__pdc.sql(1 hunks)models/pharmacy/staging/pharmacy__stg_add_ingredient_concepts.sql(1 hunks)
🔇 Additional comments (5)
models/pharmacy/final/pharmacy__pdc.sql (2)
65-86: Era construction logic looks solid (30‑day persistence, pairing, de-dup name).Correct pairing with padded end events, proper join to earliest closing end, and aggregation avoids splitting by name.
Also applies to: 88-105
1-3: Validate enabling flag aligns with pharmacy models.The model is gated by
brand_generic_enabled(falling back to claims/tuva_marts). Confirm this is the intended flag for pharmacy models.Would you prefer a dedicated
pharmacy_enabledvar to decouple from brand/generic gating?models/pharmacy/staging/pharmacy__stg_add_ingredient_concepts.sql (3)
133-149: Good: claim-level synchronization across combo ingredients.Taking MAX adjusted_start_date per claim line and recomputing end enforces synchronized coverage for combination fills as required by PQA.
Also applies to: 151-165, 166-185
49-51: Inner join drops unmapped NDCs; confirm intent.Unmapped products are excluded entirely. If seeds are incomplete, this silently undercounts exposure. Consider LEFT JOIN + filter to logged exceptions, or assert mapping coverage upstream.
I can add an exceptions CTE that surfaces unmapped NDCs with counts by data_source/person for QA if desired.
35-71: Ingredient-level partitioning/order looks right for sequential adjustment.Partition by
person_id, ingredient_rxcuiand tie-break ordering ensures deterministic sequencing.
|
Refactored the entire pdc calculation process. There were a lot of problems with my initial approach and this should more aligned with PQA methodology |
caleb-palmer
left a comment
There was a problem hiding this comment.
I think this recursion still needs more refinement.
Since the logic is fundamentally adjusting claims using ingredient relationships, I suggest creating a node graph, where each row is a claim:ingredient, and includes a reference to a parent claim if it shares an ingredient. You could then implement recursion by joining on parent_claim = child_claim, with the recursion beginning with claims that have no "parent". I can mock up an example if that would be helpful.
Refactor SQL logic to enhance clarity and maintain granularity in claims processing. Adjustments include changing recursive CTEs to LAG for prior exposures and ensuring all ingredients from the same claim line share adjusted dates.
|
Workflow has finished with the following statuses:
|
Describe your changes
This PR adds new capabilities to the Tuva healthcare data framework for calculating medication adherence metrics, specifically the Proportion of Days Covered (PDC) following PQA (Pharmacy Quality Alliance) methodology.
###Models
This PR creates 3 pharmacy models that calculate PDC and associated metrics:
pharmacy__stg_add_ingredient_concepts- Maps pharmacy claims to active ingredients via RxCUI and applies claim-level overlap adjustmentspharmacy__int_calculate_sub_exposures- Groups continuous coverage periods into sub-exposurespharmacy__pdc- Calculates final adherence metricsingredient_rxcuionly, usingmin(ingredient_name)to handle name variationsHow has this been tested?
I created the models in my local tuva instance using duckdb as a database. Code ran with no errors
Reviewer focus
I would like to validate that the logic used to calculate the drug eras/treatment periods makes sense. I think my logic is solid but there may be others who have done this calculation that have better insight.
Checklist before requesting a review
tuva_last_runto the final output -- added to the final pdc model tablePackage release checklist
dbt_project.yml(Optional) Gif of how this PR makes you feel
Loom link
Summary by CodeRabbit