Skip to content
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

added breakdown graphs and facet function #11

Merged
merged 6 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 110 additions & 2 deletions analysis/measures_definition_pf_codes_conditions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ehrql import INTERVAL, create_measures, months, codelist_from_csv
from ehrql.tables.tpp import clinical_events, practice_registrations
from ehrql import INTERVAL, create_measures, months, codelist_from_csv, case, when
from ehrql.tables.tpp import clinical_events, practice_registrations, patients, addresses

measures = create_measures()
measures.configure_dummy_data(population_size=1000)
Expand Down Expand Up @@ -28,6 +28,28 @@

registration = practice_registrations.for_patient_on(INTERVAL.end_date)

# Age bands for age breakdown
age = patients.age_on(INTERVAL.start_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"),
)
Comment on lines +32 to +40
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great!


# IMD groupings for IMD breakdown
imd = addresses.for_patient_on(INTERVAL.start_date).imd_rounded
imd_quintile = case(
when((imd >=0) & (imd < int(32844 * 1 / 5))).then("1 (most deprived)"),
when(imd < int(32844 * 2 / 5)).then("2"),
when(imd < int(32844 * 3 / 5)).then("3"),
when(imd < int(32844 * 4 / 5)).then("4"),
when(imd < int(32844 * 5 / 5)).then("5 (least deprived)"),
otherwise="unknown"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although the code does exactly the same as this suggestions in the tutorial, I'd use something like this because it's a bit easier to review and harder to make a mistake:

imd_rounded = addresses.for_patient_on(
    INTERVAL.start_date
).imd_rounded
max_imd = 32844
dataset.imd_quintile = case(
    when(imd_rounded < int(max_imd * 1 / 5)).then(1),
    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),
    when(imd_rounded <= max_imd).then(5),
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be consistent with the value for "Missing" data, I'd use the same as for age bands here as well

Suggested change
otherwise="unknown"
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)
Expand All @@ -51,6 +73,49 @@
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),
)
Comment on lines +78 to +120
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks correct, but I think we can make this much shorter if we group by multiple features, see https://docs.opensafely.org/ehrql/explanation/measures/#grouping-by-multiple-features


# Create measures for pharmacy first conditions
pharmacy_first_conditions_codes = {}
Expand All @@ -76,3 +141,46 @@
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),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment above, I think we can make this consierably shorter

13 changes: 13 additions & 0 deletions lib/functions/function_plot_measures.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ plot_measures <- function(
title = NULL,
x_label = NULL,
y_label = NULL,
facet_var = NULL,
color_label = NULL,
rotate_x_labels = FALSE,
axis_x_text_size = 7) {
# Check if the necessary columns exist in the data
if (date_col %in% names(data) == FALSE) {
Expand Down Expand Up @@ -99,5 +101,16 @@ plot_measures <- function(
date_labels = "%b %Y"
)

if (!is.null(facet_var)) {
facet_sym <- sym(facet_var)
plot1 <- plot1 + facet_wrap(vars(!!facet_sym), scales = "free_x")

}

# Conditionally apply x-axis label rotation if rotate_x_labels is TRUE
if (rotate_x_labels) {
plot1 <- plot1 + theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

plot1
}
190 changes: 189 additions & 1 deletion reports/pharmacy_first_report.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,89 @@ pf_clinical_condition_dict <- c(
"count_uncomplicated_urinary_tract_infection" = "UTI"
)

# Define the custom labels for clinical conditions by age
pf_clinical_condition_dict_by_age <- c(
"count_acute_otitis_media_by_age" = "Acute Otitis Media",
"count_herpes_zoster_by_age" = "Herpes Zoster",
"count_acute_sinusitis_by_age" = "Acute Sinusitis",
"count_impetigo_by_age" = "Impetigo",
"count_infected_insect_bite_by_age" = "Infected Insect Bite",
"count_acute_pharyngitis_by_age" = "Acute Pharyngitis",
"count_uncomplicated_urinary_tract_infection_by_age" = "UTI"
)

# Define the custom labels for clinical conditions by sex
pf_clinical_condition_dict_by_sex <- c(
"count_acute_otitis_media_by_sex" = "Acute Otitis Media",
"count_herpes_zoster_by_sex" = "Herpes Zoster",
"count_acute_sinusitis_by_sex" = "Acute Sinusitis",
"count_impetigo_by_sex" = "Impetigo",
"count_infected_insect_bite_by_sex" = "Infected Insect Bite",
"count_acute_pharyngitis_by_sex" = "Acute Pharyngitis",
"count_uncomplicated_urinary_tract_infection_by_sex" = "UTI"
)

# Define the custom labels for clinical conditions by IMD
pf_clinical_condition_dict_by_imd <- c(
"count_acute_otitis_media_by_imd" = "Acute Otitis Media",
"count_herpes_zoster_by_imd" = "Herpes Zoster",
"count_acute_sinusitis_by_imd" = "Acute Sinusitis",
"count_impetigo_by_imd" = "Impetigo",
"count_infected_insect_bite_by_imd" = "Infected Insect Bite",
"count_acute_pharyngitis_by_imd" = "Acute Pharyngitis",
"count_uncomplicated_urinary_tract_infection_by_imd" = "UTI"
)

# Define the custom labels for clinical conditions by region
pf_clinical_condition_dict_by_region <- c(
"count_acute_otitis_media_by_region" = "Acute Otitis Media",
"count_herpes_zoster_by_region" = "Herpes Zoster",
"count_acute_sinusitis_by_region" = "Acute Sinusitis",
"count_impetigo_by_region" = "Impetigo",
"count_infected_insect_bite_by_region" = "Infected Insect Bite",
"count_acute_pharyngitis_by_region" = "Acute Pharyngitis",
"count_uncomplicated_urinary_tract_infection_by_region" = "UTI"
)

# Define the custom labels for clinical services
pf_clinical_service_dict <- c(
"count_blood_pressure_service" = "Blood Pressure Service",
"count_contraception_service" = "Contraception Service",
"count_consultation_service" = "Consultation Service",
"count_pharmacy_first_service" = "Pharmacy First Service"
)

# Define the custom labels for clinical services by age
pf_clinical_service_dict_by_age <- c(
"count_blood_pressure_service_by_age" = "Blood Pressure Service",
"count_contraception_service_by_age" = "Contraception Service",
"count_consultation_service_by_age" = "Consultation Service",
"count_pharmacy_first_service_by_age" = "Pharmacy First Service"
)

# Define the custom labels for clinical services by sex
pf_clinical_service_dict_by_sex <- c(
"count_blood_pressure_service_by_sex" = "Blood Pressure Service",
"count_contraception_service_by_sex" = "Contraception Service",
"count_consultation_service_by_sex" = "Consultation Service",
"count_pharmacy_first_service_by_sex" = "Pharmacy First Service"
)

# Define the custom labels for clinical services by IMD
pf_clinical_service_dict_by_imd <- c(
"count_blood_pressure_service_by_imd" = "Blood Pressure Service",
"count_contraception_service_by_imd" = "Contraception Service",
"count_consultation_service_by_imd" = "Consultation Service",
"count_pharmacy_first_service_by_imd" = "Pharmacy First Service"
)

# Define the custom labels for clinical services by region
pf_clinical_service_dict_by_region <- c(
"count_blood_pressure_service_by_region" = "Blood Pressure Service",
"count_contraception_service_by_region" = "Contraception Service",
"count_consultation_service_by_region" = "Consultation Service",
"count_pharmacy_first_service_by_region" = "Pharmacy First Service"
)
```

# Background
Expand Down Expand Up @@ -75,9 +151,65 @@ plot_measures(df_measures,
)
```

### Clinical Services by Age

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical service by age per month",
measure_names = names(pf_clinical_service_dict_by_age),
custom_labels = pf_clinical_service_dict_by_age,
y_label = "Number of codes for consultations",
facet_var = "age_band",
rotate_x_labels = TRUE
)
```

### Clinical Services by Sex

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical service by sex per month",
measure_names = names(pf_clinical_service_dict_by_sex),
custom_labels = pf_clinical_service_dict_by_sex,
y_label = "Number of codes for consultations",
facet_var = "sex",
rotate_x_labels = TRUE
)
```

### Clinical Services by IMD

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical service by IMD per month",
measure_names = names(pf_clinical_service_dict_by_imd),
custom_labels = pf_clinical_service_dict_by_imd,
y_label = "Number of codes for consultations",
facet_var = "imd",
rotate_x_labels = TRUE
)
```

### Clinical Services by Region

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical service by region per month",
measure_names = names(pf_clinical_service_dict_by_region),
custom_labels = pf_clinical_service_dict_by_region,
y_label = "Number of codes for consultations",
facet_var = "region",
rotate_x_labels = TRUE
)
```

## Clinical Pathways

This section focuses on the Clinical Pathways element of that Pharmacy First service.
This section focuses on the Clinical Pathways element of the Pharmacy First service.

## Clinical Condition

Expand All @@ -92,3 +224,59 @@ plot_measures(df_measures,
y_label = "Number of codes for consultations",
)
```

### Clinical Conditions by Age

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical condition by age per month",
measure_names = names(pf_clinical_condition_dict_by_age),
custom_labels = pf_clinical_condition_dict_by_age,
y_label = "Number of codes for consultations",
facet_var = "age_band",
rotate_x_labels = TRUE
)
```

### Clinical Conditions by Sex

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical condition by sex per month",
measure_names = names(pf_clinical_condition_dict_by_sex),
custom_labels = pf_clinical_condition_dict_by_sex,
y_label = "Number of codes for consultations",
facet_var = "sex",
rotate_x_labels = TRUE
)
```

### Clinical Conditions by IMD

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical condition by IMD per month",
measure_names = names(pf_clinical_condition_dict_by_imd),
custom_labels = pf_clinical_condition_dict_by_imd,
y_label = "Number of codes for consultations",
facet_var = "imd",
rotate_x_labels = TRUE
)
```

### Clinical Conditions by Region

```{r, message=FALSE, warning=FALSE}

plot_measures(df_measures,
title = "Number of consultations for each clinical condition by region per month",
measure_names = names(pf_clinical_condition_dict_by_region),
custom_labels = pf_clinical_condition_dict_by_region,
y_label = "Number of codes for consultations",
facet_var = "region",
rotate_x_labels = TRUE
)
```