From 7a80e1ffa1e711ffdc7b3d7798a1ca55ef9fb2b5 Mon Sep 17 00:00:00 2001 From: Viveck Date: Fri, 23 Aug 2024 14:09:59 +0000 Subject: [PATCH] Graphs for events and conditions added --- analysis/report_measures.py | 57 ++++++++++------ analysis/reports/pf_report.Rmd | 43 ++++++++---- analysis/reports/pf_report.html | 3 +- codelists/codelists.json | 9 ++- codelists/codelists.txt | 1 + ...macy-first-clinical-pathway-conditions.csv | 8 +++ lib/functions/function_plot_measures.R | 65 +++++++++++++++---- lib/functions/sketch.R | 23 ------- 8 files changed, 138 insertions(+), 71 deletions(-) create mode 100644 codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv delete mode 100644 lib/functions/sketch.R diff --git a/analysis/report_measures.py b/analysis/report_measures.py index 1e50702..12b5cc8 100644 --- a/analysis/report_measures.py +++ b/analysis/report_measures.py @@ -15,6 +15,25 @@ # Pharmacy First service (qualifier value) "pharmacy_first_service": ["983341000000102"], } +# The following codes come from codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv file. +# Currently written as a hardcoded dictionary to allow for easy for looping (ln66-83), but will be imported from codelist csv in future commits. +# Pharmacy First seven clinical conditions codelist +pharmacy_first_conditions_codes = { + # Community Pharmacy (CP) Blood Pressure (BP) Check Service (procedure) + "acute_otitis_media": ["3110003"], + # Community Pharmacy (CP) Contraception Service (procedure) + "herpes_zoster": ["4740000"], + # Community Pharmacist (CP) Consultation Service for minor illness (procedure) + "acute_sinusitis": ["15805002"], + # Pharmacy First service (qualifier value) + "impetigo": ["48277006"], + # Community Pharmacy (CP) Contraception Service (procedure) + "infected_insect_bite": ["262550002"], + # Community Pharmacist (CP) Consultation Service for minor illness (procedure) + "acute_pharyngitis": ["363746003"], + # Pharmacy First service (qualifier value) + "uncomplicated_urinary_tract_infection": ["1090711000000102"], +} registration = practice_registrations.for_patient_on(INTERVAL.end_date) @@ -24,7 +43,7 @@ ) # Loop through each condition to create a measure -for condition_name, codelist in pharmacy_first_event_codes.items(): +for pharmacy_first_event, codelist in pharmacy_first_event_codes.items(): condition_events = selected_events.where( clinical_events.snomedct_code.is_in(codelist) ) @@ -36,33 +55,29 @@ # Define the denominator as the number of patients registered denominator = registration.exists_for_patient() - # Define the measure measures.define_measure( - name=f"count_{condition_name}", + name=f"count_{pharmacy_first_event}", numerator=numerator, denominator=denominator, intervals=months(8).starting_on("2023-11-01") ) -# # Count pharmacy first codes -# pharmacy_first_code_counts = {} +# Loop through each CLINICAL condition to create a measure +for condition_name, condition_code in pharmacy_first_conditions_codes.items(): + condition_events = selected_events.where( + clinical_events.snomedct_code.is_in(condition_code) + ) -# for code_desc, code in pharmacy_first_event_codes.items(): -# count_codes_query = selected_events.where( -# selected_events.snomedct_code.is_in(code) -# ).count_for_patient() -# pharmacy_first_code_counts[f"count_{code_desc}"] = count_codes_query + # Define the numerator as the count of events for the condition + numerator = condition_events.count_for_patient() -# for measures_name, code_counts in pharmacy_first_code_counts.items(): -# measures.define_measure( -# name=measures_name, -# numerator=code_counts, -# group_by={ -# "practice_region": registration.practice_nuts1_region_name -# }, -# denominator=patients.exists_for_patient(), -# intervals=intervals, -# ) + # Define the denominator as the number of patients registered + denominator = registration.exists_for_patient() - \ No newline at end of file + measures.define_measure( + name=f"count_{condition_name}", + numerator=numerator, + denominator=denominator, + intervals=months(8).starting_on("2023-11-01") + ) \ No newline at end of file diff --git a/analysis/reports/pf_report.Rmd b/analysis/reports/pf_report.Rmd index 6f5c7d0..231319a 100644 --- a/analysis/reports/pf_report.Rmd +++ b/analysis/reports/pf_report.Rmd @@ -24,7 +24,6 @@ pre[class] { - ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = FALSE) @@ -34,13 +33,13 @@ library(readr) ``` -$~$ +
## Background Add background here. -$~$ +
## Methods @@ -58,8 +57,7 @@ Links to the codelist for each analysis can be found beneath the relevant sectio This report contains the following sections: - - +* [Clinical Services](#services) * [Clinical Pathways](#pathways) * [Clinical condition](#condition) * [Days of the week](#dotw) @@ -82,9 +80,11 @@ This report contains the following sections: * [Age](#contra_age) * [Region](#contra_region) -$~$ +
-## Clinical services +## Clinical Services + +
```{r, message=FALSE, warning=FALSE} # Load plotting function @@ -94,12 +94,15 @@ df_measures <- readr::read_csv( here::here("output", "report", "conditions_measures.csv") ) -plot1_conditions <- plot_measures(df_measures, +plot_services <- plot_measures(df_measures, title = "Number of consultations for each clinical service per month", -x_label = "Month", -y_label = "Number of Consultations", -color_label = "Clinical Service") -print(plot1_conditions) +measure_names = c("count_blood_pressure_service", + "count_contraception_service", + "count_consultation_service", + "count_pharmacy_first_service"), +y_label = "Number of codes for consultations", +) +print(plot_services) ``` ## Clinical Pathways @@ -116,6 +119,22 @@ Here we show the number of consultations for each of the Pharmacy First Clinical > LINE CHART WITH COUNT OF EACH CLINICAL CONDITION +```{r, message=FALSE, warning=FALSE} + +plot_conditions <- plot_measures(df_measures, +title = "Number of consultations for each clinical condition per month", +measure_names = c("count_acute_otitis_media", + "count_herpes_zoster", + "count_acute_sinusitis", + "count_impetigo", + "count_infected_insect_bite", + "count_acute_pharyngitis", + "count_uncomplicated_urinary_tract_infection"), +y_label = "Number of codes for consultations", +) +print(plot_conditions) +``` + ### Counts by day of the week Here we show the number of consultations for the Pharmacy First Clinical Pathways by day of the week the consultation was conducted. (Mainly of interest to show what happens at weekends etc e.g. when GP practices are closed). diff --git a/analysis/reports/pf_report.html b/analysis/reports/pf_report.html index f1a3584..9599104 100644 --- a/analysis/reports/pf_report.html +++ b/analysis/reports/pf_report.html @@ -286,7 +286,7 @@

Methods

Clinical services

-

+

Clinical Pathways

@@ -300,6 +300,7 @@

Counts by clinical condition

LINE CHART WITH COUNT OF EACH CLINICAL CONDITION

+

diff --git a/codelists/codelists.json b/codelists/codelists.json index 5cc232f..6ec4881 100644 --- a/codelists/codelists.json +++ b/codelists/codelists.json @@ -1,3 +1,10 @@ { - "files": {} + "files": { + "user-chriswood-pharmacy-first-clinical-pathway-conditions.csv": { + "id": "user/chriswood/pharmacy-first-clinical-pathway-conditions/7ec97762", + "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" + } + } } \ No newline at end of file diff --git a/codelists/codelists.txt b/codelists/codelists.txt index e69de29..8f233d1 100644 --- a/codelists/codelists.txt +++ b/codelists/codelists.txt @@ -0,0 +1 @@ +user/chriswood/pharmacy-first-clinical-pathway-conditions/7ec97762 \ No newline at end of file diff --git a/codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv b/codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv new file mode 100644 index 0000000..88958da --- /dev/null +++ b/codelists/user-chriswood-pharmacy-first-clinical-pathway-conditions.csv @@ -0,0 +1,8 @@ +code,term +1090711000000102,Uncomplicated urinary tract infection +15805002,Acute sinusitis +262550002,Infected insect bite +3110003,Acute otitis media +363746003,Acute pharyngitis +4740000,Herpes zoster +48277006,Impetigo diff --git a/lib/functions/function_plot_measures.R b/lib/functions/function_plot_measures.R index 2455806..241fa2a 100644 --- a/lib/functions/function_plot_measures.R +++ b/lib/functions/function_plot_measures.R @@ -3,6 +3,7 @@ #' Creates a line plot of measures over time, with customisable labels and colours. #' #' @param data A dataframe containing the data to plot. +#' @param measure_names Strings specifiying the names of measure columns to be plotted. #' @param title A string specifying the title of the plot. Default is NULL. #' @param x_label A string specifying the label for the x-axis. Default is NULL. #' @param y_label A string specifying the label for the y-axis. Default is NULL. @@ -13,24 +14,49 @@ #' #' @return A ggplot object. +# Define the function plot_measures <- function( data, + measure_names, date_col = "interval_end", value_col = "numerator", measure_col = "measure", title = NULL, x_label = NULL, y_label = NULL, - color_label = NULL) { + color_label = NULL, + axis_x_text_size = 7) { + + # Check if the necessary columns exist in the data + if (date_col %in% names(data) == FALSE) { + stop("Data does not have a column with the name '", date_col, "'") + } else if (value_col %in% names(data) == FALSE) { + stop("Data does not have a column with the name '", value_col, "'") + } else if (measure_col %in% names(data) == FALSE) { + stop("Data does not have a column with the name '", measure_col, "'") + } + + # Convert column names to symbols + date_sym <- sym(date_col) + value_sym <- sym(value_col) + measure_sym <- sym(measure_col) + + # Ensure the date column is of Date type + data <- data %>% + mutate(!!date_sym := as.Date(!!date_sym)) + + # Filter measures column for user-specified measure names + data <- data %>% + filter(!!measure_sym %in% measure_names) # Create plot plot1 <- ggplot( data, aes( - x = {{date_col}}, - y = {{value_col}}, - color = {{measure_col}}, - group = {{measure_col}} + x = !!date_sym, + y = !!value_sym, + color = !!measure_sym, + group = !!measure_sym ) ) + geom_line() + @@ -38,17 +64,30 @@ plot_measures <- function( title = title, x = x_label, y = y_label, - color = "Condition" + color = color_label + ) + + scale_y_continuous( + limits = c(0, NA), ) + - # Setting the minimum y-value - ylim(0, NA) + - # Applying the minimal theme theme_minimal() + + theme(axis.text.x = element_text(size = axis_x_text_size), + legend.position="bottom", + legend.key.size = unit(0.5, "cm"), + legend.text = element_text(size = 6), + legend.title = element_text(size = 8)) + + guides( + color = guide_legend(nrow = 2) # Adjust number of rows in the legend +) + + geom_vline( + xintercept = lubridate::as_date("2024-02-01"), + linetype = "dotted", + colour = "orange", + linewidth = .7) + scale_x_date( - date_breaks = "1 month", - date_labels = "%b %Y", + date_breaks = "1 month", + date_labels = "%b %Y" ) - # Return plot + plot1 -} +} \ No newline at end of file diff --git a/lib/functions/sketch.R b/lib/functions/sketch.R deleted file mode 100644 index d4a3725..0000000 --- a/lib/functions/sketch.R +++ /dev/null @@ -1,23 +0,0 @@ -# In this file we're trying out some code -library(tidyverse) -library(ggplot2) -library(gridExtra) # For arranging multiple plots - -# Load data -df_measures <- readr::read_csv( - here::here("output", "report", "conditions_measures.csv") -) - -# Develop plotting function -p <- ggplot(df_measures, aes(x = interval_end, y = numerator, color = measure, group = measure)) + - geom_line() + - labs( - title = paste("Number of Consultations for each Pharmacy First Clinical Condition per month"), - x = "Date", - y = "Number of Consultations", - color = "Condition" - ) + - ylim(0, NA) - -source(here::here("lib", "functions", "graph_func.R")) -plot1_conditions <- plot_pharmacy_first_conditions(df_measures)