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

Viv3ckj/move validation data #90

Merged
merged 7 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 5 additions & 1 deletion analysis/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@

# measures_definition_pf_medications.py
start_date_measure_medications = "2023-11-01"
monthly_intervals_measure_medications = 9
monthly_intervals_measure_medications = 9

# measures_definition_pf_consultation_pf_counts.py
start_date_measure_med_counts = "2024-02-01"
monthly_intervals_measure_med_counts = 6
72 changes: 72 additions & 0 deletions analysis/measures_definition_pf_consultation_med_counts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from ehrql import INTERVAL, create_measures, months
from ehrql.tables.tpp import (
patients,
clinical_events,
practice_registrations,
)
from ehrql.tables.raw.tpp import medications

from config import start_date_measure_med_counts, monthly_intervals_measure_med_counts
from codelists import (
pharmacy_first_consultation_codelist,
pharmacy_first_med_codelist,
)
from pf_variables_library import select_events

# Script taken from Pharmacy First Data Development (for top 10 PF meds table)

measures = create_measures()
measures.configure_dummy_data(population_size=1000)

start_date = start_date_measure_med_counts
monthly_intervals = monthly_intervals_measure_med_counts

registration = practice_registrations.for_patient_on(INTERVAL.end_date)

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

pharmacy_first_ids = pharmacy_first_events.consultation_id
has_pharmacy_first_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
).where(medications.consultation_id.is_in(pharmacy_first_ids))

# First medication for each patient
first_selected_medication = (
selected_medications.sort_by(selected_medications.date).first_for_patient().dmd_code
)
# Boolean variable that selected medication is part of pharmacy first med codelists
has_pharmacy_first_medication = first_selected_medication.is_in(pharmacy_first_med_codelist)

# Numerator, patients with a PF medication
# This allows me to count all (first) medications linked to a PF consultation
numerator = first_selected_medication.is_not_null()

# Denominator, registered patients (f/m) with a PF consultation
denominator = (
registration.exists_for_patient()
& patients.sex.is_in(["male", "female"])
& has_pharmacy_first_consultation
)

measures.define_measure(
name="pf_medication_count",
numerator = first_selected_medication.is_not_null(),
denominator=denominator,
group_by={
"dmd_code": first_selected_medication,
"pharmacy_first_med": has_pharmacy_first_medication,
},
intervals=months(monthly_intervals).starting_on(start_date),
)
64 changes: 56 additions & 8 deletions analysis/measures_definition_pf_descriptive_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from ehrql.tables.raw.tpp import medications
from ehrql.tables.tpp import practice_registrations, patients, clinical_events

from pf_variables_library import select_events_from_codelist, select_events_by_consultation_id
from pf_variables_library import select_events
from codelists import (
pharmacy_first_med_codelist,
pharmacy_first_consultation_codelist,
Expand All @@ -20,24 +20,42 @@
registration = practice_registrations.for_patient_on(INTERVAL.end_date)

# Function to retrieve consultation ids from clinical events that are PF consultations
pharmacy_first_ids = select_events_from_codelist(
clinical_events, pharmacy_first_consultation_codelist
pharmacy_first_ids = select_events(
clinical_events, codelist=pharmacy_first_consultation_codelist
).consultation_id

# Function to retrieve selected events using pharmacy first ids
selected_clinical_events = select_events_by_consultation_id(
clinical_events, pharmacy_first_ids
selected_clinical_events = select_events(
clinical_events, consultation_ids=pharmacy_first_ids
).where(clinical_events.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date))

selected_med_events = select_events_by_consultation_id(medications, pharmacy_first_ids).where(
selected_med_events = select_events(
medications, consultation_ids=pharmacy_first_ids).where(
medications.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date)
)

selected_clinical_pathways = select_events(
clinical_events, codelist=pharmacy_first_conditions_codelist
).where(clinical_events.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 = select_events_from_codelist(selected_clinical_events, pharmacy_first_consultation_codelist).exists_for_patient()
has_pf_consultation = select_events(
selected_clinical_events, codelist=pharmacy_first_consultation_codelist).exists_for_patient()

# PF consultations with PF clinical condition
has_pf_condition = select_events_from_codelist(selected_clinical_events, pharmacy_first_conditions_codelist).exists_for_patient()
has_pf_condition = select_events(
selected_clinical_events, codelist=pharmacy_first_conditions_codelist).exists_for_patient()

# Dates of pharmacy first consultations
pharmacy_first_dates = select_events(clinical_events, codelist=pharmacy_first_consultation_codelist).date
viv3ckj marked this conversation as resolved.
Show resolved Hide resolved

# Specify whether a patient has been prescribed a PF medication on the same day as a PF consultation code
has_pfmed_on_pfdate = selected_med_events.where(medications.date.is_in(pharmacy_first_dates)).exists_for_patient()
Copy link
Member

@milanwiedemann milanwiedemann Jan 7, 2025

Choose a reason for hiding this comment

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

selected_med_events has already been filtered to only include those events where the consultation_id matches a pharmacy_first_ids, see:

selected_med_events = select_events(
    medications, consultation_ids=pharmacy_first_ids).where(
    medications.date.is_on_or_between(INTERVAL.start_date, INTERVAL.end_date)
)

here we want to use the date as a backup to look at all pharmacy first medications on the same day as a pharmacy first consultation, specifically for those events where the linkage through consultation_id doesnt exist. so we need to provide a medications event frame that still includes all the pf medications

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good spot! Thanks, I've made the changes now.


# Specify whether a patient has a PF condition
has_pfpathway_on_pfdate = selected_clinical_pathways.where(
selected_clinical_pathways.date.is_in(pharmacy_first_dates)
).exists_for_patient()

# PF consultations with prescribed PF medication
has_pf_medication = selected_med_events.where(
Expand All @@ -52,6 +70,12 @@
)
measures.define_defaults(denominator=denominator)

# Denominator without the pf_consultation constraint
study_population = (
registration.exists_for_patient()
& patients.sex.is_in(["male", "female"])
)
viv3ckj marked this conversation as resolved.
Show resolved Hide resolved

# Measures for PF consultations with PF medication
measures.define_measure(
name="pf_with_pfmed",
Expand All @@ -71,3 +95,27 @@
numerator=has_pf_condition & has_pf_medication,
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for PF medications prescribed on the same day as PF consultation
measures.define_measure(
name="pfmed_on_pfdate",
numerator=has_pfmed_on_pfdate,
denominator=study_population,
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for PF conditions diagnosed on the same day as PF consultation
measures.define_measure(
name="pfpathway_on_pfdate",
numerator=has_pfpathway_on_pfdate,
denominator=study_population,
intervals=months(monthly_intervals).starting_on(start_date),
)

# Measures for PF conditions diagnosed and PF med prescribed on the same day as PF consultation
measures.define_measure(
name="pfmed_and_pfpathway_on_pfdate",
numerator=has_pfmed_on_pfdate & has_pfpathway_on_pfdate,
denominator=study_population,
intervals=months(monthly_intervals).starting_on(start_date),
)
39 changes: 39 additions & 0 deletions lib/functions/create_tables.R
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,42 @@ create_clinical_conditions_codes_table <- function(title) {
heading.subtitle.font.size = "small"
)
}

# Create top 5 table grouped by pharmacy_first_med status
# Data needs to have the following columns:
# pharmacy_first_med
# term
# count
# ratio_by_group
gt_top_meds <- function(data) {
data |>
gt(
groupname_col = "pharmacy_first_med",
rowname_col = "term"
) %>%
tab_header(
title = "Top 5 medications linked to Pharmacy First consultations",
subtitle = "Timeframe: 1st Feb 2024 to 31st July 2024"
) %>%
cols_label(
term = md("**Medication**"),
count = md("**Count**"),
ratio_by_group = md("**%**")
) %>%
fmt_number(
columns = count,
decimals = 0
) %>%
fmt_percent(
columns = ratio_by_group,
decimals = 1
) %>%
tab_style(
style = cell_text(weight = "bold"),
locations = cells_row_groups(groups = everything())
) %>%
tab_stub_indent(
rows = everything(),
indent = 3
)
}
36 changes: 36 additions & 0 deletions lib/functions/eps_erd_prescribing_data.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
library(tidyverse)
library(janitor)
library(here)
library(httr)

# Function to download and read the xlsx files
read_xlsx_from_url <- function(url_list, sheet = NULL, skip = NULL, ...) {
temp_file <- tempfile(fileext = ".xlsx")
GET(
url_list,
write_disk(temp_file, overwrite = TRUE)
)
readxl::read_xlsx(
temp_file,
col_names = TRUE,
.name_repair = janitor::make_clean_names,
sheet = sheet,
skip = skip,
...
)
}

df <- read_xlsx_from_url(
"https://github.com/user-attachments/files/17774058/EPS.and.eRD.Prescribing.Dashboard.July.2024.xlsx",
skip = 2,
sheet = "Historical Data"
)

df_filtered <- df %>%
select(month, region_code, practice_code, eps_items, erd_items) %>%
filter(month %in% c(202402, 202403, 202404, 202405, 202406, 202407)) %>%
mutate(month = ym(month))

df_filtered |> write_csv(
here("lib", "validation", "data", "eps_erd_prescribing_2024-02-01_to_2024-07-01.csv")
)
11 changes: 10 additions & 1 deletion project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,13 @@ actions:
--output output/population/pf_population.csv.gz
outputs:
highly_sensitive:
cohort: output/population/pf_population.csv.gz
cohort: output/population/pf_population.csv.gz

generate_pf_med_counts_measures:
run: >
ehrql:v1 generate-measures analysis/measures_definition_pf_consultation_med_counts.py
--dummy-tables dummy_tables
--output output/measures/consultation_med_counts_measures.csv
outputs:
moderately_sensitive:
measure: output/measures/consultation_med_counts_measures.csv
Loading