diff --git a/analysis/codelists.py b/analysis/codelists.py index 9a269a6..18e4cb7 100644 --- a/analysis/codelists.py +++ b/analysis/codelists.py @@ -68,3 +68,12 @@ # Community Pharmacist Consultation Service for minor illness - 1577041000000109 # Pharmacy First service - 983341000000102 pharmacy_first_consultation_codelist = ["1577041000000109", "983341000000102"] + +# PF codes separated for individual analysis +pharmacy_first_event_codes = { + # Community Pharmacist (CP) Consultation Service for minor illness (procedure) + "consultation_service": ["1577041000000109"], + # Pharmacy First service (qualifier value) + "pharmacy_first_service": ["983341000000102"], + "combined_pf_service": ["1577041000000109", "983341000000102"], +} diff --git a/analysis/config.py b/analysis/config.py new file mode 100644 index 0000000..0eacb9b --- /dev/null +++ b/analysis/config.py @@ -0,0 +1,15 @@ +# measures_definition_pf_breakdown.py +start_date_measure_pf_breakdown = "2023-11-01" +monthly_intervals_measure_pf_breakdown = 12 + +# measures_definition_pf_condition_provider.py +start_date_measure_condition_provider = "2023-01-01" +monthly_intervals_measure_condition_provider = 22 + +# measures_definition_pf_descriptive_stats.py +start_date_measure_descriptive_stats = "2024-02-01" +monthly_intervals_measure_descriptive_stats = 9 + +# measures_definition_pf_medications.py +start_date_measure_medications = "2023-11-01" +monthly_intervals_measure_medications = 9 \ No newline at end of file diff --git a/analysis/measures_definition_pf_breakdown.py b/analysis/measures_definition_pf_breakdown.py index 8f5d730..d024413 100644 --- a/analysis/measures_definition_pf_breakdown.py +++ b/analysis/measures_definition_pf_breakdown.py @@ -11,13 +11,16 @@ ethnicity_codelist, ) -from pf_dataset import pharmacy_first_event_codes, get_latest_ethnicity +from pf_dataset import get_latest_ethnicity +from codelists import pharmacy_first_event_codes, pharmacy_first_consultation_codelist +from config import start_date_measure_pf_breakdown, monthly_intervals_measure_pf_breakdown +from pf_variables_library import select_events measures = create_measures() measures.configure_dummy_data(population_size=1000) -start_date = "2023-11-01" -monthly_intervals = 12 +start_date = start_date_measure_pf_breakdown +monthly_intervals = monthly_intervals_measure_pf_breakdown registration = practice_registrations.for_patient_on(INTERVAL.end_date) ethnicity_combined = get_latest_ethnicity( @@ -56,16 +59,12 @@ otherwise="Missing", ) -pharmacy_first_ids = clinical_events.where( - clinical_events.snomedct_code.is_in( - pharmacy_first_event_codes["combined_pf_service"] - ) -).consultation_id +pharmacy_first_ids = select_events(clinical_events, codelist=pharmacy_first_consultation_codelist).consultation_id -# Select clinical events in interval date range -selected_events = clinical_events.where( - clinical_events.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date) -).where(clinical_events.consultation_id.is_in(pharmacy_first_ids)) +# # 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) +) # Breakdown metrics to be produced as graphs breakdown_metrics = { diff --git a/analysis/measures_definition_pf_condition_provider.py b/analysis/measures_definition_pf_condition_provider.py index c0d8e56..e70b07f 100644 --- a/analysis/measures_definition_pf_condition_provider.py +++ b/analysis/measures_definition_pf_condition_provider.py @@ -8,27 +8,22 @@ pharmacy_first_conditions_codes, imd_quintile, ) -from pf_dataset import pharmacy_first_event_codes - +from codelists import pharmacy_first_consultation_codelist +from config import start_date_measure_condition_provider, monthly_intervals_measure_condition_provider +from pf_variables_library import select_events measures = create_measures() measures.configure_dummy_data(population_size=1000) -start_date = "2023-01-01" -monthly_intervals = 22 +start_date = start_date_measure_condition_provider +monthly_intervals = monthly_intervals_measure_condition_provider registration = practice_registrations.for_patient_on(INTERVAL.end_date) -selected_events = clinical_events.where( - clinical_events.date.is_on_or_between(INTERVAL.start_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 = selected_events.where( - selected_events.snomedct_code.is_in( - pharmacy_first_event_codes["combined_pf_service"] - ) -).exists_for_patient() +has_pharmacy_first = select_events(selected_events, codelist=pharmacy_first_consultation_codelist).exists_for_patient() for condition_name, condition_code in pharmacy_first_conditions_codes.items(): condition_events = selected_events.where( diff --git a/analysis/measures_definition_pf_descriptive_stats.py b/analysis/measures_definition_pf_descriptive_stats.py index 45ec28e..61db72a 100644 --- a/analysis/measures_definition_pf_descriptive_stats.py +++ b/analysis/measures_definition_pf_descriptive_stats.py @@ -2,45 +2,42 @@ from ehrql.tables.raw.tpp import medications from ehrql.tables.tpp import practice_registrations, patients, clinical_events -from pf_variables_library import get_consultation_ids, get_consultationid_events +from pf_variables_library import select_events_from_codelist, select_events_by_consultation_id from codelists import ( pharmacy_first_med_codelist, pharmacy_first_consultation_codelist, pharmacy_first_conditions_codelist, ) +from config import start_date_measure_descriptive_stats, monthly_intervals_measure_descriptive_stats measures = create_measures() measures.configure_dummy_data(population_size=1000) measures.configure_disclosure_control(enabled=True) -start_date = "2024-02-01" -monthly_intervals = 9 +start_date = start_date_measure_descriptive_stats +monthly_intervals = monthly_intervals_measure_descriptive_stats registration = practice_registrations.for_patient_on(INTERVAL.end_date) # Function to retrieve consultation ids from clinical events that are PF consultations -pharmacy_first_ids = get_consultation_ids( +pharmacy_first_ids = select_events_from_codelist( clinical_events, pharmacy_first_consultation_codelist -) +).consultation_id # Function to retrieve selected events using pharmacy first ids -selected_clinical_events = get_consultationid_events( +selected_clinical_events = select_events_by_consultation_id( clinical_events, pharmacy_first_ids ).where(clinical_events.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date)) -selected_med_events = get_consultationid_events(medications, pharmacy_first_ids).where( +selected_med_events = select_events_by_consultation_id(medications, pharmacy_first_ids).where( medications.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date) ) # Create variable which contains boolean values of whether pharmacy first event exists for patient -has_pf_consultation = selected_clinical_events.where( - selected_clinical_events.snomedct_code.is_in(pharmacy_first_consultation_codelist) -).exists_for_patient() +has_pf_consultation = select_events_from_codelist(selected_clinical_events, pharmacy_first_consultation_codelist).exists_for_patient() # PF consultations with PF clinical condition -has_pf_condition = selected_clinical_events.where( - selected_clinical_events.snomedct_code.is_in(pharmacy_first_conditions_codelist) -).exists_for_patient() +has_pf_condition = select_events_from_codelist(selected_clinical_events, pharmacy_first_conditions_codelist).exists_for_patient() # PF consultations with prescribed PF medication has_pf_medication = selected_med_events.where( diff --git a/analysis/measures_definition_pf_medications.py b/analysis/measures_definition_pf_medications.py index e969fad..e8364cf 100644 --- a/analysis/measures_definition_pf_medications.py +++ b/analysis/measures_definition_pf_medications.py @@ -5,46 +5,32 @@ practice_registrations, ) from ehrql.tables.raw.tpp import medications - -from pf_dataset import pharmacy_first_event_codes -from codelists import * - -pharmacy_first_med_codes = ( - acute_otitis_media_tx_cod - + impetigo_treatment_tx_cod - + infected_insect_bites_tx_cod - + shingles_treatment_tx_cod - + sinusitis_tx_cod - + sore_throat_tx_cod - + urinary_tract_infection_tx_cod -) +from codelists import pharmacy_first_consultation_codelist, pharmacy_first_med_codelist +from config import start_date_measure_medications, monthly_intervals_measure_medications +from pf_variables_library import select_events measures = create_measures() measures.configure_dummy_data(population_size=1000) # Turn off during code development, but turn on before running against on the server measures.configure_disclosure_control(enabled=True) -start_date = "2023-11-01" -monthly_intervals = 9 +start_date = start_date_measure_medications +monthly_intervals = monthly_intervals_measure_medications registration = practice_registrations.for_patient_on(INTERVAL.end_date) # Select Pharmacy First consultations during interval date range -pharmacy_first_events = clinical_events.where( - clinical_events.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date) -).where( - clinical_events.snomedct_code.is_in( - pharmacy_first_event_codes["combined_pf_service"] - ) +pharmacy_first_events = select_events(clinical_events, start_date=INTERVAL.start_date, end_date=INTERVAL.end_date).where( + clinical_events.snomedct_code.is_in(pharmacy_first_consultation_codelist) ) pharmacy_first_ids = pharmacy_first_events.consultation_id has_pharmacy_first_consultation = pharmacy_first_events.exists_for_patient() # Select medications prescribed with PF consultation ID -selected_medications = medications.where( - medications.date.is_on_or_between(INTERVAL.start_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 = ( @@ -53,7 +39,7 @@ # Check if a medication is from our PF codelists has_pharmacy_first_medication = first_selected_medication.is_in( - pharmacy_first_med_codes + pharmacy_first_med_codelist ) # Numerator, patients with a PF medication diff --git a/analysis/pf_codelist_functions.py b/analysis/pf_codelist_functions.py deleted file mode 100644 index f4c7585..0000000 --- a/analysis/pf_codelist_functions.py +++ /dev/null @@ -1,10 +0,0 @@ -# Function which formats the unformatted conditions codelist, and retrieves its codes -# Unused in descriptive_stats.py, but will be used in later tickets (streamline breakdown.py) -def get_pf_condition_codes(pharmacy_first_conditions_codelist): - pharmacy_first_conditions_codes = {} - for codes, term in pharmacy_first_conditions_codelist.items(): - normalised_term = term.lower().replace(" ", "_") - codes = [codes] - pharmacy_first_conditions_codes[normalised_term] = codes - - return pharmacy_first_conditions_codes diff --git a/analysis/pf_dataset.py b/analysis/pf_dataset.py index 8bde032..85288e4 100644 --- a/analysis/pf_dataset.py +++ b/analysis/pf_dataset.py @@ -2,18 +2,8 @@ from pf_variables_library import check_pregnancy_status, count_past_events -pharmacy_first_event_codes = { - # # Community Pharmacy (CP) Blood Pressure (BP) Check Service (procedure) - # "blood_pressure_service": ["1659111000000107"], - # # Community Pharmacy (CP) Contraception Service (procedure) - # "contraception_service": ["1659121000000101"], - # Community Pharmacist (CP) Consultation Service for minor illness (procedure) - "consultation_service": ["1577041000000109"], - # Pharmacy First service (qualifier value) - "pharmacy_first_service": ["983341000000102"], - "combined_pf_service": ["1577041000000109", "983341000000102"], -} - +# 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 diff --git a/analysis/pf_variables_library.py b/analysis/pf_variables_library.py index 66eacdc..ac68e75 100644 --- a/analysis/pf_variables_library.py +++ b/analysis/pf_variables_library.py @@ -1,7 +1,6 @@ # 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)) @@ -11,7 +10,6 @@ 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 ( @@ -24,19 +22,40 @@ 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) + ) -# Function to get consultation IDs linked to a specified codelist -def get_consultation_ids(clinical_events, codelist): - consultation_ids = clinical_events.where( - clinical_events.snomedct_code.is_in(codelist) - ).consultation_id - - return consultation_ids - + return selected_events # Function to get events with specific consultation IDs -def get_consultationid_events(event_frame, consultation_ids): +def select_events_by_consultation_id(event_frame, consultation_ids): selected_events = event_frame.where( event_frame.consultation_id.is_in(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( + event_frame.date.is_on_or_between(start_date, end_date) + ) + return selected_events + +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. + """ + selected_events = event_frame + + 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) + 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