-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update PPU tool to remove manually curated list and reflect dm+d rather than BNF codes #5096
Comments
I have now modified the formulation swap SQL to mirror the Google Sheets version (minus spaces in the column names and the "really equivalent?" column, as it's not necessary):
|
I should think the best way to test this would be to create a new spreadsheet with the same structure as the original. We can then update If there's a problem with the new data (perhaps there are too many rows and performance suffers), we can easily revert the change and redeploy. |
Thanks @inglesp that works for the substitution sets, but what about the VMP to AMP mapping? |
I'd assumed, without proper consideration, that the VMP to AMP mapping could be handled in the same way, but it doesn't have the concept of an alternative formulation (obviously). So that's not going to work. |
Issues raised by @chrisjwood16 on first test iteration:
|
latest version of code to fix some of the issues WITH drugs AS (
SELECT
vmp.id,
nm,
bnf_code,
pres_stat,
droute.route,
pres_f,
sug_f,
CASE WHEN formroute.descr LIKE 'solutioninfusion%' then 'solutioninfusion' -- simplify multiple infusion routes as 'infusion'
WHEN formroute.descr LIKE 'solutioninjection%' then 'solutioninjection' -- simplify multiple injection routes as 'injection'
ELSE formroute.descr END AS formroute,
udfs,
form.descr AS form,
STRING_AGG(
CONCAT(
COALESCE(COALESCE(CAST (bs_subid AS STRING), (CAST(ing AS STRING))), 'NULL'), '|', -- ingredient code, maps to base if it exists
COALESCE(CAST(strnt_nmrtr_val AS STRING), 'NULL'), '|', -- strength numerator value
COALESCE(CAST(strnt_nmrtr_uom AS STRING), 'NULL'), '|', -- strength numerator unit
COALESCE(CAST(strnt_dnmtr_val AS STRING), 'NULL'), '|', -- strength denominator value
COALESCE(CAST(strnt_dnmtr_uom AS STRING), 'NULL'), '|', -- strength denominator unit
COALESCE(CAST(droute.route AS STRING), 'NULL'), '|',-- route
COALESCE(CAST(pres_stat AS STRING), 'NULL'), '|',-- prescribing suitable for primary care value
COALESCE(CASE WHEN formroute.descr NOT LIKE '%.oral%' THEN CAST(udfs AS STRING) ELSE 'NULL' END, 'NULL'), '|', -- if not oral meds, then ensure unit doses are the same (e.g. injections)
CASE WHEN LOWER(formroute.descr) LIKE '%modified-release%' THEN 'MR' ELSE 'NULL' END, '|', -- add 'modified release' flag on match string, so that non modified-release preps aren't matched with standard release
CASE WHEN LOWER(form.descr) LIKE '%concentrate%' THEN 'conc' ELSE 'NULL' END, '|', -- add 'concentrate' flag on match string, so that non concentrate preps aren't matched with standard preps
CASE WHEN LOWER(route.descr) LIKE '%cutaneous%' THEN LEFT(formroute.descr, STRPOS(formroute.descr, '.') - 1) ELSE 'NULL' END -- add type of formulation to cutaneous preps
),
','
ORDER BY ing, basis_strnt
) AS vpi_string
FROM
dmd.vpi AS vpi
INNER JOIN
dmd.vmp AS vmp ON vpi.vmp = vmp.id
INNER JOIN
dmd.droute AS droute ON vmp.id = droute.vmp
INNER JOIN
dmd.route AS route ON route.cd = droute.route
INNER JOIN
dmd.ont AS ont ON vmp.id = ONT.vmp
INNER JOIN
dmd.ontformroute AS formroute ON ont.form = formroute.cd
INNER JOIN
dmd.dform AS dform ON vmp.id = dform.vmp
INNER JOIN
dmd.form AS form ON dform.form = form.cd
WHERE bnf_code IS NOT NULL --make sure all drugs have BNF code
AND SUBSTR(bnf_code,10,2) = 'AA' --make sure all generic codes
AND (non_avail != 1 OR non_avail is NULL) -- make sure all drugs are available
AND strnt_nmrtr_val IS NOT NULL -- make sure all drugs have a strength
AND pres_stat = 1 -- must be "suitable for prescribing in primary care", e.g. no cautions on switching preparations
AND nm LIKE 'Ondansetr%'
GROUP BY
vmp.id, nm, bnf_code, pres_stat, pres_f, sug_f, route, formroute, udfs, form
)
SELECT DISTINCT
drugs_1.bnf_code AS Code,
drugs_1.nm AS Name,
drugs_2.bnf_code AS Alternative_code,
CASE WHEN drugs_1.pres_f IS TRUE THEN concat(drugs_1.form, ' preservative free')
WHEN drugs_1.sug_f IS TRUE THEN concat(drugs_1.form, ' sugar free')
ELSE drugs_1.form END AS Formulation,
CASE WHEN drugs_2.pres_f IS TRUE THEN concat(drugs_2.form, ' preservative free')
WHEN drugs_2.sug_f IS TRUE THEN concat(drugs_2.form, ' sugar free') ELSE drugs_2.form END AS Alternative_formulation,
"Y" AS Really_equivalent
FROM
drugs AS drugs_1
INNER JOIN
drugs AS drugs_2
ON drugs_1.vpi_string = drugs_2.vpi_string -- only where strings are the same, i.e. same ingredients, strength, and route
WHERE
drugs_1.id != drugs_2.id -- so don't get duplication
and drugs_1.nm LIKE 'Ondansetr%'
AND
drugs_1.bnf_code != drugs_2.bnf_code -- remove duplication where two different VMP types have same BNF code (e.g. solution/suspension)
ORDER BY drugs_1.bnf_code |
Thanks for the detailed and very clear write-up, Rich! Just to note for other people's benefit the things we've already discussed elsewhere:
|
We released the "price-per-unit" tool in 2017. It works by identifying possible switches in two different ways:
Omeprazole 20mg dispersible gastro-resistant tablets [0103050P0AAANAN]
andLosec MUPS 20mg gastro-resistant tablets [0103050P0BBAEAN]
have the same "9+2" code (013050P0____AN
), and therefore we can easily find themRamipril 10mg tablets
andRamipril 10mg capsules
have the same active ingredient, and don't have any specific reason why they are not interchangeable. These swaps were created using a manually-created list of suitable formulation swaps.Although this was fine when the tool was released in 2017, it is in urgent need of an overhaul, for a number of reasons:
Hypromellose 0.3% eye drops
have the BNF code1108010F0AAAAAA
(in the Eye chapter), whereas common brands, e.g.Xailin Hydrate 0.3% eye drops
have an Appliance chapter BNF code21300000169
, which can't be mapped to the generic.sulphasalazine 500mg GR tablets
contains bothSulazine EC 500mg tablets
(which have been discontinued for some time) andSalazopyrin EN-Tabs 500mg
(which have been recently discontinued).However, these issues should be able to be (hopefully) relatively easy to resolve.
From my relatively-limited understanding, the tool uses the following to decide on what's in the "substitution set":
get_formulation_swaps
function, a csv version of the current formulation swaps lists from Google Sheets is used to map e.g. tablet to capsule formulations.generic_equivalent_for_bnf_code
function, the generic BNF code is created from the existing branded BNF code, in order to map to all brands.1. Change formulation swaps table to SQL-based view in BigQuery, using logic from dm+d to define suitable swaps
By defining logic, we can create a formulation swap in SQL from the dm+d
Virtual Medicinal Product
dataset. I have created a first attempt, using the following logic, which should mirror what the current list attempts manually.Logic
This list is created by creating a string containing ingredient codes, strength numerator and denominator value and units, route, prescribing status flag, unit dose values for non-oral preps, and a "modified-release" flag.
For example the string for
Ramipril 10mg capsules
(0205051R0AAADAD
) is386872004|1|10|258684004|NULL|NULL|26643006|1|NULL|NULL
, comprising:NOTE: this doesn't contain the drug form, i.e. capsule, so it will match to any other generic which matches this string. In the case above, we get this:
i.e. it finds that ramipril 10mg tablets are a suitable alternative formulation.
Full SQL is here:
Although it's not yet perfectly matching the fields in the old
formulation_swaps.csv
file, this could be resolved easily, when we get nearer deployment.2. Mapping generic to brands
As described above, due to differences in BNF code hierarchy, not everything is currently being mapped. However, by using
VMP
toAMP
mapping, this can be resolved.Logic
Using this logic, for
Hypromellose 0.3% eye drops
(1108010F0AAAAAA
) returns this:As you can see, none of these brands would have appeared under the old
bnf_code
"9+2" mapping.Full SQL is here:
Next steps
Creating the SQL-based formulation swaps map results in about 10x the number of lines in the old Google Sheets version. They look reasonable, but further testing is required. @inglesp @evansd it would be great to understand from your perspective what else is needed, and your views on the best way to test.
The text was updated successfully, but these errors were encountered: