From 9f9cbc01ab9cba2b32e58dc94eaf92d7ee521f83 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:48:00 +0000 Subject: [PATCH 01/12] Add note on structure to README --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 7fa3368..8b28606 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,12 @@ An app to explore data for Types of Potentially-Mitigatable Activity (TPMAs). The app is [deployed to Posit Connect](https://connect.strategyunitwm.nhs.uk/tpma-explorer/) (login and permissions required). +## Structure + The app is made with [Shiny](https://shiny.posit.co/) and is an R package following [the nolem approach](https://github.com/StatsRhian/nolem). + +In `R/`: + +* Shiny modules (server and UI components) are stored in `mod_*.R` scripts +* functions to help prepare data are in `utils_*.R` scripts +* logic for user facing outputs (plots, tables) are in `fct_*.R` scripts From f3e48ee6efceb8b1826609771bd1d536a9502a47 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:50:06 +0000 Subject: [PATCH 02/12] Add module and support functions for age-sex pyramid --- R/fct_plots.R | 30 ++++++++++++++++++++++ R/mod_plot_age_sex_pyramid.R | 49 ++++++++++++++++++++++++++++++++++++ R/utils-data.R | 30 ++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 R/mod_plot_age_sex_pyramid.R create mode 100644 R/utils-data.R diff --git a/R/fct_plots.R b/R/fct_plots.R index bdbfcc6..9bfe814 100644 --- a/R/fct_plots.R +++ b/R/fct_plots.R @@ -120,3 +120,33 @@ theme_rates <- function(has_y_axis = TRUE) { theme } + +#' Plot Age-Sex Pyramid +#' @param age_data A data.frame. +#' @return A 'ggplot2' object. +#' @export +plot_age_sex_pyramid <- function(age_data) { + age_data |> + ggplot2::ggplot( + ggplot2::aes( + .data[["n"]], + .data[["age_group"]], + colour = .data[["sex"]], + fill = ggplot2::after_scale(ggplot2::alpha(.data[["colour"]], 0.4)) + ) + ) + + ggplot2::geom_col(position = "stack", width = 1, na.rm = TRUE) + + ggplot2::scale_colour_manual( + values = c("Males" = "#5881c1", "Females" = "#ec6555") + ) + + ggplot2::scale_x_continuous(labels = purrr::compose(scales::comma, abs)) + + ggplot2::scale_y_discrete(drop = FALSE) + + ggplot2::guides( + colour = ggplot2::guide_legend(NULL) + ) + + ggplot2::labs(x = NULL, y = NULL) + + ggplot2::theme( + legend.position = "bottom", + panel.background = ggplot2::element_blank() + ) +} diff --git a/R/mod_plot_age_sex_pyramid.R b/R/mod_plot_age_sex_pyramid.R new file mode 100644 index 0000000..a4b4012 --- /dev/null +++ b/R/mod_plot_age_sex_pyramid.R @@ -0,0 +1,49 @@ +#' Plot Age-Sex Pyramid Trend UI +#' @param id,input,output,session Internal parameters for `shiny`. +#' @noRd +mod_plot_age_sex_pyramid_ui <- function(id) { + ns <- shiny::NS(id) + bslib::card( + bslib::card_header("Age-sex pyramid"), + bslib::card_body(shiny::plotOutput(ns("age_sex_pyramid"))), + full_screen = TRUE + ) +} + +#' Plot Age-Sex Pyramid Server +#' @param id Internal parameter for `shiny`. +#' @param age_sex_data A data.frame. +#' @param selected_provider Character. Provider code, e.g. `"RCF"`. +#' @param selected_strategy Character. Strategy variable name, e.g. +#' `"alcohol_partially_attributable_acute"`. +#' @noRd +mod_plot_age_sex_pyramid_server <- function( + id, + age_sex_data, + selected_provider, + selected_strategy, + start_year +) { + shiny::moduleServer(id, function(input, output, session) { + output$age_sex_pyramid <- shiny::renderPlot({ + shiny::req(age_sex_data) + shiny::req(selected_provider()) + shiny::req(selected_strategy()) + shiny::req(start_year) + + age_data <- age_sex_data |> + dplyr::filter( + .data$provider == selected_provider(), + .data$strategy == selected_strategy(), + .data$fyear == .env$start_year + ) + + shiny::validate(shiny::need( + nrow(age_data) > 0, + "No data available for these selections." + )) + + plot_age_sex_pyramid(age_data) + }) + }) +} diff --git a/R/utils-data.R b/R/utils-data.R new file mode 100644 index 0000000..501daec --- /dev/null +++ b/R/utils-data.R @@ -0,0 +1,30 @@ +#' Prepare Age-Sex Data +#' @param age_sex_data A data.frame. +#' @return A data.frame. +#' @export +prepare_age_sex_data <- function(age_sex_data) { + age_fct <- age_sex_data[["age_group"]] |> unique() |> sort() + age_sex_data |> + dplyr::mutate( + age_group = factor( + .data[["age_group"]], + levels = .env[["age_fct"]] + ), + dplyr::across( + "sex", + \(value) { + forcats::fct_recode( + as.character(value), + "Males" = "1", + "Females" = "2" + ) + } + ), + dplyr::across( + "n", + \(value) { + dplyr::if_else(.data[["sex"]] == "Males", value * -1, value) + } + ) + ) +} From ac03b2c7bb80d7a1a54d352a1fef65facb30d84b Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:50:35 +0000 Subject: [PATCH 03/12] Integrate age-sex pyramid into UI and server --- R/app_server.R | 13 +++++++++++++ R/app_ui.R | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/R/app_server.R b/R/app_server.R index b9fc927..208add1 100644 --- a/R/app_server.R +++ b/R/app_server.R @@ -24,6 +24,12 @@ app_server <- function(input, output, session) { "diagnoses", data_version ) + age_sex_data <- azkit::read_azure_parquet( + inputs_container, + "age_sex", + data_version + ) |> + prepare_age_sex_data() # Lookups ---- providers_lookup <- jsonlite::read_json( @@ -90,4 +96,11 @@ app_server <- function(input, output, session) { selected_strategy, start_year ) + mod_plot_age_sex_pyramid_server( + "mod_plot_age_sex_pyramid", + age_sex_data, + selected_provider, + selected_strategy, + start_year + ) } diff --git a/R/app_ui.R b/R/app_ui.R index 2449731..1f8f96c 100644 --- a/R/app_ui.R +++ b/R/app_ui.R @@ -35,7 +35,8 @@ app_ui <- function(request) { width = 1 / 2, mod_table_procedures_ui("mod_table_procedures"), mod_table_diagnoses_ui("mod_table_diagnoses") - ) + ), + mod_plot_age_sex_pyramid_ui("mod_plot_age_sex_pyramid"), ) ) ) From b9cd334b8f84a09fced5e7824a4c3bdced78282c Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:11:46 +0000 Subject: [PATCH 04/12] Update pyramid fun docs, re-document --- NAMESPACE | 2 ++ R/fct_plots.R | 14 ++++++++------ R/mod_plot_age_sex_pyramid.R | 6 +++--- R/utils-data.R | 9 +++++++-- man/plot_age_sex_pyramid.Rd | 19 +++++++++++++++++++ man/plot_rates_funnel.Rd | 2 +- man/plot_rates_trend.Rd | 4 ++-- man/prepare_age_sex_data.Rd | 18 ++++++++++++++++++ 8 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 man/plot_age_sex_pyramid.Rd create mode 100644 man/prepare_age_sex_data.Rd diff --git a/NAMESPACE b/NAMESPACE index e1f2844..6f91e05 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,9 +6,11 @@ export(fetch_strategy_text) export(generate_rates_baseline_data) export(generate_rates_funnel_data) export(isolate_provider_peers) +export(plot_age_sex_pyramid) export(plot_rates_box) export(plot_rates_funnel) export(plot_rates_trend) +export(prepare_age_sex_data) export(prepare_diagnoses_data) export(prepare_procedures_data) export(run_app) diff --git a/R/fct_plots.R b/R/fct_plots.R index 9bfe814..0424ae4 100644 --- a/R/fct_plots.R +++ b/R/fct_plots.R @@ -3,8 +3,8 @@ #' `rate_col`. Pre-filtered for a given provider and strategy. One row per #' financial year. #' @param baseline_year Numeric. In the form `202324`. -#' @param x_axis_title Character. -#' @param y_axis_title Character. +#' @param x_axis_title Character. Title for the x-axis. +#' @param y_axis_title Character. Title for the y-axis. #' @param y_axis_limits Numeric vector. Min and max values for the y axis. #' @return A 'ggplot2' object. #' @export @@ -37,7 +37,7 @@ plot_rates_trend <- function( #' Plot Rates Funnel with Peers #' @param rates_funnel_data A data.frame. #' @param y_axis_limits Numeric vector. Min and max values for the y axis. -#' @param x_axis_title Character. +#' @param x_axis_title Character. Title for the x-axis. #' @return A 'ggplot2' object. #' @export plot_rates_funnel <- function(rates_funnel_data, y_axis_limits, x_axis_title) { @@ -122,11 +122,13 @@ theme_rates <- function(has_y_axis = TRUE) { } #' Plot Age-Sex Pyramid -#' @param age_data A data.frame. +#' @param age_sex_data A data.frame. Read from Azure and processed with +#' [prepare_age_sex_data]. Counts for each strategy split by provider, year, +#' age group and sex. #' @return A 'ggplot2' object. #' @export -plot_age_sex_pyramid <- function(age_data) { - age_data |> +plot_age_sex_pyramid <- function(age_sex_data) { + age_sex_data |> ggplot2::ggplot( ggplot2::aes( .data[["n"]], diff --git a/R/mod_plot_age_sex_pyramid.R b/R/mod_plot_age_sex_pyramid.R index a4b4012..af9d5b4 100644 --- a/R/mod_plot_age_sex_pyramid.R +++ b/R/mod_plot_age_sex_pyramid.R @@ -31,7 +31,7 @@ mod_plot_age_sex_pyramid_server <- function( shiny::req(selected_strategy()) shiny::req(start_year) - age_data <- age_sex_data |> + age_sex_filtered <- age_sex_data |> dplyr::filter( .data$provider == selected_provider(), .data$strategy == selected_strategy(), @@ -39,11 +39,11 @@ mod_plot_age_sex_pyramid_server <- function( ) shiny::validate(shiny::need( - nrow(age_data) > 0, + nrow(age_sex_filtered) > 0, "No data available for these selections." )) - plot_age_sex_pyramid(age_data) + plot_age_sex_pyramid(age_sex_filtered) }) }) } diff --git a/R/utils-data.R b/R/utils-data.R index 501daec..2410f40 100644 --- a/R/utils-data.R +++ b/R/utils-data.R @@ -1,5 +1,6 @@ #' Prepare Age-Sex Data -#' @param age_sex_data A data.frame. +#' @param age_sex_data A data.frame. Read from Azure. Counts for each strategy +#' split by provider, year, age group and sex. #' @return A data.frame. #' @export prepare_age_sex_data <- function(age_sex_data) { @@ -23,7 +24,11 @@ prepare_age_sex_data <- function(age_sex_data) { dplyr::across( "n", \(value) { - dplyr::if_else(.data[["sex"]] == "Males", value * -1, value) + dplyr::if_else( + .data[["sex"]] == "Males", + value * -1, # negative, to appear on the left of the pyramid + value # positive, to appear on the right of the pyramid + ) } ) ) diff --git a/man/plot_age_sex_pyramid.Rd b/man/plot_age_sex_pyramid.Rd new file mode 100644 index 0000000..493c6fb --- /dev/null +++ b/man/plot_age_sex_pyramid.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fct_plots.R +\name{plot_age_sex_pyramid} +\alias{plot_age_sex_pyramid} +\title{Plot Age-Sex Pyramid} +\usage{ +plot_age_sex_pyramid(age_sex_data) +} +\arguments{ +\item{age_sex_data}{A data.frame. Read from Azure and processed with +\link{prepare_age_sex_data}. Counts for each strategy split by provider, year, +age group and sex.} +} +\value{ +A 'ggplot2' object. +} +\description{ +Plot Age-Sex Pyramid +} diff --git a/man/plot_rates_funnel.Rd b/man/plot_rates_funnel.Rd index d3c118e..09b212a 100644 --- a/man/plot_rates_funnel.Rd +++ b/man/plot_rates_funnel.Rd @@ -11,7 +11,7 @@ plot_rates_funnel(rates_funnel_data, y_axis_limits, x_axis_title) \item{y_axis_limits}{Numeric vector. Min and max values for the y axis.} -\item{x_axis_title}{Character.} +\item{x_axis_title}{Character. Title for the x-axis.} } \value{ A 'ggplot2' object. diff --git a/man/plot_rates_trend.Rd b/man/plot_rates_trend.Rd index 922d2d5..0bc18c6 100644 --- a/man/plot_rates_trend.Rd +++ b/man/plot_rates_trend.Rd @@ -19,9 +19,9 @@ financial year.} \item{baseline_year}{Numeric. In the form \code{202324}.} -\item{x_axis_title}{Character.} +\item{x_axis_title}{Character. Title for the x-axis.} -\item{y_axis_title}{Character.} +\item{y_axis_title}{Character. Title for the y-axis.} \item{y_axis_limits}{Numeric vector. Min and max values for the y axis.} } diff --git a/man/prepare_age_sex_data.Rd b/man/prepare_age_sex_data.Rd new file mode 100644 index 0000000..1212b3d --- /dev/null +++ b/man/prepare_age_sex_data.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils-data.R +\name{prepare_age_sex_data} +\alias{prepare_age_sex_data} +\title{Prepare Age-Sex Data} +\usage{ +prepare_age_sex_data(age_sex_data) +} +\arguments{ +\item{age_sex_data}{A data.frame. Read from Azure. Counts for each strategy +split by provider, year, age group and sex.} +} +\value{ +A data.frame. +} +\description{ +Prepare Age-Sex Data +} From 0f28425993529744e3716eee3c98e5e8bec56403 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:29:54 +0000 Subject: [PATCH 05/12] Improve plot function docs, re-document --- R/fct_plots.R | 28 ++++++++++++++++------------ R/utils_plot.R | 6 +++--- man/generate_rates_baseline_data.Rd | 2 +- man/generate_rates_funnel_data.Rd | 4 ++-- man/plot_age_sex_pyramid.Rd | 6 +++--- man/plot_rates_box.Rd | 6 ++++-- man/plot_rates_funnel.Rd | 5 ++++- man/plot_rates_trend.Rd | 7 +++---- 8 files changed, 36 insertions(+), 28 deletions(-) diff --git a/R/fct_plots.R b/R/fct_plots.R index 0424ae4..870385d 100644 --- a/R/fct_plots.R +++ b/R/fct_plots.R @@ -1,7 +1,6 @@ #' Plot Rates Trend Over Time -#' @param rates_df A data.frame. Must contain columns given by `fyear_col` and -#' `rate_col`. Pre-filtered for a given provider and strategy. One row per -#' financial year. +#' @param rates_trend_data A data.frame. Rates data read in from Azure, filtered +#' for a given provider and strategy, and arranged by year. #' @param baseline_year Numeric. In the form `202324`. #' @param x_axis_title Character. Title for the x-axis. #' @param y_axis_title Character. Title for the y-axis. @@ -9,13 +8,13 @@ #' @return A 'ggplot2' object. #' @export plot_rates_trend <- function( - rates_df, + rates_trend_data, baseline_year = 202324, x_axis_title = "Financial year", y_axis_title = "Rate", y_axis_limits ) { - rates_df |> + rates_trend_data |> ggplot2::ggplot( ggplot2::aes(as.factor(.data[["fyear"]]), .data[["rate"]], group = 1) ) + @@ -35,7 +34,10 @@ plot_rates_trend <- function( } #' Plot Rates Funnel with Peers -#' @param rates_funnel_data A data.frame. +#' @param rates_funnel_data A data.frame. Rates data read in from Azure and +#' processed with [generate_rates_baseline_data] to filter for provider, +#' strategy and year, followed by [generate_rates_funnel_data] to generate +#' values for funnel structure. #' @param y_axis_limits Numeric vector. Min and max values for the y axis. #' @param x_axis_title Character. Title for the x-axis. #' @return A 'ggplot2' object. @@ -75,12 +77,14 @@ plot_rates_funnel <- function(rates_funnel_data, y_axis_limits, x_axis_title) { } #' Plot Rates Boxplot with Peers -#' @param trend_data A data.frame. +#' @param rates_box_data A data.frame. Rates data read in from Azure and +#' processed with [generate_rates_baseline_data] to filter for provider, +#' strategy and year. #' @param y_axis_limits Numeric vector. Min and max values for the y axis. #' @return A 'ggplot2' object. #' @export -plot_rates_box <- function(trend_data, y_axis_limits) { - trend_data |> +plot_rates_box <- function(rates_box_data, y_axis_limits) { + rates_box_data |> ggplot2::ggplot(ggplot2::aes(x = "", y = .data$rate)) + ggplot2::geom_boxplot(alpha = 0.2, outlier.shape = NA) + ggbeeswarm::geom_quasirandom(ggplot2::aes(colour = .data$is_peer)) + @@ -122,9 +126,9 @@ theme_rates <- function(has_y_axis = TRUE) { } #' Plot Age-Sex Pyramid -#' @param age_sex_data A data.frame. Read from Azure and processed with -#' [prepare_age_sex_data]. Counts for each strategy split by provider, year, -#' age group and sex. +#' @param age_sex_data A data.frame. Age-sex data read from Azure and processed +#' with [prepare_age_sex_data]. Counts for each strategy split by provider, +#' year, age group and sex. #' @return A 'ggplot2' object. #' @export plot_age_sex_pyramid <- function(age_sex_data) { diff --git a/R/utils_plot.R b/R/utils_plot.R index ac2991a..fa81819 100644 --- a/R/utils_plot.R +++ b/R/utils_plot.R @@ -12,7 +12,7 @@ isolate_provider_peers <- function(provider, peers) { } #' Generate Rates Baseline Data -#' @param rates A data.frame. +#' @param rates A data.frame. Rates data read from Azure. #' @param provider Character. Provider code, e.g. `"RCF"`. #' @param peers Character. A vector of peers for given `provider`. #' @param strategy Character. Strategy variable name, e.g. @@ -44,8 +44,8 @@ generate_rates_baseline_data <- function( } #' Generate Data for the Funnel Plot -#' @param rates_baseline_data A data.frame. Output created by -#' [generate_rates_baseline_data]. +#' @param rates_baseline_data A data.frame. Rates data read in from Azure and +#' processed [generate_rates_baseline_data]. #' @return A data.frame. #' @export generate_rates_funnel_data <- function(rates_baseline_data) { diff --git a/man/generate_rates_baseline_data.Rd b/man/generate_rates_baseline_data.Rd index 5407ad4..944ccb5 100644 --- a/man/generate_rates_baseline_data.Rd +++ b/man/generate_rates_baseline_data.Rd @@ -7,7 +7,7 @@ generate_rates_baseline_data(rates, provider, peers, strategy, start_year) } \arguments{ -\item{rates}{A data.frame.} +\item{rates}{A data.frame. Rates data read from Azure.} \item{provider}{Character. Provider code, e.g. \code{"RCF"}.} diff --git a/man/generate_rates_funnel_data.Rd b/man/generate_rates_funnel_data.Rd index 19fecfd..da4874e 100644 --- a/man/generate_rates_funnel_data.Rd +++ b/man/generate_rates_funnel_data.Rd @@ -7,8 +7,8 @@ generate_rates_funnel_data(rates_baseline_data) } \arguments{ -\item{rates_baseline_data}{A data.frame. Output created by -\link{generate_rates_baseline_data}.} +\item{rates_baseline_data}{A data.frame. Rates data read in from Azure and +processed \link{generate_rates_baseline_data}.} } \value{ A data.frame. diff --git a/man/plot_age_sex_pyramid.Rd b/man/plot_age_sex_pyramid.Rd index 493c6fb..d479c20 100644 --- a/man/plot_age_sex_pyramid.Rd +++ b/man/plot_age_sex_pyramid.Rd @@ -7,9 +7,9 @@ plot_age_sex_pyramid(age_sex_data) } \arguments{ -\item{age_sex_data}{A data.frame. Read from Azure and processed with -\link{prepare_age_sex_data}. Counts for each strategy split by provider, year, -age group and sex.} +\item{age_sex_data}{A data.frame. Age-sex data read from Azure and processed +with \link{prepare_age_sex_data}. Counts for each strategy split by provider, +year, age group and sex.} } \value{ A 'ggplot2' object. diff --git a/man/plot_rates_box.Rd b/man/plot_rates_box.Rd index e80559f..74167e7 100644 --- a/man/plot_rates_box.Rd +++ b/man/plot_rates_box.Rd @@ -4,10 +4,12 @@ \alias{plot_rates_box} \title{Plot Rates Boxplot with Peers} \usage{ -plot_rates_box(trend_data, y_axis_limits) +plot_rates_box(rates_box_data, y_axis_limits) } \arguments{ -\item{trend_data}{A data.frame.} +\item{rates_box_data}{A data.frame. Rates data read in from Azure and +processed with \link{generate_rates_baseline_data} to filter for provider, +strategy and year.} \item{y_axis_limits}{Numeric vector. Min and max values for the y axis.} } diff --git a/man/plot_rates_funnel.Rd b/man/plot_rates_funnel.Rd index 09b212a..80495e1 100644 --- a/man/plot_rates_funnel.Rd +++ b/man/plot_rates_funnel.Rd @@ -7,7 +7,10 @@ plot_rates_funnel(rates_funnel_data, y_axis_limits, x_axis_title) } \arguments{ -\item{rates_funnel_data}{A data.frame.} +\item{rates_funnel_data}{A data.frame. Rates data read in from Azure and +processed with \link{generate_rates_baseline_data} to filter for provider, +strategy and year, followed by \link{generate_rates_funnel_data} to generate +values for funnel structure.} \item{y_axis_limits}{Numeric vector. Min and max values for the y axis.} diff --git a/man/plot_rates_trend.Rd b/man/plot_rates_trend.Rd index 0bc18c6..b112065 100644 --- a/man/plot_rates_trend.Rd +++ b/man/plot_rates_trend.Rd @@ -5,7 +5,7 @@ \title{Plot Rates Trend Over Time} \usage{ plot_rates_trend( - rates_df, + rates_trend_data, baseline_year = 202324, x_axis_title = "Financial year", y_axis_title = "Rate", @@ -13,9 +13,8 @@ plot_rates_trend( ) } \arguments{ -\item{rates_df}{A data.frame. Must contain columns given by \code{fyear_col} and -\code{rate_col}. Pre-filtered for a given provider and strategy. One row per -financial year.} +\item{rates_trend_data}{A data.frame. Rates data read in from Azure, filtered +for a given provider and strategy, and arranged by year.} \item{baseline_year}{Numeric. In the form \code{202324}.} From 3324e828588b4aa061e5ccd763090314d8a8a589 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:43:45 +0000 Subject: [PATCH 06/12] Improve table function docs --- R/fct_tables.R | 6 +++--- R/mod_table_diagnoses.R | 10 +++++----- R/mod_table_procedures.R | 10 +++++----- R/utils_tables.R | 16 ++++++++-------- man/entable_encounters.Rd | 6 +++--- man/prepare_diagnoses_data.Rd | 6 +++--- man/prepare_procedures_data.Rd | 8 ++++---- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/R/fct_tables.R b/R/fct_tables.R index 8f94c56..dd19de0 100644 --- a/R/fct_tables.R +++ b/R/fct_tables.R @@ -1,7 +1,7 @@ #' Create 'gt' Summary Table of Procedures or Diagnoses -#' @param encounters_prepared A data.frame. Data for procedures or diagnoses -#' ('encounters') that's been processed with [prepare_procedures_data] or -#' [prepare_diagnoses_data]. +#' @param encounters_prepared A data.frame. Procedures or diagnoses +#' ('encounters') data read from Azure and processed with +#' [prepare_procedures_data] or [prepare_diagnoses_data]. #' @return A 'gt' table. #' @export entable_encounters <- function(encounters_prepared) { diff --git a/R/mod_table_diagnoses.R b/R/mod_table_diagnoses.R index 5127051..212ec9c 100644 --- a/R/mod_table_diagnoses.R +++ b/R/mod_table_diagnoses.R @@ -13,8 +13,8 @@ mod_table_diagnoses_ui <- function(id) { #' Diagnoses Table Server #' @param id Internal parameter for `shiny`. -#' @param diagnoses A data.frame. Annual diagnosis counts by provider and -#' strategy. +#' @param diagnoses_data A data.frame. Diagnosis data read in from Azure. Annual +#' diagnosis counts by provider and strategy. #' @param diagnosis_lookup A data.frame. Type, code and description for #' diagnoses. #' @param selected_provider Character. Provider code, e.g. `"RCF"`. @@ -24,7 +24,7 @@ mod_table_diagnoses_ui <- function(id) { #' @noRd mod_table_diagnoses_server <- function( id, - diagnoses, + diagnoses_data, diagnoses_lookup, selected_provider, selected_strategy, @@ -32,13 +32,13 @@ mod_table_diagnoses_server <- function( ) { shiny::moduleServer(id, function(input, output, session) { diagnoses_prepared <- shiny::reactive({ - shiny::req(diagnoses) + shiny::req(diagnoses_data) shiny::req(selected_provider()) shiny::req(selected_strategy()) shiny::req(start_year) prepare_diagnoses_data( - diagnoses, + diagnoses_data, diagnoses_lookup, selected_provider(), selected_strategy(), diff --git a/R/mod_table_procedures.R b/R/mod_table_procedures.R index 29baa03..19e30d2 100644 --- a/R/mod_table_procedures.R +++ b/R/mod_table_procedures.R @@ -13,8 +13,8 @@ mod_table_procedures_ui <- function(id) { #' Procedures Table Server #' @param id Internal parameter for `shiny`. -#' @param procedures A data.frame. Annual procedure counts by provider and -#' strategy. +#' @param procedures_data A data.frame. Procedure data read in from Azure. +#' Annual procedure counts by provider and strategy. #' @param procedures_lookup A data.frame. Type, code and description for #' procedures. #' @param selected_provider Character. Provider code, e.g. `"RCF"`. @@ -24,7 +24,7 @@ mod_table_procedures_ui <- function(id) { #' @noRd mod_table_procedures_server <- function( id, - procedures, + procedures_data, procedures_lookup, selected_provider, selected_strategy, @@ -32,13 +32,13 @@ mod_table_procedures_server <- function( ) { shiny::moduleServer(id, function(input, output, session) { procedures_prepared <- shiny::reactive({ - shiny::req(procedures) + shiny::req(procedures_data) shiny::req(selected_provider()) shiny::req(selected_strategy()) shiny::req(start_year) prepare_procedures_data( - procedures, + procedures_data, procedures_lookup, selected_provider(), selected_strategy(), diff --git a/R/utils_tables.R b/R/utils_tables.R index 7ba072b..fd5ba07 100644 --- a/R/utils_tables.R +++ b/R/utils_tables.R @@ -1,6 +1,6 @@ #' Prepare Data for Procedures Table -#' @param procedures A data.frame. Annual procedure counts by provider and -#' strategy. +#' @param procedures A data.frame. Procedure data read in from Azure. Annual +#' procedure counts by provider and strategy. #' @param procedures_lookup A data.frame. Type, code and description for #' procedures. #' @param provider Character. Provider code, e.g. `"RCF"`. @@ -10,13 +10,13 @@ #' @return A data.frame. #' @export prepare_procedures_data <- function( - procedures, + procedures_data, procedures_lookup, provider, strategy, start_year ) { - procedures_prepared <- procedures |> + procedures_prepared <- procedures_data |> dplyr::filter( .data$provider == .env$provider, .data$strategy == .env$strategy, @@ -47,8 +47,8 @@ prepare_procedures_data <- function( } #' Prepare Data for Diagnoses Table -#' @param diagnoses A data.frame. Annual diagnosis counts by provider and -#' strategy. +#' @param diagnoses_data A data.frame. Diagnosis data read in from Azure. Annual +#' diagnosis counts by provider and strategy. #' @param diagnoses_lookup A data.frame. Type, code and description for #' diagnoses #' @param provider Character. Provider code, e.g. `"RCF"`. @@ -58,13 +58,13 @@ prepare_procedures_data <- function( #' @return A data.frame. #' @export prepare_diagnoses_data <- function( - diagnoses, + diagnoses_data, diagnoses_lookup, provider, strategy, start_year ) { - diagnoses_prepared <- diagnoses |> + diagnoses_prepared <- diagnoses_data |> dplyr::filter( .data$provider == .env$provider, .data$strategy == .env$strategy, diff --git a/man/entable_encounters.Rd b/man/entable_encounters.Rd index ad0a78b..b6435bd 100644 --- a/man/entable_encounters.Rd +++ b/man/entable_encounters.Rd @@ -7,9 +7,9 @@ entable_encounters(encounters_prepared) } \arguments{ -\item{encounters_prepared}{A data.frame. Data for procedures or diagnoses -('encounters') that's been processed with \link{prepare_procedures_data} or -\link{prepare_diagnoses_data}.} +\item{encounters_prepared}{A data.frame. Procedures or diagnoses +('encounters') data read from Azure and processed with +\link{prepare_procedures_data} or \link{prepare_diagnoses_data}.} } \value{ A 'gt' table. diff --git a/man/prepare_diagnoses_data.Rd b/man/prepare_diagnoses_data.Rd index 4abad69..7a77948 100644 --- a/man/prepare_diagnoses_data.Rd +++ b/man/prepare_diagnoses_data.Rd @@ -5,7 +5,7 @@ \title{Prepare Data for Diagnoses Table} \usage{ prepare_diagnoses_data( - diagnoses, + diagnoses_data, diagnoses_lookup, provider, strategy, @@ -13,8 +13,8 @@ prepare_diagnoses_data( ) } \arguments{ -\item{diagnoses}{A data.frame. Annual diagnosis counts by provider and -strategy.} +\item{diagnoses_data}{A data.frame. Diagnosis data read in from Azure. Annual +diagnosis counts by provider and strategy.} \item{diagnoses_lookup}{A data.frame. Type, code and description for diagnoses} diff --git a/man/prepare_procedures_data.Rd b/man/prepare_procedures_data.Rd index 8318faa..d23e454 100644 --- a/man/prepare_procedures_data.Rd +++ b/man/prepare_procedures_data.Rd @@ -5,7 +5,7 @@ \title{Prepare Data for Procedures Table} \usage{ prepare_procedures_data( - procedures, + procedures_data, procedures_lookup, provider, strategy, @@ -13,9 +13,6 @@ prepare_procedures_data( ) } \arguments{ -\item{procedures}{A data.frame. Annual procedure counts by provider and -strategy.} - \item{procedures_lookup}{A data.frame. Type, code and description for procedures.} @@ -25,6 +22,9 @@ procedures.} \code{"alcohol_partially_attributable_acute"}.} \item{start_year}{Integer. Baseline year in the form \code{202324}.} + +\item{procedures}{A data.frame. Procedure data read in from Azure. Annual +procedure counts by provider and strategy.} } \value{ A data.frame. From 6ee8ff0852df0d0d3857900b6b543afb7592fef6 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:44:11 +0000 Subject: [PATCH 07/12] Add note about running the app to the README --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8b28606..16c0b20 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,15 @@ An app to explore data for Types of Potentially-Mitigatable Activity (TPMAs). The app is [deployed to Posit Connect](https://connect.strategyunitwm.nhs.uk/tpma-explorer/) (login and permissions required). -## Structure +## For developers -The app is made with [Shiny](https://shiny.posit.co/) and is an R package following [the nolem approach](https://github.com/StatsRhian/nolem). +To run the app, you must: -In `R/`: +* create an `.Renviron file from the `.Renviron.example` template +* run `app.R` to launch the app locally for development purposes +* run `deploy.R` to deploy the app to Posit Connect when ready + +The app is made with [Shiny](https://shiny.posit.co/) and is an R package following [the nolem approach](https://github.com/StatsRhian/nolem). In `R/`: * Shiny modules (server and UI components) are stored in `mod_*.R` scripts * functions to help prepare data are in `utils_*.R` scripts From 77aef8b6d72e207b6c9a6b5751527525d095a584 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:44:20 +0000 Subject: [PATCH 08/12] Add a deploy script --- deploy.R | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 deploy.R diff --git a/deploy.R b/deploy.R new file mode 100644 index 0000000..505d552 --- /dev/null +++ b/deploy.R @@ -0,0 +1,15 @@ +rsconnect::deployApp( + appName = "tpma-explorer", + appTitle = "Explore TPMA data", + server = "connect.strategyunitwm.nhs.uk", + appId = 200, + appFiles = c( + "R/", + "inst/", + "NAMESPACE", + "DESCRIPTION", + "app.R" + ), + lint = FALSE, + forceUpdate = TRUE +) From 64fc85cd4c77e4460935fcb940f413a3512ba9ef Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:55:11 +0000 Subject: [PATCH 09/12] Appease check --- .Rbuildignore | 3 ++- DESCRIPTION | 11 +++++++---- R/utils_tables.R | 4 ++-- man/prepare_procedures_data.Rd | 6 +++--- man/tpma.explorer-package.Rd | 5 +++++ 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index eeb3025..7bf7e41 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -4,8 +4,9 @@ ^\.vscode$ ^app\.R$ ^CODEOWNERS$ -^tpma-explorer\.Rproj$ +^deploy\.R$ ^dev$ ^LICENSE\.md$ ^README\.md$ ^rsconnect$ +^tpma-explorer\.Rproj$ diff --git a/DESCRIPTION b/DESCRIPTION index a4b9034..4b7c48e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,10 +1,12 @@ Package: tpma.explorer Title: Explore TPMA Data Version: 0.1.0.9000 -Authors@R: - person("Matt", "Dray", , "matt.dray@nhs.net", role = c("aut", "cre")) -Description: A Shiny-app-in-a-package to explore data related to - Types of Potentially Mitigatable Activity (TPMAs). +Authors@R: c( + person("Matt", "Dray", , "matt.dray@nhs.net", role = c("aut", "cre")), + person("Tom", "Jemmett", , "thomas.jemmett@nhs.net", role = "aut") + ) +Description: A Shiny-app-in-a-package to explore data related to Types of + Potentially Mitigatable Activity (TPMAs). License: MIT + file LICENSE URL: https://github.com/The-Strategy-Unit/tpma-explorer BugReports: https://github.com/The-Strategy-Unit/tpma-explorer/issues @@ -15,6 +17,7 @@ Imports: bsicons, bslib, dplyr, + forcats, ggbeeswarm, ggplot2, ggrepel, diff --git a/R/utils_tables.R b/R/utils_tables.R index fd5ba07..7e5f56c 100644 --- a/R/utils_tables.R +++ b/R/utils_tables.R @@ -1,6 +1,6 @@ #' Prepare Data for Procedures Table -#' @param procedures A data.frame. Procedure data read in from Azure. Annual -#' procedure counts by provider and strategy. +#' @param procedures_data A data.frame. Procedure data read in from Azure. +#' Annual procedure counts by provider and strategy. #' @param procedures_lookup A data.frame. Type, code and description for #' procedures. #' @param provider Character. Provider code, e.g. `"RCF"`. diff --git a/man/prepare_procedures_data.Rd b/man/prepare_procedures_data.Rd index d23e454..175065d 100644 --- a/man/prepare_procedures_data.Rd +++ b/man/prepare_procedures_data.Rd @@ -13,6 +13,9 @@ prepare_procedures_data( ) } \arguments{ +\item{procedures_data}{A data.frame. Procedure data read in from Azure. +Annual procedure counts by provider and strategy.} + \item{procedures_lookup}{A data.frame. Type, code and description for procedures.} @@ -22,9 +25,6 @@ procedures.} \code{"alcohol_partially_attributable_acute"}.} \item{start_year}{Integer. Baseline year in the form \code{202324}.} - -\item{procedures}{A data.frame. Procedure data read in from Azure. Annual -procedure counts by provider and strategy.} } \value{ A data.frame. diff --git a/man/tpma.explorer-package.Rd b/man/tpma.explorer-package.Rd index 491f73e..5196a8c 100644 --- a/man/tpma.explorer-package.Rd +++ b/man/tpma.explorer-package.Rd @@ -19,5 +19,10 @@ Useful links: \author{ \strong{Maintainer}: Matt Dray \email{matt.dray@nhs.net} +Authors: +\itemize{ + \item Tom Jemmett \email{thomas.jemmett@nhs.net} +} + } \keyword{internal} From 92feeeff2ca19b3b4ab8686440aae29a286e4aa2 Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:59:28 +0000 Subject: [PATCH 10/12] Put app and deplot in dev/ --- README.md | 6 +++--- app.R | 2 -- deploy.R | 15 --------------- dev/deploy.R | 6 +----- 4 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 app.R delete mode 100644 deploy.R diff --git a/README.md b/README.md index 16c0b20..d0eee7b 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ The app is [deployed to Posit Connect](https://connect.strategyunitwm.nhs.uk/tpm To run the app, you must: -* create an `.Renviron file from the `.Renviron.example` template -* run `app.R` to launch the app locally for development purposes -* run `deploy.R` to deploy the app to Posit Connect when ready +* create an `.Renviron` file from the `.Renviron.example` template +* run `dev/app.R` to launch the app locally for development purposes +* run `dev/deploy.R` to deploy the app to Posit Connect when ready The app is made with [Shiny](https://shiny.posit.co/) and is an R package following [the nolem approach](https://github.com/StatsRhian/nolem). In `R/`: diff --git a/app.R b/app.R deleted file mode 100644 index 7bfdf0a..0000000 --- a/app.R +++ /dev/null @@ -1,2 +0,0 @@ -pkgload::load_all(".") -run_app() diff --git a/deploy.R b/deploy.R deleted file mode 100644 index 505d552..0000000 --- a/deploy.R +++ /dev/null @@ -1,15 +0,0 @@ -rsconnect::deployApp( - appName = "tpma-explorer", - appTitle = "Explore TPMA data", - server = "connect.strategyunitwm.nhs.uk", - appId = 200, - appFiles = c( - "R/", - "inst/", - "NAMESPACE", - "DESCRIPTION", - "app.R" - ), - lint = FALSE, - forceUpdate = TRUE -) diff --git a/dev/deploy.R b/dev/deploy.R index 90d5a0b..505d552 100644 --- a/dev/deploy.R +++ b/dev/deploy.R @@ -1,6 +1,6 @@ rsconnect::deployApp( appName = "tpma-explorer", - appTitle = "TPMA explorer", + appTitle = "Explore TPMA data", server = "connect.strategyunitwm.nhs.uk", appId = 200, appFiles = c( @@ -10,10 +10,6 @@ rsconnect::deployApp( "DESCRIPTION", "app.R" ), - envVars = c( - "AZ_CONTAINER_INPUTS", - "AZ_STORAGE_EP" - ), lint = FALSE, forceUpdate = TRUE ) From a82ea02dd98e74cce08618225c2e8971e4d118fd Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:09:02 +0000 Subject: [PATCH 11/12] Revert app.R to root --- app.R | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 app.R diff --git a/app.R b/app.R new file mode 100644 index 0000000..7bfdf0a --- /dev/null +++ b/app.R @@ -0,0 +1,2 @@ +pkgload::load_all(".") +run_app() From 4c69a932da2019cb0dd2082259dbdfa0e6cda02f Mon Sep 17 00:00:00 2001 From: Matt Dray <18232097+matt-dray@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:16:12 +0000 Subject: [PATCH 12/12] Appease lintr --- R/mod_plot_age_sex_pyramid.R | 2 ++ R/utils-data.R | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/R/mod_plot_age_sex_pyramid.R b/R/mod_plot_age_sex_pyramid.R index af9d5b4..b86acbe 100644 --- a/R/mod_plot_age_sex_pyramid.R +++ b/R/mod_plot_age_sex_pyramid.R @@ -17,7 +17,9 @@ mod_plot_age_sex_pyramid_ui <- function(id) { #' @param selected_strategy Character. Strategy variable name, e.g. #' `"alcohol_partially_attributable_acute"`. #' @noRd +# nolint start: object_length_linter. mod_plot_age_sex_pyramid_server <- function( + # nolint end id, age_sex_data, selected_provider, diff --git a/R/utils-data.R b/R/utils-data.R index 2410f40..114dd58 100644 --- a/R/utils-data.R +++ b/R/utils-data.R @@ -4,7 +4,10 @@ #' @return A data.frame. #' @export prepare_age_sex_data <- function(age_sex_data) { - age_fct <- age_sex_data[["age_group"]] |> unique() |> sort() + age_fct <- age_sex_data[["age_group"]] |> # nolint: object_usage_linter. + unique() |> + sort() + age_sex_data |> dplyr::mutate( age_group = factor(