Skip to content

Commit

Permalink
Merge pull request #19 from opensafely/viv3ckj/report-breakdown
Browse files Browse the repository at this point in the history
Add ethnicity breakdown and codelist
  • Loading branch information
viv3ckj authored Oct 4, 2024
2 parents b0137c7 + e87c20e commit 4f689dd
Show file tree
Hide file tree
Showing 6 changed files with 730 additions and 126 deletions.
15 changes: 15 additions & 0 deletions analysis/codelists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from ehrql import codelist_from_csv

# Import pharmacy first conditions codelist
pharmacy_first_conditions_codelist = codelist_from_csv(
"codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv",
column="code",
category_column="term",
)

# Import ethnicity codelist
ethnicity_codelist = codelist_from_csv(
"codelists/opensafely-ethnicity-snomed-0removed.csv",
column="snomedcode",
category_column="Grouping_6",
)
181 changes: 58 additions & 123 deletions analysis/measures_definition_pf_codes_conditions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from ehrql import INTERVAL, create_measures, months, codelist_from_csv, case, when
from ehrql.tables.tpp import clinical_events, practice_registrations, patients, addresses, ethnicity_from_sus
from ehrql import INTERVAL, create_measures, months, case, when
from ehrql.tables.tpp import (
clinical_events,
practice_registrations,
patients,
addresses,
)
from codelists import pharmacy_first_conditions_codelist, ethnicity_codelist

measures = create_measures()
measures.configure_dummy_data(population_size=1000)
Expand All @@ -19,14 +25,24 @@
"pharmacy_first_service": ["983341000000102"],
}

# Import pharmacy first conditions codelist
pharmacy_first_conditions_codelist = codelist_from_csv(
"codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv",
column="code",
category_column="term",
registration = practice_registrations.for_patient_on(INTERVAL.end_date)

latest_ethnicity_category_num = (
clinical_events.where(clinical_events.snomedct_code.is_in(ethnicity_codelist))
.where(clinical_events.date.is_on_or_before(INTERVAL.start_date))
.sort_by(clinical_events.date)
.last_for_patient()
.snomedct_code.to_category(ethnicity_codelist)
)

registration = practice_registrations.for_patient_on(INTERVAL.end_date)
latest_ethnicity_category_desc = case(
when(latest_ethnicity_category_num == "1").then("White"),
when(latest_ethnicity_category_num == "2").then("Mixed"),
when(latest_ethnicity_category_num == "3").then("Asian or Asian British"),
when(latest_ethnicity_category_num == "4").then("Black or Black British"),
when(latest_ethnicity_category_num == "5").then("Chinese or Other Ethnic Groups"),
when(latest_ethnicity_category_num.is_null()).then("Missing"),
)

# Age bands for age breakdown
age = patients.age_on(INTERVAL.start_date)
Expand All @@ -43,19 +59,31 @@
imd = addresses.for_patient_on(INTERVAL.start_date).imd_rounded
max_imd = 32844
imd_quintile = case(
when((imd >=0) & (imd < int(max_imd * 1 / 5))).then("1"),
when((imd >= 0) & (imd < int(max_imd * 1 / 5))).then("1"),
when(imd < int(max_imd * 2 / 5)).then("2"),
when(imd < int(max_imd * 3 / 5)).then("3"),
when(imd < int(max_imd * 4 / 5)).then("4"),
when(imd <= max_imd).then("5"),
otherwise="Missing"
otherwise="Missing",
)

# 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)
)

# Breakdown metrics to be produced as graphs
breakdown_metrics = {
"age": age_band,
"sex": patients.sex,
"imd": imd_quintile,
"region": registration.practice_nuts1_region_name,
"ethnicity": latest_ethnicity_category_desc,
}

# Define the denominator as the number of patients registered
denominator = registration.exists_for_patient() & patients.sex.is_in(["male", "female"])

# Create measures for pharmacy first services
for pharmacy_first_event, codelist in pharmacy_first_event_codes.items():
condition_events = selected_events.where(
Expand All @@ -65,70 +93,23 @@
# Define the numerator as the count of events for the condition
numerator = condition_events.count_for_patient()

# Define the denominator as the number of patients registered
denominator = registration.exists_for_patient() & patients.sex.is_in(["male", "female", "intersex"])

# Measures for overall clinical services graph
measures.define_measure(
name=f"count_{pharmacy_first_event}",
numerator=numerator,
denominator=denominator,
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for age breakdown of clinical services
measures.define_measure(
name=f"count_{pharmacy_first_event}_by_age",
numerator=numerator,
denominator=denominator,
group_by={
"age_band": age_band,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for sex breakdown of clinical services
measures.define_measure(
name=f"count_{pharmacy_first_event}_by_sex",
numerator=numerator,
denominator=denominator,
group_by={
"sex": patients.sex,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for IMD breakdown of clinical services
measures.define_measure(
name=f"count_{pharmacy_first_event}_by_imd",
numerator=numerator,
denominator=denominator,
group_by={
"imd": imd_quintile,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for region breakdown of clinical services
measures.define_measure(
name=f"count_{pharmacy_first_event}_by_region",
numerator=numerator,
denominator=denominator,
group_by={
"region": registration.practice_nuts1_region_name,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for ethnicity code breakdown of clinical services
measures.define_measure(
name=f"count_{pharmacy_first_event}_by_ethnicity",
numerator=numerator,
denominator=denominator,
group_by={
"ethnicity": ethnicity_from_sus.code,
},
intervals=months(monthly_intervals).starting_on(start_date),
)
# Nested loop for each breakdown measure in clinical services
for breakdown, variable in breakdown_metrics.items():
measures.define_measure(
name=f"count_{pharmacy_first_event}_by_{breakdown}",
numerator=numerator,
denominator=denominator,
group_by={breakdown: variable},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Create measures for pharmacy first conditions
pharmacy_first_conditions_codes = {}
Expand All @@ -145,66 +126,20 @@
# Define the numerator as the count of events for the condition
numerator = condition_events.count_for_patient()

# Define the denominator as the number of patients registered
denominator = registration.exists_for_patient()

# Measures for overall clinical services graph
measures.define_measure(
name=f"count_{condition_name}",
numerator=numerator,
denominator=denominator,
intervals=months(monthly_intervals).starting_on(start_date),
)
# Measures for age breakdown of clinical conditions
measures.define_measure(
name=f"count_{condition_name}_by_age",
numerator=numerator,
denominator=denominator,
group_by={
"age_band": age_band,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for age breakdown of clinical conditions
measures.define_measure(
name=f"count_{condition_name}_by_sex",
numerator=numerator,
denominator=denominator,
group_by={
"sex": patients.sex,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for imd breakdown of clinical conditions
measures.define_measure(
name=f"count_{condition_name}_by_imd",
numerator=numerator,
denominator=denominator,
group_by={
"imd": imd_quintile,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for region breakdown of clinical conditions
measures.define_measure(
name=f"count_{condition_name}_by_region",
numerator=numerator,
denominator=denominator,
group_by={
"region": registration.practice_nuts1_region_name,
},
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for region breakdown of clinical conditions
measures.define_measure(
name=f"count_{condition_name}_by_ethnicity",
numerator=numerator,
denominator=denominator,
group_by={
"ethnicity": ethnicity_from_sus.code,
},
intervals=months(monthly_intervals).starting_on(start_date),
)
# Nested loop for each breakdown measure in clinical conditions
for breakdown, variable in breakdown_metrics.items():
measures.define_measure(
name=f"count_{condition_name}_by_{breakdown}",
numerator=numerator,
denominator=denominator,
group_by={breakdown: variable},
intervals=months(monthly_intervals).starting_on(start_date),
)
6 changes: 6 additions & 0 deletions codelists/codelists.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
"url": "https://www.opencodelists.org/codelist/user/chriswood/pharmacy-first-clinical-pathway-conditions/7ec97762/",
"downloaded_at": "2024-08-22 12:53:00.167017Z",
"sha": "bed7f74add5c2d2ac6f7120d89f5ba94e57a28cb"
},
"opensafely-ethnicity-snomed-0removed.csv": {
"id": "opensafely/ethnicity-snomed-0removed/2e641f61",
"url": "https://www.opencodelists.org/codelist/opensafely/ethnicity-snomed-0removed/2e641f61/",
"downloaded_at": "2024-10-03 11:34:59.878387Z",
"sha": "8b849b5b785d80306e488d0109640c8c1d2da01b"
}
}
}
3 changes: 2 additions & 1 deletion codelists/codelists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
user/chriswood/pharmacy-first-clinical-pathway-conditions/7ec97762
user/chriswood/pharmacy-first-clinical-pathway-conditions/7ec97762
opensafely/ethnicity-snomed-0removed/2e641f61
Loading

0 comments on commit 4f689dd

Please sign in to comment.