diff --git a/analysis/codelists.py b/analysis/codelists.py index 68e05a6..785193f 100644 --- a/analysis/codelists.py +++ b/analysis/codelists.py @@ -87,7 +87,8 @@ # Community Pharmacy Pharmacy First Service "pf_consultation_cp_service": pf_consultation_cp_service, "pf_consultation_services_combined": pf_consultation_cp_minorillness - + pf_consultation_service + pf_consultation_cp_service, + + pf_consultation_service + + pf_consultation_cp_service, } uti_code = ["1090711000000102"] @@ -96,4 +97,4 @@ otitismedia_code = ["3110003"] sorethroat_code = ["363746003"] shingles_code = ["4740000"] -impetigo_code = ["48277006"] \ No newline at end of file +impetigo_code = ["48277006"] diff --git a/analysis/dataset_definition_table1.py b/analysis/dataset_definition_table1.py index 2bb0547..2249e0d 100644 --- a/analysis/dataset_definition_table1.py +++ b/analysis/dataset_definition_table1.py @@ -18,7 +18,7 @@ get_numerator, get_latest_ethnicity, get_age_band, - get_imd + get_imd, ) from pf_variables_library import select_events import codelists @@ -27,7 +27,9 @@ dataset = create_dataset() registration = practice_registrations.for_patient_on(index_date) -selected_events = clinical_events.where(clinical_events.date.is_on_or_before(index_date)) +selected_events = clinical_events.where( + clinical_events.date.is_on_or_before(index_date) +) # Columns for demographics table dataset.sex = patients.sex @@ -35,7 +37,11 @@ dataset.region = registration.practice_nuts1_region_name dataset.imd = get_imd(addresses, index_date) dataset.ethnicity = get_latest_ethnicity( - index_date, selected_events, codelists.ethnicity_group16_codelist, ethnicity_from_sus, grouping=16 + index_date, + selected_events, + codelists.ethnicity_group16_codelist, + ethnicity_from_sus, + grouping=16, ) @@ -65,17 +71,36 @@ index_date, selected_events, codelists.pregnancy_codelist ) -dataset.uti_numerator = get_numerator(selected_events, codelists.uti_code, dataset.uti_denominator) -dataset.sinusitis_numerator = get_numerator(selected_events, codelists.sinusitis_code, dataset.sinusitis_denominator) -dataset.insectbite_numerator = get_numerator(selected_events, codelists.insectbite_code, dataset.insectbite_denominator) -dataset.otitismedia_numerator = get_numerator(selected_events, codelists.otitismedia_code, dataset.otitismedia_denominator) -dataset.sorethroat_numerator = get_numerator(selected_events, codelists.sorethroat_code, dataset.sorethroat_denominator) -dataset.shingles_numerator = get_numerator(selected_events, codelists.shingles_code, dataset.shingles_denominator) -dataset.impetigo_numerator = get_numerator(selected_events, codelists.impetigo_code, dataset.impetigo_denominator) +dataset.uti_numerator = get_numerator( + selected_events, codelists.uti_code, dataset.uti_denominator +) +dataset.sinusitis_numerator = get_numerator( + selected_events, codelists.sinusitis_code, dataset.sinusitis_denominator +) +dataset.insectbite_numerator = get_numerator( + selected_events, codelists.insectbite_code, dataset.insectbite_denominator +) +dataset.otitismedia_numerator = get_numerator( + selected_events, codelists.otitismedia_code, dataset.otitismedia_denominator +) +dataset.sorethroat_numerator = get_numerator( + selected_events, codelists.sorethroat_code, dataset.sorethroat_denominator +) +dataset.shingles_numerator = get_numerator( + selected_events, codelists.shingles_code, dataset.shingles_denominator +) +dataset.impetigo_numerator = get_numerator( + selected_events, codelists.impetigo_code, dataset.impetigo_denominator +) -pf_consultation_events = select_events(selected_events, codelist=codelists.pf_consultation_events_dict["pf_consultation_services_combined"]) +pf_consultation_events = select_events( + selected_events, + codelist=codelists.pf_consultation_events_dict["pf_consultation_services_combined"], +) has_pf_consultation = pf_consultation_events.exists_for_patient() dataset.define_population( - registration.exists_for_patient() & patients.sex.is_in(["male", "female"]) & has_pf_consultation + registration.exists_for_patient() + & patients.sex.is_in(["male", "female"]) + & has_pf_consultation ) diff --git a/analysis/measures_definition_pf_breakdown.py b/analysis/measures_definition_pf_breakdown.py index 4ccb2c3..92b24ee 100644 --- a/analysis/measures_definition_pf_breakdown.py +++ b/analysis/measures_definition_pf_breakdown.py @@ -13,7 +13,10 @@ from pf_dataset import get_latest_ethnicity from codelists import pf_consultation_events_dict -from config import start_date_measure_pf_breakdown, monthly_intervals_measure_pf_breakdown +from config import ( + start_date_measure_pf_breakdown, + monthly_intervals_measure_pf_breakdown, +) from pf_variables_library import select_events measures = create_measures() @@ -59,12 +62,15 @@ otherwise="Missing", ) -pharmacy_first_ids = select_events(clinical_events, codelist=pf_consultation_events_dict["pf_consultation_services_combined"]).consultation_id +pharmacy_first_ids = select_events( + clinical_events, + codelist=pf_consultation_events_dict["pf_consultation_services_combined"], +).consultation_id # # Select clinical events in interval date range -selected_events = select_events(clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date).where( - clinical_events.consultation_id.is_in(pharmacy_first_ids) -) +selected_events = select_events( + clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date +).where(clinical_events.consultation_id.is_in(pharmacy_first_ids)) # Breakdown metrics to be produced as graphs breakdown_metrics = { @@ -75,11 +81,18 @@ "ethnicity": ethnicity_combined, } -pf_consultation_events = select_events(selected_events, codelist=pf_consultation_events_dict["pf_consultation_services_combined"]) +pf_consultation_events = select_events( + selected_events, + codelist=pf_consultation_events_dict["pf_consultation_services_combined"], +) has_pf_consultation = pf_consultation_events.exists_for_patient() # Define the denominator as the number of patients registered -denominator = registration.exists_for_patient() & patients.sex.is_in(["male", "female"]) & has_pf_consultation +denominator = ( + registration.exists_for_patient() + & patients.sex.is_in(["male", "female"]) + & has_pf_consultation +) # Create measures for pharmacy first services for pharmacy_first_event, codelist in pf_consultation_events_dict.items(): @@ -150,4 +163,4 @@ denominator=pf_condition_denominators[condition_name], group_by={breakdown: variable}, intervals=months(monthly_intervals).starting_on(start_date), - ) \ No newline at end of file + ) diff --git a/analysis/measures_definition_pf_condition_provider.py b/analysis/measures_definition_pf_condition_provider.py index 1e4ec31..391ae59 100644 --- a/analysis/measures_definition_pf_condition_provider.py +++ b/analysis/measures_definition_pf_condition_provider.py @@ -9,7 +9,10 @@ imd_quintile, ) from codelists import pf_consultation_events_dict -from config import start_date_measure_condition_provider, monthly_intervals_measure_condition_provider +from config import ( + start_date_measure_condition_provider, + monthly_intervals_measure_condition_provider, +) from pf_variables_library import select_events measures = create_measures() @@ -20,10 +23,15 @@ registration = practice_registrations.for_patient_on(INTERVAL.end_date) -selected_events = select_events(clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date) +selected_events = select_events( + clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date +) # Create variable which contains boolean values of whether pharmacy first event exists for patient -has_pharmacy_first = select_events(selected_events, codelist=pf_consultation_events_dict["pf_consultation_services_combined"]).exists_for_patient() +has_pharmacy_first = select_events( + selected_events, + codelist=pf_consultation_events_dict["pf_consultation_services_combined"], +).exists_for_patient() for condition_name, condition_code in pharmacy_first_conditions_codes.items(): condition_events = selected_events.where( @@ -32,16 +40,15 @@ # Define the numerator as the count of events for the condition numerator = condition_events.count_for_patient() - denominator = registration.exists_for_patient() & patients.sex.is_in(["male", "female"]) + denominator = registration.exists_for_patient() & patients.sex.is_in( + ["male", "female"] + ) # Measures for overall clinical services graph measures.define_measure( name=f"count_{condition_name}_total", numerator=numerator, denominator=denominator, - group_by={ - "pf_status": has_pharmacy_first, - "imd": imd_quintile - }, + group_by={"pf_status": has_pharmacy_first, "imd": imd_quintile}, intervals=months(monthly_intervals).starting_on(start_date), - ) \ No newline at end of file + ) diff --git a/analysis/measures_definition_pf_consultation_med_counts.py b/analysis/measures_definition_pf_consultation_med_counts.py index 8074076..d3fc80d 100644 --- a/analysis/measures_definition_pf_consultation_med_counts.py +++ b/analysis/measures_definition_pf_consultation_med_counts.py @@ -25,21 +25,19 @@ # Select Pharmacy First events during interval date range pharmacy_first_events = select_events( - clinical_events, - start_date=INTERVAL.start_date, - end_date=INTERVAL.end_date).where( - clinical_events.snomedct_code.is_in( - pf_consultation_events_dict["pf_consultation_services_combined"] - ) + clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date +).where( + clinical_events.snomedct_code.is_in( + pf_consultation_events_dict["pf_consultation_services_combined"] ) +) pharmacy_first_ids = pharmacy_first_events.consultation_id has_pf_consultation = pharmacy_first_events.exists_for_patient() # Select Pharmacy First consultations during interval date range selected_medications = select_events( - medications, - start_date=INTERVAL.start_date, end_date=INTERVAL.end_date + medications, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date ).where(medications.consultation_id.is_in(pharmacy_first_ids)) # First medication for each patient @@ -62,7 +60,7 @@ measures.define_measure( name="pf_medication_count", - numerator = first_selected_medication.is_not_null(), + numerator=first_selected_medication.is_not_null(), denominator=denominator, group_by={ "dmd_code": first_selected_medication, diff --git a/analysis/measures_definition_pf_medications.py b/analysis/measures_definition_pf_medications.py index e46e0be..0fea365 100644 --- a/analysis/measures_definition_pf_medications.py +++ b/analysis/measures_definition_pf_medications.py @@ -20,27 +20,29 @@ registration = practice_registrations.for_patient_on(INTERVAL.end_date) # Select Pharmacy First consultations during interval date range -pharmacy_first_events = select_events(clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date).where( - clinical_events.snomedct_code.is_in(pf_consultation_events_dict["pf_consultation_services_combined"]) +pharmacy_first_events = select_events( + clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date +).where( + clinical_events.snomedct_code.is_in( + pf_consultation_events_dict["pf_consultation_services_combined"] + ) ) pharmacy_first_ids = pharmacy_first_events.consultation_id has_pf_consultation = pharmacy_first_events.exists_for_patient() # Select medications prescribed with PF consultation ID -selected_medications = select_events(medications, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date).where( - medications.consultation_id.is_in(pharmacy_first_ids) -) +selected_medications = select_events( + medications, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date +).where(medications.consultation_id.is_in(pharmacy_first_ids)) # Select first medication for group_by argument in measures first_selected_medication = ( selected_medications.sort_by(medications.date).first_for_patient().dmd_code ) -# Check if a medication is from our PF codelists -has_pharmacy_first_medication = first_selected_medication.is_in( - pf_med_codelist -) +# Check if a medication is from our PF codelists +has_pharmacy_first_medication = first_selected_medication.is_in(pf_med_codelist) # Numerator, patients with a PF medication # This allows me to count all (first) medications linked to a PF consultation @@ -58,8 +60,6 @@ name="pf_medication_count", numerator=has_pharmacy_first_medication, denominator=denominator, - group_by={ - "dmd_code": first_selected_medication - }, + group_by={"dmd_code": first_selected_medication}, intervals=months(monthly_intervals).starting_on(start_date), ) diff --git a/analysis/pf_dataset.py b/analysis/pf_dataset.py index f316d8c..551d76b 100644 --- a/analysis/pf_dataset.py +++ b/analysis/pf_dataset.py @@ -5,6 +5,7 @@ # This file contains functions for the denominators of the patient population for each clinical condition. # It will be used to calculate rates, and is separate from pf_variables_library + # Create denominator variables for each clinical condition # These are based on NHS England rules using sex, age, pregnancy status and repeated diagnoses # NOTE: The following exclusions have not been added: @@ -125,32 +126,37 @@ def get_acute_otitis_media_denominator( return inclusion_criteria & ~exclusion_criteria + def get_numerator(selected_events, condition_code, condition_denominator): - numerator_counts = selected_events.where( - selected_events.snomedct_code.is_in(condition_code) - ).where( - condition_denominator - ).count_for_patient() + numerator_counts = ( + selected_events.where(selected_events.snomedct_code.is_in(condition_code)) + .where(condition_denominator) + .count_for_patient() + ) return numerator_counts + def get_age_band(patients, index_date): age = patients.age_on(index_date) age_band = case( - when((age >= 0) & (age < 20)).then("0-19"), - when((age >= 20) & (age < 40)).then("20-39"), - when((age >= 40) & (age < 60)).then("40-59"), - when((age >= 60) & (age < 80)).then("60-79"), - when(age >= 80).then("80+"), - when(age.is_null()).then("Missing"), -) + when((age >= 0) & (age < 20)).then("0-19"), + when((age >= 20) & (age < 40)).then("20-39"), + when((age >= 40) & (age < 60)).then("40-59"), + when((age >= 60) & (age < 80)).then("60-79"), + when(age >= 80).then("80+"), + when(age.is_null()).then("Missing"), + ) return age_band + def get_imd(addresses, index_date): imd_rounded = addresses.for_patient_on(index_date).imd_rounded max_imd = 32844 imd_quintile = case( - when((imd_rounded >= 0) & (imd_rounded < int(max_imd * 1 / 5))).then("1 (Most Deprived)"), + when((imd_rounded >= 0) & (imd_rounded < int(max_imd * 1 / 5))).then( + "1 (Most Deprived)" + ), when(imd_rounded < int(max_imd * 2 / 5)).then("2"), when(imd_rounded < int(max_imd * 3 / 5)).then("3"), when(imd_rounded < int(max_imd * 4 / 5)).then("4"), @@ -159,6 +165,7 @@ def get_imd(addresses, index_date): ) return imd_quintile + def get_latest_ethnicity( index_date, clinical_events, ethnicity_codelist, ethnicity_from_sus, grouping=6 ): @@ -200,15 +207,9 @@ def get_latest_ethnicity( ) elif grouping == 16: latest_ethnicity_from_codes = case( - when(latest_ethnicity_from_codes_category_num == "1").then( - "White British" - ), - when(latest_ethnicity_from_codes_category_num == "2").then( - "White Irish" - ), - when(latest_ethnicity_from_codes_category_num == "3").then( - "Other White" - ), + when(latest_ethnicity_from_codes_category_num == "1").then("White British"), + when(latest_ethnicity_from_codes_category_num == "2").then("White Irish"), + when(latest_ethnicity_from_codes_category_num == "3").then("Other White"), when(latest_ethnicity_from_codes_category_num == "4").then( "White and Caribbean" ), @@ -218,94 +219,41 @@ def get_latest_ethnicity( when(latest_ethnicity_from_codes_category_num == "6").then( "White and Asian" ), - when(latest_ethnicity_from_codes_category_num == "7").then( - "Other Mixed" - ), - when(latest_ethnicity_from_codes_category_num == "8").then( - "Indian" - ), - when(latest_ethnicity_from_codes_category_num == "9").then( - "Pakistani" - ), - when(latest_ethnicity_from_codes_category_num == "10").then( - "Bangladeshi" - ), + when(latest_ethnicity_from_codes_category_num == "7").then("Other Mixed"), + when(latest_ethnicity_from_codes_category_num == "8").then("Indian"), + when(latest_ethnicity_from_codes_category_num == "9").then("Pakistani"), + when(latest_ethnicity_from_codes_category_num == "10").then("Bangladeshi"), when(latest_ethnicity_from_codes_category_num == "11").then( "Other South Asian" ), - when(latest_ethnicity_from_codes_category_num == "12").then( - "Caribbean" - ), - when(latest_ethnicity_from_codes_category_num == "13").then( - "African" - ), - when(latest_ethnicity_from_codes_category_num == "14").then( - "Other Black" - ), - when(latest_ethnicity_from_codes_category_num == "15").then( - "Chinese" - ), + when(latest_ethnicity_from_codes_category_num == "12").then("Caribbean"), + when(latest_ethnicity_from_codes_category_num == "13").then("African"), + when(latest_ethnicity_from_codes_category_num == "14").then("Other Black"), + when(latest_ethnicity_from_codes_category_num == "15").then("Chinese"), when(latest_ethnicity_from_codes_category_num == "16").then( "All other ethnic groups" ), - when(latest_ethnicity_from_codes_category_num == "17").then( - "Not stated" - ), - + when(latest_ethnicity_from_codes_category_num == "17").then("Not stated"), ) ethnicity_from_sus = case( - when(ethnicity_from_sus.code == "A").then( - "White British" - ), - when(ethnicity_from_sus.code == "B").then( - "White Irish" - ), - when(ethnicity_from_sus.code == "C").then( - "Other White" - ), - when(ethnicity_from_sus.code == "D").then( - "White and Caribbean" - ), - when(ethnicity_from_sus.code == "E").then( - "White and African" - ), - when(ethnicity_from_sus.code == "F").then( - "White and Asian" - ), - when(ethnicity_from_sus.code == "G").then( - "Other Mixed" - ), - when(ethnicity_from_sus.code == "H").then( - "Indian" - ), - when(ethnicity_from_sus.code == "J").then( - "Pakistani" - ), - when(ethnicity_from_sus.code == "K").then( - "Bangladeshi" - ), - when(ethnicity_from_sus.code == "L").then( - "Other South Asian" - ), - when(ethnicity_from_sus.code == "M").then( - "Caribbean" - ), - when(ethnicity_from_sus.code == "N").then( - "African" - ), - when(ethnicity_from_sus.code == "P").then( - "Other Black" - ), - when(ethnicity_from_sus.code == "R").then( - "Chinese" - ), - when(ethnicity_from_sus.code == "S").then( - "All other ethnic groups" - ), - when(ethnicity_from_sus.code == "Z").then( - "Not stated" - ), + when(ethnicity_from_sus.code == "A").then("White British"), + when(ethnicity_from_sus.code == "B").then("White Irish"), + when(ethnicity_from_sus.code == "C").then("Other White"), + when(ethnicity_from_sus.code == "D").then("White and Caribbean"), + when(ethnicity_from_sus.code == "E").then("White and African"), + when(ethnicity_from_sus.code == "F").then("White and Asian"), + when(ethnicity_from_sus.code == "G").then("Other Mixed"), + when(ethnicity_from_sus.code == "H").then("Indian"), + when(ethnicity_from_sus.code == "J").then("Pakistani"), + when(ethnicity_from_sus.code == "K").then("Bangladeshi"), + when(ethnicity_from_sus.code == "L").then("Other South Asian"), + when(ethnicity_from_sus.code == "M").then("Caribbean"), + when(ethnicity_from_sus.code == "N").then("African"), + when(ethnicity_from_sus.code == "P").then("Other Black"), + when(ethnicity_from_sus.code == "R").then("Chinese"), + when(ethnicity_from_sus.code == "S").then("All other ethnic groups"), + when(ethnicity_from_sus.code == "Z").then("Not stated"), ) ethnicity_combined = case( diff --git a/analysis/pf_variables_library.py b/analysis/pf_variables_library.py index ac68e75..f888e1b 100644 --- a/analysis/pf_variables_library.py +++ b/analysis/pf_variables_library.py @@ -1,6 +1,7 @@ # Function to check status of a condition within a specified time window from ehrql import months + def check_pregnancy_status(index_date, selected_events, codelist): return ( selected_events.where(selected_events.snomedct_code.is_in(codelist)) @@ -10,6 +11,7 @@ def check_pregnancy_status(index_date, selected_events, codelist): .exists_for_patient() ) + # Function to count number of coded events within a specified time window def count_past_events(index_date, selected_events, codelist, num_months): return ( @@ -22,14 +24,14 @@ def count_past_events(index_date, selected_events, codelist, num_months): .count_for_patient() ) + # Function to get events linked to a specified codelist def select_events_from_codelist(event_frame, codelist): - selected_events = event_frame.where( - event_frame.snomedct_code.is_in(codelist) - ) + selected_events = event_frame.where(event_frame.snomedct_code.is_in(codelist)) return selected_events + # Function to get events with specific consultation IDs def select_events_by_consultation_id(event_frame, consultation_ids): selected_events = event_frame.where( @@ -37,6 +39,7 @@ def select_events_by_consultation_id(event_frame, consultation_ids): ) return selected_events + # Function to get events within a time frame def select_events_between(event_frame, start_date, end_date): selected_events = event_frame.where( @@ -44,7 +47,10 @@ def select_events_between(event_frame, start_date, end_date): ) return selected_events -def select_events(event_frame, codelist=None, consultation_ids=None, start_date=None, end_date=None): + +def select_events( + event_frame, codelist=None, consultation_ids=None, start_date=None, end_date=None +): """ Wrapper function to select events based on codelist, consultation IDs, or a date range. Allows combining multiple selection criteria. @@ -54,8 +60,10 @@ def select_events(event_frame, codelist=None, consultation_ids=None, start_date= if codelist is not None: selected_events = select_events_from_codelist(selected_events, codelist) if consultation_ids is not None: - selected_events = select_events_by_consultation_id(selected_events, consultation_ids) + selected_events = select_events_by_consultation_id( + selected_events, consultation_ids + ) if start_date is not None and end_date is not None: selected_events = select_events_between(selected_events, start_date, end_date) - return selected_events \ No newline at end of file + return selected_events