From e3229a56ae8a8e081f6b17f2d46b22d4cdf9355d Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Mon, 27 Apr 2026 16:12:14 +0100 Subject: [PATCH 01/14] Create/use metadata for stochastic folder --- R/stochastic_files.R | 46 ++++++++++++++++++++++++++++++ inst/app/utils.R | 68 +++++++++++++++++++------------------------- 2 files changed, 76 insertions(+), 38 deletions(-) diff --git a/R/stochastic_files.R b/R/stochastic_files.R index 70cd9bd..31c13c6 100644 --- a/R/stochastic_files.R +++ b/R/stochastic_files.R @@ -257,3 +257,49 @@ stone_stochastic_central <- function(base, touchstone, disease, group, outfile <- sprintf("%s_%s_central.pq", group, scenario) arrow::write_parquet(central, file.path(path, outfile)) } + + + +stone_stochastic_make_meta <- function(path) { + + explore_files <- function(touchstone, folder, disease, group) { + files <- list.files(file.path(path, touchstone, folder)) + first <- file.path(path, touchstone, folder, files[1]) + ds <- arrow::open_dataset(first) + outcomes <- ds$schema$names + outcomes <- outcomes[tolower(outcomes) %in% + c("cases", "deaths", "dalys", "yll")] + + files <- strsplit(list.files(file.path(path, touchstone, folder)), "_") + + scenarios <- unique(unlist(lapply(files, `[[`, 2))) + df <- data.frame() + for (scenario in scenarios) { + matches <- files[unlist(lapply(files, `[[`, 2)) == scenario] + countries <- unique(unlist(lapply(matches, `[[`, 3))) + countries <- gsub(".pq", "", countries) + df <- rbind(df, data.frame( + touchstone = touchstone, + disease = disease, + group = group, + scenario = scenario, + countries = paste0(countries, collapse = ";"), + outcomes = paste0(outcomes, collapse = ";") + )) + } + df + } + + touchstone_meta <- function(touchstone) { + entries <- list.files(file.path(path, touchstone)) + data.table::rbindlist(lapply(entries, function(x) { + xs <- strsplit(x, "_")[[1]] + explore_files(touchstone, x, xs[1], xs[2]) + })) + } + + touchstones <- basename(list.dirs(paste0(path, "/"), recursive = FALSE)) + res <- data.table::rbindlist(lapply(touchstones, touchstone_meta)) + write.csv(res, file.path(path, "meta.csv"), + row.names = FALSE, quote = FALSE) +} diff --git a/inst/app/utils.R b/inst/app/utils.R index daaa9cc..26ce773 100644 --- a/inst/app/utils.R +++ b/inst/app/utils.R @@ -7,42 +7,35 @@ # Functions ending in _mg will find things common # between to modelling groups, for comparisons. +meta <- read.csv(file.path(data_dir, "meta.csv")) + get_touchstones <- function() { - sort(unique(basename(list.dirs(data_dir, recursive = FALSE)))) + sort(unique(meta$touchstone), decreasing = TRUE) } -get_diseases <- function(touchstone1, touchstone2) { - lookup <- function(touchstone, res = NULL) { - if (is.null(touchstone)) return(res) - path <- file.path(data_dir, touchstone) - dirs <- basename(list.dirs(path, recursive = FALSE)) - unique(unlist(lapply(strsplit(dirs, "_"), `[[`, 1))) +get_diseases <- function(touchstone1, touchstone2 = NULL) { + res <- meta$disease[meta$touchstone %in% touchstone1] + if (!is.null(touchstone2)) { + res <- intersect(res, meta$disease[meta$touchstone %in% touchstone2]) } - res <- lookup(touchstone1) - sort(unique(res[res %in% lookup(touchstone2, res)])) + sort(unique(res)) } get_groups <- function(touchstone1, touchstone2, disease) { - lookup <- function(touchstone, disease, res = NULL) { - if (is.null(touchstone)) return(res) - path <- file.path(data_dir, touchstone) - dirs <- basename(list.dirs(path, recursive = FALSE)) - dirs <- dirs[substr(dirs, 1, nchar(disease) + 1) == paste0(disease, "_")] - unique(substring(dirs, nchar(disease) + 2)) + m <- meta[meta$disease %in% disease, ] + res <- m$group[m$touchstone %in% touchstone1] + if (!is.null(touchstone2)) { + res <- intersect(res, m$group[m$touchstone %in% touchstone2]) } - res <- lookup(touchstone1, disease) - sort(unique(res[res %in% lookup(touchstone2, disease, res)])) + sort(unique(res)) } get_scenarios <- function(touchstone1, touchstone2, disease, group1, group2) { lookup <- function(touchstone, disease, group, res = NULL) { if ((is.null(touchstone)) || (is.null(group))) return(res) - path <- file.path(data_dir, touchstone, paste(disease, group, sep = "_")) - files <- basename(list.files(path, recursive = FALSE)) - files <- gsub(".pq", "", files) - files <- substring(files, nchar(group) + 2) - ends <- unlist(lapply(gregexpr("_", files), `[[`, 1)) - 1 - unique(substring(files, 1, ends)) + sort(unique(meta$scenario[(meta$touchstone %in% touchstone) & + (meta$disease %in% disease) & + (meta$group %in% group)])) } res <- lookup(touchstone1, disease, group1) @@ -57,14 +50,11 @@ get_countries <- function(touchstone1, touchstone2, disease, group1, group2, if ((is.null(touchstone)) || (is.null(group)) || (is.null(scenario))) { return(res) } - - path <- file.path(data_dir, touchstone, paste(disease, group, sep = "_")) - files <- basename(list.files(path, recursive = FALSE)) - files <- gsub(".pq", "", files) - files <- substring(files, nchar(group) + 2) - files <- files[substring(files, 1, nchar(scenario) + 1) == paste0(scenario, "_")] - ends <- unlist(lapply(gregexpr("_", files), `[[`, 1)) + 1 - unique(substring(files, ends)) + cts <- meta$countries[(meta$touchstone %in% touchstone) & + (meta$disease %in% disease) & + (meta$group %in% group) & + (meta$scenario %in% scenario)] + sort(unique(strsplit(cts, ";")[[1]])) } res <- lookup(touchstone1, disease, group1, scenario1) res <- res[res %in% lookup(touchstone2, disease, group1, scenario1, res)] @@ -78,17 +68,18 @@ get_countries <- function(touchstone1, touchstone2, disease, group1, group2, get_outcomes <- function(touchstone1, touchstone2, disease, group1, group2, scenario1, scenario2, country) { + lookup <- function(touchstone, disease, group, scenario, country, res = NULL) { if ((is.null(touchstone)) || (is.null(group)) || (is.null(scenario))) { return(res) } - path <- file.path(data_dir, touchstone, paste(disease, group, sep = "_")) - thefile <- sprintf("%s_%s_%s.pq", group, scenario, country) - tbl <- arrow::read_parquet(file.path(path, thefile), col_select = NULL) - cols <- names(tbl) - sort(cols[!cols %in% c("disease", "run_id", "year", "age", - "country", "cohort_size")]) + outs <- meta$outcomes[(meta$touchstone %in% touchstone) & + (meta$disease %in% disease) & + (meta$group %in% group) & + (meta$scenario %in% scenario)] + sort(unique(strsplit(outs, ";")[[1]])) } + res <- lookup(touchstone1, disease, group1, scenario1, country) res <- res[res %in% lookup(touchstone2, disease, group1, scenario1, country, res)] res <- res[res %in% lookup(touchstone1, disease, group2, scenario1, country, res)] @@ -96,7 +87,8 @@ get_outcomes <- function(touchstone1, touchstone2, disease, group1, group2, res <- res[res %in% lookup(touchstone1, disease, group1, scenario2, country, res)] res <- res[res %in% lookup(touchstone2, disease, group1, scenario2, country, res)] res <- res[res %in% lookup(touchstone1, disease, group2, scenario2, country, res)] - sort(unique(res[res %in% lookup(touchstone2, disease, group2, scenario2, country, res)])) + res <- sort(unique(res[res %in% lookup(touchstone2, disease, group2, scenario2, country, res)])) + res } # GUI helpers. From 1b75e1debc78aa6708cc1e1176dfca3ba261e313 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Mon, 27 Apr 2026 16:26:15 +0100 Subject: [PATCH 02/14] Toggle stochastic lines --- R/stochastic_graphs.R | 20 ++++++++++++++------ inst/app/app.R | 7 ++++--- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/R/stochastic_graphs.R b/R/stochastic_graphs.R index 172369f..7158804 100644 --- a/R/stochastic_graphs.R +++ b/R/stochastic_graphs.R @@ -35,6 +35,8 @@ age_string <- function(ages) { ##' @param packit_file Used with packit_id to specify the filename of an RDS ##' file providing burden estimates. We expect to find scenario, year, age, ##' country, burden_outcome and value fields in the table. +##' @param include_stochastics Default TRUE, select whether to draw the +##' individual stochastic lines. ##' @param include_quantiles Default TRUE, select whether to plot the ##' 5% and 95% quantile lines. ##' @param include_mean Default TRUE, select whether to plot the mean. @@ -49,6 +51,7 @@ stone_stochastic_graph <- function(base, touchstone, disease, group, country, scenario, outcome, ages = NULL, by_cohort = FALSE, log = FALSE, packit_id = NULL, packit_file = NULL, + include_stochastics = TRUE, include_quantiles = TRUE, include_mean = TRUE, include_median = TRUE, @@ -76,6 +79,8 @@ stone_stochastic_graph <- function(base, touchstone, disease, group, country, runs <- max(d$run_id) miny <- max(1, min(d[[outcome]])) maxy <- max(d[[outcome]]) + minx <- min(d$year) + maxx <- max(d$year) log <- if (log) "y" else "" if (!is.null(packit_id)) { @@ -85,12 +90,15 @@ stone_stochastic_graph <- function(base, touchstone, disease, group, country, } par(mar = c(5, 4, 5, 2)) plot(ylab = outcome_ylab, xlab = if (by_cohort) "Birth Cohort" else "year", - x = d$year[d$run_id == 1], y = d[[outcome]][d$run_id == 1], type="l", - col = "#b0b0b0", ylim = c(miny, maxy), main = title, log = log) - - for (i in 2:runs) { - lines(x = d$year[d$run_id == i], y = d[[outcome]][d$run_id == i], - col = "#b0b0b0") + x = NULL, y = NULL, + col = "#b0b0b0", xlim = c(minx, maxx), ylim = c(miny, maxy), main = title, + log = log) + + if (include_stochastics) { + for (i in seq_len(runs)) { + lines(x = d$year[d$run_id == i], y = d[[outcome]][d$run_id == i], + col = "#b0b0b0") + } } avgs <- d %>% group_by(.data$year) %>% diff --git a/inst/app/app.R b/inst/app/app.R index bef6258..0850ee9 100644 --- a/inst/app/app.R +++ b/inst/app/app.R @@ -54,8 +54,8 @@ app_ui <- function() { choices = c("All", "Under 5")), checkboxGroupInput( sprintf("%s_options", prefix), "Plot Options:", - choices = c("Quantiles", "Median", "Mean", "Log-Y"), - selected = c("Quantiles", "Median", "Mean", "Log-Y"), + choices = c("Stochastics", "Quantiles", "Median", "Mean", "Log-Y"), + selected = c("Stochastics", "Quantiles", "Median", "Mean", "Log-Y"), inline = TRUE), actionButton(sprintf("%s_plot_btn", prefix), "Plot") )) @@ -325,7 +325,8 @@ app_server <- function(input, output, session) { log = "Log-Y" %in% pr$opts, include_median = "Median" %in% pr$opts, include_quantiles = "Quantiles" %in% pr$opts, - include_mean = "Mean" %in% pr$opts + include_mean = "Mean" %in% pr$opts, + include_stochastics = "Stochastics" %in% pr$opts ) }) }) From a33090186b5661d13751c2a9c012d8fcd950b548 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Mon, 27 Apr 2026 16:31:02 +0100 Subject: [PATCH 03/14] Impact -> Burden Diff --- inst/app/app.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/inst/app/app.R b/inst/app/app.R index 0850ee9..a1d7509 100644 --- a/inst/app/app.R +++ b/inst/app/app.R @@ -112,9 +112,9 @@ app_ui <- function() { make_panel("Burden", "b", 1, 1, 1), make_panel("Burden/TS", "bts", 2, 1, 1), make_panel("Burden/MG", "bmg", 1, 1, 2), - make_panel("Impact", "i", 1, 2, 1), - make_panel("Impact/TS", "its", 2, 2, 1), - make_panel("Impact/MG", "img", 1, 2, 2) + make_panel("Burden Diff", "i", 1, 2, 1), + make_panel("Burden Diff/TS", "its", 2, 2, 1), + make_panel("Burden Diff/MG", "img", 1, 2, 2) ) ) } From 87ee15cca68fc9d9b919666e1a187f25913edd6b Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Thu, 30 Apr 2026 16:01:01 +0100 Subject: [PATCH 04/14] Refactor graphs for y-axis and add age-based --- DESCRIPTION | 2 +- R/stochastic_graphs.R | 359 +++++++++++++++++------- inst/app/app.R | 79 +++--- inst/app/utils.R | 16 ++ tests/testthat/test_stochastic_graphs.R | 37 ++- 5 files changed, 335 insertions(+), 158 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 72ada1b..f82a26c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: stoner Title: Support for Building VIMC Montagu Touchstones, using Dettl -Version: 0.1.21 +Version: 0.1.22 Authors@R: c(person("Wes", "Hinsley",role = c("aut", "cre", "cst", "dnc", "elg", "itr", "sng", "ard"), email = "w.hinsley@imperial.ac.uk"), diff --git a/R/stochastic_graphs.R b/R/stochastic_graphs.R index 7158804..a2c59e7 100644 --- a/R/stochastic_graphs.R +++ b/R/stochastic_graphs.R @@ -1,13 +1,25 @@ -age_string <- function(ages) { - if (is.null(ages)) { - "all ages" - } else if (identical(unique(sort(ages)), as.numeric(min(ages):max(ages)))) { - sprintf("age %d..%d", min(ages), max(ages)) - } else "selected ages" +filter_string <- function(s, units) { + if (is.null(s)) return(sprintf("all %s", units)) + s <- as.numeric(s) + if (identical(unique(sort(s)), as.numeric(min(s):max(s)))) { + sprintf("%s %d..%d", units, min(s), max(s)) + } else sprintf("selected %s", units) } -##' Draw a stochastic plot showing all the different runs, with the mean, -##' median, 5% and 95% quantiles shown. +##' A feature-rich graph plotting function, which can show the stochastic +##' line for an outcome for different runs, the mean, median and quantiles. +##' If two scenarios are specified, then the burden difference between +##' scenarios is plotted. Additionally, if multiple groups or multiple +##' touchstones are specified, then separate graphs with the same scaling are +##' plotted to allow comparison. +##' +##' Graphs can have calendar year or birth cohort on the y-axis for +##' time-series plots, in which case age is aggregated and the ages to be +##' included can be specified. Alternatively, the x-axis can be age aggregated +##' over time, where the calendar years to be aggregated can be specified. +##' +##' Finally, it can also include a central estimate provided as a file within +##' a packit, from the montagu reporting portal. ##' ##' @export ##' @title Stochastic plot @@ -17,18 +29,33 @@ age_string <- function(ages) { ##' @importFrom graphics lines par ##' @importFrom stats quantile median ##' @importFrom grDevices recordPlot -##' @param base The folder in which the standardised stochastic files are found. -##' @param touchstone The touchstone name (for the graph title) -##' @param disease The disease, used for building the filename and graph title. -##' @param group The modelling group, used in the filename and graph title. +##' @param base The root older in which the standardised stochastic files are +##' found. Within it should be folders by touchstone. +##' @param touchstones A touchstone to plot, or a pair of touchstones to +##' compare. If a pair, then only one modelling group can be specified. +##' @param disease The disease for the stochastics to display +##' @param groups The modelling group to plot data for, or a pair of modelling +##' groups to be compared, in which case, only touchstone can be specified. ##' @param country The country to plot. -##' @param scenario The scenario to plot. -##' @param outcome The outcome to plot, for example `deaths`, `cases`, `dalys` or -##' since 2023, `yll`. -##' @param ages A vector of one or more ages to be selected and aggregated, or -##' if left as NULL, then all ages are used and aggregated. +##' @param scenarios A scenario to plot data for, or a pair of scenarios, in +##' which case the outcome for the first scenario, subtract the second, will be +##' plotted. +##' @param outcome The outcome to plot, for example `deaths`, `cases`, `dalys` +##' or since 2023, `yll`. +##' @param xaxis The default is "time", meaning the x-axis is either calendar +##' year, or birth cohort, depending on the `by_cohort` parameter, and age is +##' then aggregated according using the `aggregate_ages` parameter. +##' Alternatively, set to "age", and aggregate by time using the +##' `aggregate_years` parameter. +##' @param filter Filter either ages, years, or birth cohort years to a range, +##' depending on what `xaxis` is chosen. When `xaxis` is `time`, we are summing +##' ages, so can filter to `0:4` for example, to only include under 5 burdens. +##' When `xaxis` is `age` then we want to see burden by age for an aggregated +##' time period; the filter here is for which years to include, or which +##' birth cohorts to include, depending on whether `by_cohort` is set. `filter` +##' can be left as `NULL` to include all ages or years. ##' @param by_cohort If TRUE, then age is subtracted from year to convert it to -##' year of birth before aggregating. +##' year of birth before aggregating. This is only used when xaxis is "time". ##' @param log If TRUE, then use a logged y-axis. ##' @param packit_id If set, then read central burden estimates from a file ##' within a packit on the Montagu packit server. @@ -41,129 +68,247 @@ age_string <- function(ages) { ##' 5% and 95% quantile lines. ##' @param include_mean Default TRUE, select whether to plot the mean. ##' @param include_median Default TRUE, select whether to plot the median. -##' @param scenario2 Default NULL; if set, then the burdens from this -##' scenario will be subtracted from those in `scenario` - ie, this plots -##' an impact graph of applying the second scenario. For many graphs that -##' use this, the result will be positive numbers, representing cases -##' or deaths averted. - -stone_stochastic_graph <- function(base, touchstone, disease, group, country, - scenario, outcome, ages = NULL, + +stone_stochastic_graph <- function(base, + touchstones, disease, groups, country, + scenarios, outcome, + xaxis = "time", + filter = NULL, by_cohort = FALSE, log = FALSE, packit_id = NULL, packit_file = NULL, include_stochastics = TRUE, include_quantiles = TRUE, include_mean = TRUE, - include_median = TRUE, - scenario2 = NULL) { + include_median = TRUE) { - d <- prepare_graph_data(base, touchstone, disease, group, country, - scenario, outcome, ages, by_cohort) - age <- age_string(ages) + # Forbid multi-touchstone AND multi-group for now - which means we will + # only produce 2 graphs. We could do more - for example, 3 groups that + # model a disease, but for now, we'll stick with two. - title <- sprintf("%s, %s, %s, %s\n%s, %s\n", touchstone, disease, group, age, - scenario, country) - outcome_ylab <- outcome + if (!length(touchstones) %in% 1:2) { + cli::cli_abort("Only specify one or two touchstones.") + } + + if (!length(groups) %in% 1:2) { + cli::cli_abort("Only specify one or two modelling groups.") + } + + if (!length(scenarios) %in% 1:2) { + cli::cli_abort("Only specify one or two scenarios.") + } + + if ((length(touchstones) == 2) && (length(groups) ==2)) { + cli::cli_abort("Only one of `touchstones` or `groups` can be plural.") + } + + if (!isTRUE(tolower(xaxis) %in% c("time", "age"))) { + cli::cli_abort("`xaxis` must be either `time` or `age`.") + } + + xaxis <- tolower(xaxis) - if (!is.null(scenario2)) { - d2 <- prepare_graph_data(base, touchstone, disease, group, country, - scenario2, outcome, ages, by_cohort) - title <- sprintf("%s, %s, %s, %s\n%s, %s\n", touchstone, disease, group, age, - sprintf("Impact of %s ->\n%s", scenario, scenario2), country) - d <- d[order(d$year, d$run_id), ] - d2 <- d2[order(d2$year, d2$run_id), ] - d[[outcome]] <- d[[outcome]] - d2[[outcome]] - outcome_ylab <- paste(outcome_ylab, "averted") + if (!isTRUE(tolower(outcome) %in% c("cases", "dalys", "deaths", "yll"))) { + cli::cli_abort("`outcome` must be `cases`, `dalys`, `deaths` or `yll`") } - runs <- max(d$run_id) - miny <- max(1, min(d[[outcome]])) - maxy <- max(d[[outcome]]) - minx <- min(d$year) - maxx <- max(d$year) - log <- if (log) "y" else "" + outcome <- tolower(outcome) + + # Fetch the packit data if wanted if (!is.null(packit_id)) { - central <- prepare_central_data(packit_id, packit_file, - country, scenario, outcome, ages, by_cohort) - # To be continued... - } - par(mar = c(5, 4, 5, 2)) - plot(ylab = outcome_ylab, xlab = if (by_cohort) "Birth Cohort" else "year", - x = NULL, y = NULL, - col = "#b0b0b0", xlim = c(minx, maxx), ylim = c(miny, maxy), main = title, - log = log) - - if (include_stochastics) { - for (i in seq_len(runs)) { - lines(x = d$year[d$run_id == i], y = d[[outcome]][d$run_id == i], - col = "#b0b0b0") + central <- get_packit_data(packit_id, packit_file, + country, scenarios, outcome, by_cohort) + if (xaxis == "age") { + central <- aggregate_by_age(central, outcome, filter) + } else { + central <- aggregate_by_year(central, outcome, filter) } } - avgs <- d %>% group_by(.data$year) %>% - summarise( - mean = mean(.data[[outcome]]), - median = median(.data[[outcome]]), - q05 = quantile(.data[[outcome]], 0.05), - q95 = quantile(.data[[outcome]], 0.95), - .groups = "drop" - ) - if (include_mean) { - lines(x = avgs$year, y = avgs$mean, col = "#ff4040", lwd = 2) - } - if (include_median) { - lines(x = avgs$year, y = avgs$median, col = "#00ff00", lwd = 2) - } - if (include_quantiles) { - lines(x = avgs$year, y = avgs$q05, col = "#202020", lwd = 2) - lines(x = avgs$year, y = avgs$q95, col = "#202020", lwd = 2) - } - recordPlot() + # Fetch the data from the files. We may end up with 1 or 2 graphs here. + + data <- list() + n_graphs <- 0 + for (touchstone in touchstones) { + for (group in groups) { + n_graphs <- n_graphs + 1 + data[[n_graphs]] <- get_graph_data(base, touchstone, disease, group, + country, scenarios, outcome, by_cohort) + } + } + + # Filter and aggregate the data, finding bounds + + miny <- Inf + maxy <- -Inf + minx <- Inf + maxx <- -Inf + + for (i in 1:n_graphs) { + if (xaxis == "age") { + data[[i]] <- aggregate_by_age(data[[i]], outcome, filter) + minx <- 0 + maxx <- 100 + } else { + data[[i]] <- aggregate_by_year(data[[i]], outcome, filter) + minx <- min(minx, data[[i]]$year) + maxx <- max(maxx, data[[i]]$year) + } + miny <- min(miny, data[[i]][[outcome]]) + maxy <- max(maxy, data[[i]][[outcome]]) + } + + # Sort out titles and labels common to both graphs + units <- if (xaxis == "time") "ages" else if (by_cohort) "cohorts" else "years" + filter_title <- filter_string(filter, units) + outcome_ylab <- outcome + scenario_title <- scenarios[1] + + if (log) { + log <- "y" + miny <- max(1, miny) + } else { + log <- "" + } + + if (length(scenarios) == 2) { + outcome_ylab <- paste(outcome_ylab, "avered") + scenario_title <- sprintf("Difference of %s ->\n%s", scenarios[1], + scenarios[2]) + } + + + # Plot the one or two graphs + + res <- list() + for (i in 1:n_graphs) { + touchstone_title <- touchstones[min(i, length(touchstones))] + group_title <- groups[min(i, length(groups))] + title <- sprintf("%s, %s, %s, %s\n%s, %s\n", + touchstone_title, disease, group_title, + filter_title, + scenario_title, country) + + + if (xaxis == "age") { + xlabel <- "Age" + xfield <- "age" + } else { + xlabel <- if (by_cohort) "Birth Cohort" else "Year" + xfield <- "year" + } + + + runs <- max(data[[i]]$run_id) + par(mar = c(5, 4, 5, 2)) + plot(ylab = outcome_ylab, xlab = xlabel, + x = NULL, y = NULL, + col = "#b0b0b0", xlim = c(minx, maxx), ylim = c(miny, maxy), + main = title, log = log) + + if (include_stochastics) { + for (j in seq_len(runs)) { + lines(x = data[[i]][[xfield]][data[[i]]$run_id == j], + y = data[[i]][[outcome]][data[[i]]$run_id == j], + col = "#b0b0b0") + } + } + + if (include_mean | include_median | include_quantiles) { + avgs <- data[[i]] %>% group_by(.data[[xfield]]) %>% + summarise( + mean = mean(.data[[outcome]]), + median = median(.data[[outcome]]), + q05 = quantile(.data[[outcome]], 0.05), + q95 = quantile(.data[[outcome]], 0.95), + .groups = "drop" + ) + if (include_mean) { + lines(x = avgs[[xfield]], y = avgs$mean, col = "#ff4040", lwd = 2) + } + if (include_median) { + lines(x = avgs[[xfield]], y = avgs$median, col = "#00ff00", lwd = 2) + } + if (include_quantiles) { + lines(x = avgs[[xfield]], y = avgs$q05, col = "#202020", lwd = 2) + lines(x = avgs[[xfield]], y = avgs$q95, col = "#202020", lwd = 2) + } + } + res[[i]] <- recordPlot() + } + res +} + +get_burden_difference <- function(data, outcome) { + if (length(data) == 2) { + data[[1]][[outcome]] <- data[[1]][[outcome]] - data[[2]][[outcome]] + } + data[[1]] } +# Here we'll fetch the data for one graph. `scenarios` might be length 1 +# (which is easy) or 2 - in which case we'll subtract the outcome values of +# the second from the first. We can also do the by_cohort calculation in here -prepare_graph_data <- function(base, touchstone, disease, group, country, - scenario, outcome, ages, by_cohort) { +get_graph_data <- function(base, touchstone, disease, group, country, + scenarios, outcome, by_cohort) { - pq <- sprintf("%s/%s/%s_%s/%s_%s_%s.pq", base, touchstone, disease, - group, group, scenario, country) + data <- list() + for (s in seq_along(scenarios)) { + pq <- sprintf("%s/%s/%s_%s/%s_%s_%s.pq", base, touchstone, disease, + group, group, scenarios[s], country) - d <- arrow::read_parquet(pq) + if (!file.exists(pq)) { + cli::cli_abort("Couldn't find file {pq} - check files or parameters") + } + d <- arrow::read_parquet(pq) + if (by_cohort) { + d$year <- d$year - d$age + } + d <- d[order(d$year ,d$age, d$run_id), ] + data[[s]] <- d[, c("run_id", "year", "age", outcome)] + } + get_burden_difference(data, outcome) +} + +aggregate_by_year <- function(d, outcome, ages = NULL) { if (!is.null(ages)) { d <- d[d$age %in% ages, ] } - if (by_cohort) { - d$year <- d$year - d$age + d %>% group_by(.data$run_id, .data$year) %>% + summarise( + !!outcome := sum(.data[[outcome]], na.rm = TRUE), + .groups = "drop") +} + +aggregate_by_age <- function(d, outcome, years = NULL) { + if (!is.null(years)) { + d <- d[d$year %in% years, ] } - d <- d[, c("run_id", "year", "age", outcome)] - d <- d %>% group_by(.data$run_id, .data$year) %>% + d %>% group_by(.data$run_id, .data$age) %>% summarise( !!outcome := sum(.data[[outcome]], na.rm = TRUE), .groups = "drop") - d } -prepare_central_data <- function(packit_id, packit_file, - country, scenario, outcome, ages, by_cohort) { +get_packit_data <- function(packit_id, packit_file, + country, scenarios, outcome, by_cohort) { central <- readRDS(fetch_packit(packit_id, packit_file)) central <- central[central$country == country, ] - central <- central[central$scenario == scenario, ] central <- central[central$burden_outcome == outcome, ] - if (!is.null(ages)) { - central <- central[central$age %in% ages, ] - } - if (by_cohort) { - central$year <- central$year - central$age + + data <- list() + for (s in seq_along(scenarios)) { + d <- central[central$scenario == scenarios[s], ] + if (by_cohort) { + d$year <- d$year - d$age + } + names(d)[names(d) == "value"] <- outcome + d <- d[order(d$year, d$age), ] + data[[s]] <- d } - names(central)[names(central) == "value"] <- outcome - central <- central %>% group_by(.data$year) %>% - summarise( - !!outcome := sum(.data[[outcome]], na.rm = TRUE), - .groups = "drop") - central$run_id <- 0 - central + get_burden_difference(data, outcome) } ##' Launch a Shiny app to allow interactive plotting of diff --git a/inst/app/app.R b/inst/app/app.R index a1d7509..03a5242 100644 --- a/inst/app/app.R +++ b/inst/app/app.R @@ -47,11 +47,11 @@ app_ui <- function() { sidebar <- append(sidebar, scenarios) sidebar <- append(sidebar, list( selectInput(sprintf("%s_country", prefix), "Country:", choices = c0), + selectInput(sprintf("%s_xaxis", prefix), "X-Axis:", choices = + c("Time (Calendar)", "Time (Birth Cohort)", + "Age (Calendar)", "Age (Birth Cohort)")), selectInput(sprintf("%s_outcome", prefix), "Y-Axis:", choices = c0), - radioButtons(sprintf("%s_year", prefix), "X-Axis:", inline = TRUE, - choices = c("Calendar", "Cohort")), - radioButtons(sprintf("%s_ages", prefix), "Age:", inline = TRUE, - choices = c("All", "Under 5")), + textInput(sprintf("%s_filter", prefix), "Filter Ages", value = "All"), checkboxGroupInput( sprintf("%s_options", prefix), "Plot Options:", choices = c("Stochastics", "Quantiles", "Median", "Mean", "Log-Y"), @@ -253,6 +253,17 @@ app_server <- function(input, output, session) { input, prefix)}) + filter <- sprintf("%s_filter", prefix) + xaxis <- sprintf("%s_xaxis", prefix) + + observeEvent(input[[xaxis]], { + xaxis_val <- input[[xaxis]] + if (grepl("Time", xaxis_val)) lab <- "Filter age: (eg. All, or 0-4)" + else if (grepl("Calendar", xaxis_val)) lab <- "Filter years: (eg. All, or 2000-2010)" + else lab <- "Filter cohorts: (eg. All, or 2000-2010)" + updateTextInput(session, filter, label = lab) + }) + n_graphs <- 1 + ((n_touchstone * n_group) > 1) if (n_graphs == 1) { graphs <- sprintf("%s_main_plot", prefix) @@ -262,11 +273,6 @@ app_server <- function(input, output, session) { button <- sprintf("%s_plot_btn", prefix) plot_reactive <- eventReactive(input[[button]], { - ages <- NULL - if (input[[sprintf("%s_ages", prefix)]] == "Under 5") { - ages <- 0:4 - } - check <- function(pre, x1, x2, type) { if (is.null(x2)) return(TRUE) if ((grepl(pre, prefix)) && (input[[x1]] == input[[x2]])) { @@ -286,7 +292,7 @@ app_server <- function(input, output, session) { if (!check("i", is1, is2, "scenario")) return(NULL) list( - ages = ages, + filter = input[[sprintf("%s_filter", prefix)]], opts = input[[sprintf("%s_options", prefix)]], touchstone = c(input[[it1]], if (!is.null(it2)) input[[it2]] else NULL), @@ -296,38 +302,41 @@ app_server <- function(input, output, session) { disease = input[[id]], country = input[[ic]], outcome = input[[sprintf("%s_outcome", prefix)]], - year = input[[sprintf("%s_year", prefix)]] + xaxis = input[[sprintf("%s_xaxis", prefix)]] + ) + }) + + res_reactive <- reactive({ + pr <- plot_reactive() + req(pr) + filter_vec <- parse_filter(pr$filter) + + stoner::stone_stochastic_graph( + base = data_dir, + touchstones = pr$touchstone, + disease = pr$disease, + groups = pr$group, + country = pr$country, + scenarios = pr$scenario, + outcome = pr$outcome, + xaxis = if (grepl("Time", pr$xaxis)) "time" else "age", + filter = filter_vec, + by_cohort = grepl("Birth Cohort", pr$xaxis), + log = "Log-Y" %in% pr$opts, + include_median = "Median" %in% pr$opts, + include_quantiles = "Quantiles" %in% pr$opts, + include_mean = "Mean" %in% pr$opts, + include_stochastics = "Stochastics" %in% pr$opts ) }) for (g in seq_along(graphs)) { local({ gg <- g - output[[graphs[gg]]] <- renderPlot({ - pr <- plot_reactive() - req(pr) - - it <- pr$touchstone[min(gg, length(pr$touchstone))] - ig <- pr$group[min(gg, length(pr$group))] - - stoner::stone_stochastic_graph( - base = data_dir, - touchstone = it, - disease = pr$disease, - group = ig, - country = pr$country, - scenario = pr$scenario[1], - scenario2 = if (length(pr$scenario) > 1) pr$scenario[2] else NULL, - outcome = pr$outcome, - by_cohort = pr$year == "Cohort", - ages = pr$ages, - log = "Log-Y" %in% pr$opts, - include_median = "Median" %in% pr$opts, - include_quantiles = "Quantiles" %in% pr$opts, - include_mean = "Mean" %in% pr$opts, - include_stochastics = "Stochastics" %in% pr$opts - ) + res <- res_reactive() + req(res) + replayPlot(res[[gg]]) }) }) } diff --git a/inst/app/utils.R b/inst/app/utils.R index 26ce773..f3a6ad6 100644 --- a/inst/app/utils.R +++ b/inst/app/utils.R @@ -170,3 +170,19 @@ update_country <- function(session, ts1, ts2, disease, g1, g2, s1, s2, country, outcomes <- get_outcomes(ts1, ts2, disease, g1, g2, s1, s2, country) update_dropdown_keep(session, o, outcomes, input[[o]]) } + +parse_filter <- function(s) { + if (tolower(s) == "all") return(NULL) + s <- gsub(" ", "", s) + s <- strsplit(s, ",")[[1]] + sel <- integer(0) + for (bit in s) { + if (!grepl("-", bit)) { + sel <- c(sel, as.integer(bit)) + } else { + from_to <- strsplit(bit, "-")[[1]] + sel <- c(sel, from_to[1]:from_to[2]) + } + } + sort(unique(sel)) +} diff --git a/tests/testthat/test_stochastic_graphs.R b/tests/testthat/test_stochastic_graphs.R index 58a2735..01b03e1 100644 --- a/tests/testthat/test_stochastic_graphs.R +++ b/tests/testthat/test_stochastic_graphs.R @@ -29,32 +29,39 @@ test_that("stochastic_graph data transforms", { if (file.exists(f)) file.remove(f) arrow::write_parquet(data, file.path(folder, filename)) - # Aggregate all ages, not by cohort. Should have 1 point per year. + # Expecting unaggregated data - res <- prepare_graph_data(base, touchstone, disease, group, country, - scenario, "deaths", NULL, FALSE) + res <- get_graph_data(base, touchstone, disease, group, country, + scenario, "deaths", FALSE) - expect_equal(nrow(res), 25) # 5 runs, 5 years - expect_equal(res$deaths[res$run_id == 1 & res$year == 2002], + expect_equal(nrow(res), 125) # 5 runs, 5 years, 5 ages + + # Aggregate by year + + res2 <- aggregate_by_year(res, "deaths") + expect_equal(nrow(res2), 25) # 5 runs, 5 years, all age + + expect_equal(res2$deaths[res2$run_id == 1 & res2$year == 2002], sum(data$deaths[data$year == 2002 & data$run_id == 1])) # Select ages - res <- prepare_graph_data(base, touchstone, disease, group, country, - scenario, "deaths", c(10, 12, 14), FALSE) + res2 <- aggregate_by_year(res, "deaths", c(10, 12, 14)) - expect_equal(nrow(res), 25) # 5 runs, 5 years - expect_equal(res$deaths[res$run_id == 1 & res$year == 2003], + expect_equal(nrow(res2), 25) # 5 runs, 5 years + expect_equal(res2$deaths[res2$run_id == 1 & res2$year == 2003], sum(data$deaths[data$year == 2003 & data$run_id == 1 & data$age %in% c(10, 12, 14)])) # By cohort - res <- prepare_graph_data(base, touchstone, disease, group, country, - scenario, "deaths", c(10, 12, 14), TRUE) + res <- get_graph_data(base, touchstone, disease, group, country, + scenario, "deaths", TRUE) + + res2 <- aggregate_by_year(res, "deaths", c(10, 12, 14)) - expect_equal(min(res$year), min(data$year) - max(data$age)) - expect_equal(max(res$year), max(data$year) - min(data$age)) + expect_equal(min(res2$year), min(data$year) - max(data$age)) + expect_equal(max(res2$year), max(data$year) - min(data$age)) # Test graph - we can't really, but just check it doesn't crash. @@ -68,12 +75,12 @@ test_that("stochastic_graph data transforms", { expect_no_error(stone_stochastic_graph( base, touchstone, disease, group, country, - scenario, "deaths", scenario2 = scenario)) + c(scenario, scenario), "deaths")) # Packit gets called if needed fake_result <- mockery::mock("fake_result") - mockery::stub(stone_stochastic_graph, "prepare_central_data", fake_result) + mockery::stub(stone_stochastic_graph, "get_packit_data", fake_result) expect_no_error(stone_stochastic_graph( base, touchstone, disease, group, country, From 1e6279be6edca51387d0caddd63dbe25cc66dd59 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Thu, 30 Apr 2026 17:24:06 +0100 Subject: [PATCH 05/14] Cleanup docs --- R/stochastic_graphs.R | 42 +++++++-------- man/stone_stochastic_graph.Rd | 71 ++++++++++++++++--------- tests/testthat/test_stochastic_graphs.R | 10 ++-- 3 files changed, 71 insertions(+), 52 deletions(-) diff --git a/R/stochastic_graphs.R b/R/stochastic_graphs.R index a2c59e7..1b9e38b 100644 --- a/R/stochastic_graphs.R +++ b/R/stochastic_graphs.R @@ -6,7 +6,7 @@ filter_string <- function(s, units) { } else sprintf("selected %s", units) } -##' A feature-rich graph plotting function, which can show the stochastic +##' A feature-rich-ish graph plotting function, which can show the stochastic ##' line for an outcome for different runs, the mean, median and quantiles. ##' If two scenarios are specified, then the burden difference between ##' scenarios is plotted. Additionally, if multiple groups or multiple @@ -29,33 +29,29 @@ filter_string <- function(s, units) { ##' @importFrom graphics lines par ##' @importFrom stats quantile median ##' @importFrom grDevices recordPlot -##' @param base The root older in which the standardised stochastic files are -##' found. Within it should be folders by touchstone. +##' @param base The root folder in which the standardised stochastic files are +##' found. Within it should be folders with touchstone names. ##' @param touchstones A touchstone to plot, or a pair of touchstones to -##' compare. If a pair, then only one modelling group can be specified. -##' @param disease The disease for the stochastics to display -##' @param groups The modelling group to plot data for, or a pair of modelling -##' groups to be compared, in which case, only touchstone can be specified. +##' compare. If a pair, then only one modelling group can be specified below. +##' @param disease The disease to display. +##' @param groups The modelling group to plot, or a pair of modelling +##' groups to be compared, in which case, only one touchstone can be specified. ##' @param country The country to plot. -##' @param scenarios A scenario to plot data for, or a pair of scenarios, in -##' which case the outcome for the first scenario, subtract the second, will be -##' plotted. +##' @param scenarios A scenario to plot data for, or a pair of scenarios in +##' which case we plot the outcome for the first scenario, subtract the second. ##' @param outcome The outcome to plot, for example `deaths`, `cases`, `dalys` -##' or since 2023, `yll`. +##' or since 2023, `yll`. ##' @param xaxis The default is "time", meaning the x-axis is either calendar -##' year, or birth cohort, depending on the `by_cohort` parameter, and age is -##' then aggregated according using the `aggregate_ages` parameter. -##' Alternatively, set to "age", and aggregate by time using the -##' `aggregate_years` parameter. -##' @param filter Filter either ages, years, or birth cohort years to a range, -##' depending on what `xaxis` is chosen. When `xaxis` is `time`, we are summing -##' ages, so can filter to `0:4` for example, to only include under 5 burdens. -##' When `xaxis` is `age` then we want to see burden by age for an aggregated -##' time period; the filter here is for which years to include, or which -##' birth cohorts to include, depending on whether `by_cohort` is set. `filter` -##' can be left as `NULL` to include all ages or years. +##' year, or birth cohort, depending on the `by_cohort` parameter. This means +##' and age gets filtered by the `filter` parameter, and then aggregated. +##' Alternatively, if set to "age", then age will be on the x-axis, and the +##' `filter` parameter specifies either the years, or the birth cohorts to +##' select (depending on the `by_cohort` parameter), before aggregation. +##' @param filter Filter either ages, years, or birth cohort years to a range - +##' see the comments above on the `xaxis` parametern. The filter is a vector +##' of ages, or years, or can be NULL to do no filtering. ##' @param by_cohort If TRUE, then age is subtracted from year to convert it to -##' year of birth before aggregating. This is only used when xaxis is "time". +##' year of birth before aggregating. See the `xaxis` and `filter` parameters. ##' @param log If TRUE, then use a logged y-axis. ##' @param packit_id If set, then read central burden estimates from a file ##' within a packit on the Montagu packit server. diff --git a/man/stone_stochastic_graph.Rd b/man/stone_stochastic_graph.Rd index 5e40693..59451ec 100644 --- a/man/stone_stochastic_graph.Rd +++ b/man/stone_stochastic_graph.Rd @@ -6,44 +6,57 @@ \usage{ stone_stochastic_graph( base, - touchstone, + touchstones, disease, - group, + groups, country, - scenario, + scenarios, outcome, - ages = NULL, + xaxis = "time", + filter = NULL, by_cohort = FALSE, log = FALSE, packit_id = NULL, packit_file = NULL, + include_stochastics = TRUE, include_quantiles = TRUE, include_mean = TRUE, - include_median = TRUE, - scenario2 = NULL + include_median = TRUE ) } \arguments{ -\item{base}{The folder in which the standardised stochastic files are found.} +\item{base}{The root folder in which the standardised stochastic files are +found. Within it should be folders with touchstone names.} -\item{touchstone}{The touchstone name (for the graph title)} +\item{touchstones}{A touchstone to plot, or a pair of touchstones to +compare. If a pair, then only one modelling group can be specified below.} -\item{disease}{The disease, used for building the filename and graph title.} +\item{disease}{The disease to display.} -\item{group}{The modelling group, used in the filename and graph title.} +\item{groups}{The modelling group to plot, or a pair of modelling +groups to be compared, in which case, only one touchstone can be specified.} \item{country}{The country to plot.} -\item{scenario}{The scenario to plot.} +\item{scenarios}{A scenario to plot data for, or a pair of scenarios in +which case we plot the outcome for the first scenario, subtract the second.} -\item{outcome}{The outcome to plot, for example \code{deaths}, \code{cases}, \code{dalys} or -since 2023, \code{yll}.} +\item{outcome}{The outcome to plot, for example \code{deaths}, \code{cases}, \code{dalys} +or since 2023, \code{yll}.} -\item{ages}{A vector of one or more ages to be selected and aggregated, or -if left as NULL, then all ages are used and aggregated.} +\item{xaxis}{The default is "time", meaning the x-axis is either calendar +year, or birth cohort, depending on the \code{by_cohort} parameter. This means +and age gets filtered by the \code{filter} parameter, and then aggregated. +Alternatively, if set to "age", then age will be on the x-axis, and the +\code{filter} parameter specifies either the years, or the birth cohorts to +select (depending on the \code{by_cohort} parameter), before aggregation.} + +\item{filter}{Filter either ages, years, or birth cohort years to a range - +see the comments above on the \code{xaxis} parametern. The filter is a vector +of ages, or years, or can be NULL to do no filtering.} \item{by_cohort}{If TRUE, then age is subtracted from year to convert it to -year of birth before aggregating.} +year of birth before aggregating. See the \code{xaxis} and \code{filter} parameters.} \item{log}{If TRUE, then use a logged y-axis.} @@ -54,20 +67,30 @@ within a packit on the Montagu packit server.} file providing burden estimates. We expect to find scenario, year, age, country, burden_outcome and value fields in the table.} +\item{include_stochastics}{Default TRUE, select whether to draw the +individual stochastic lines.} + \item{include_quantiles}{Default TRUE, select whether to plot the 5\% and 95\% quantile lines.} \item{include_mean}{Default TRUE, select whether to plot the mean.} \item{include_median}{Default TRUE, select whether to plot the median.} - -\item{scenario2}{Default NULL; if set, then the burdens from this -scenario will be subtracted from those in \code{scenario} - ie, this plots -an impact graph of applying the second scenario. For many graphs that -use this, the result will be positive numbers, representing cases -or deaths averted.} } \description{ -Draw a stochastic plot showing all the different runs, with the mean, -median, 5\% and 95\% quantiles shown. +A feature-rich-ish graph plotting function, which can show the stochastic +line for an outcome for different runs, the mean, median and quantiles. +If two scenarios are specified, then the burden difference between +scenarios is plotted. Additionally, if multiple groups or multiple +touchstones are specified, then separate graphs with the same scaling are +plotted to allow comparison. +} +\details{ +Graphs can have calendar year or birth cohort on the y-axis for +time-series plots, in which case age is aggregated and the ages to be +included can be specified. Alternatively, the x-axis can be age aggregated +over time, where the calendar years to be aggregated can be specified. + +Finally, it can also include a central estimate provided as a file within +a packit, from the montagu reporting portal. } diff --git a/tests/testthat/test_stochastic_graphs.R b/tests/testthat/test_stochastic_graphs.R index 01b03e1..ca167cd 100644 --- a/tests/testthat/test_stochastic_graphs.R +++ b/tests/testthat/test_stochastic_graphs.R @@ -123,10 +123,10 @@ test_that("Can launch shiny app", { }) }) -test_that("Age formats are reasonable", { - expect_equal(age_string(NULL), "all ages") - expect_equal(age_string(c(5,4,3,2,1,5,4,3,2,1)), "age 1..5") - expect_equal(age_string(c(2,4,6,8)), "selected ages") +test_that("Filter formats are reasonable", { + expect_equal(filter_string(NULL, "ages"), "all ages") + expect_equal(filter_string(c(5,4,3,2,1,5,4,3,2,1), "ages"), "ages 1..5") + expect_equal(filter_string(c(2,4,6,8), "potatoes"), "selected potatoes") }) test_that("Parsing central from packit works", { @@ -144,7 +144,7 @@ test_that("Parsing central from packit works", { saveRDS(fake, rds) fetch_fake <- function(id, file) rds - mockery::stub(prepare_central_data, "fetch_packit", fetch_fake) + mockery::stub(get_packit_data, "fetch_packit", fetch_fake) res <- prepare_central_data("123", "file.csv", "RFP", "RSV-rout", "deaths", 0:5, TRUE) From 4bbc072a02ff27181db032fd682c7c974e941915 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Fri, 1 May 2026 15:01:03 +0100 Subject: [PATCH 06/14] Omit X and X.1 cols in standardise --- R/stochastic_files.R | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/R/stochastic_files.R b/R/stochastic_files.R index 31c13c6..cbcd9e1 100644 --- a/R/stochastic_files.R +++ b/R/stochastic_files.R @@ -150,10 +150,19 @@ stone_stochastic_standardise <- function( } } + # Note that for MenA, we want to keep the _cwyx outcomes. + if (missing_run_id_fix) { if ((!"run_id" %in% names(d)) && (length(index) == 200)) d$run_id <- j } + # Remove columns "X" and "X.1" that have crept in with some of the + # inputs saved with row.names + + d[["X"]] <- NULL + d[["X.1"]] <- NULL + + # Round to integer, as per guidance. (Not using as.integer, as that # has limits on how large numbers can be, so we are just truncating From e71cf0d4d92b9f7138170d0fbb4b4e04286bbb3c Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Fri, 1 May 2026 18:44:20 +0100 Subject: [PATCH 07/14] Update meta creation --- R/stochastic_files.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/stochastic_files.R b/R/stochastic_files.R index cbcd9e1..64fe58e 100644 --- a/R/stochastic_files.R +++ b/R/stochastic_files.R @@ -276,8 +276,9 @@ stone_stochastic_make_meta <- function(path) { first <- file.path(path, touchstone, folder, files[1]) ds <- arrow::open_dataset(first) outcomes <- ds$schema$names - outcomes <- outcomes[tolower(outcomes) %in% - c("cases", "deaths", "dalys", "yll")] + outcomes <- outcomes[!outcomes %in% c("run_id", "disease", "year", "age", + "country", "cohort_size")] + outcomes <- sort(unique(tolower(outcomes))) files <- strsplit(list.files(file.path(path, touchstone, folder)), "_") From 51fe6283183e38c9024bf3ccfe59d92214233b82 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Fri, 1 May 2026 20:11:15 +0100 Subject: [PATCH 08/14] Plot more outcomes --- R/stochastic_graphs.R | 4 ---- scripts/process_stoch_202602yf.R | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 scripts/process_stoch_202602yf.R diff --git a/R/stochastic_graphs.R b/R/stochastic_graphs.R index 1b9e38b..417d19c 100644 --- a/R/stochastic_graphs.R +++ b/R/stochastic_graphs.R @@ -103,10 +103,6 @@ stone_stochastic_graph <- function(base, xaxis <- tolower(xaxis) - if (!isTRUE(tolower(outcome) %in% c("cases", "dalys", "deaths", "yll"))) { - cli::cli_abort("`outcome` must be `cases`, `dalys`, `deaths` or `yll`") - } - outcome <- tolower(outcome) # Fetch the packit data if wanted diff --git a/scripts/process_stoch_202602yf.R b/scripts/process_stoch_202602yf.R new file mode 100644 index 0000000..f203742 --- /dev/null +++ b/scripts/process_stoch_202602yf.R @@ -0,0 +1,27 @@ +base_in_path <- "//wpia-hn2.hpc.dide.ic.ac.uk/vimc_stochastics_dropbox/latest/202602yf" +base_out_path <- "//wpia-hn2.hpc.dide.ic.ac.uk/vimc_stochastics/2026202yf" + +############### +# YF + +scenarios = c("yf-no-vaccination", "yf-routine-default", + "yf-routine-campaign-default") + +stoner::stone_stochastic_standardise( + group = "IC-Gaythorpe", + in_path = file.path(base_in_path, "YF-IC-Gaythorpe"), + out_path = file.path(base_out_path, "YF_IC-Gaythorpe"), + scenarios = scenarios, + files = c("burden_results_stochastic_2026_updates_01_novacc_:index.csv.xz", + "burden_results_stochastic_2026_updates_02_default_routine_:index.csv.xz", + "burden_results_stochastic_2026_updates_03_default_routine_campaign_:index.csv.xz"), + index = 1:200) + +stoner::stone_stochastic_standardise( + group = "UND-Perkins", + in_path = file.path(base_in_path, "YF-UND-Perkins"), + out_path = file.path(base_out_path, "YF_UND-Perkins"), + scenarios = "yf-routine-campaign-default", + files = c("stochastic_burden_est_YF_UND-Perkins_yf-scenario-1-default-Routine_and_Campaign_:index.csv.xz"), + index = 1:200) + From 1ebd4461e79814140281a89971ae640d84ece95d Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Fri, 1 May 2026 20:13:26 +0100 Subject: [PATCH 09/14] Fix trailing comma in 2023 script --- scripts/process_stoch_202310gavi.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/process_stoch_202310gavi.R b/scripts/process_stoch_202310gavi.R index b2eaaef..49349bd 100644 --- a/scripts/process_stoch_202310gavi.R +++ b/scripts/process_stoch_202310gavi.R @@ -323,7 +323,7 @@ stoner::stone_stochastic_standardise( scenarios = c("malaria-no-vaccination", "malaria-rts3-default", "malaria-rts3-rts4-default", - "malaria-rts3-bluesky", "malaria-rts3-rts4-bluesky"), + "malaria-rts3-bluesky", "malaria-rts3-rts4-bluesky") stoner::stone_stochastic_standardise( group = "UAC-Kakai", From 5f1facedfa0182b0fa4369f70599c631c7cc4975 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Fri, 1 May 2026 20:58:12 +0100 Subject: [PATCH 10/14] Fix packet tests --- R/stochastic_graphs.R | 30 ++++++++++++++----- tests/testthat/test_stochastic_graphs.R | 39 ++----------------------- 2 files changed, 25 insertions(+), 44 deletions(-) diff --git a/R/stochastic_graphs.R b/R/stochastic_graphs.R index 417d19c..7a2d7b8 100644 --- a/R/stochastic_graphs.R +++ b/R/stochastic_graphs.R @@ -102,9 +102,13 @@ stone_stochastic_graph <- function(base, } xaxis <- tolower(xaxis) - outcome <- tolower(outcome) + miny <- Inf + maxy <- -Inf + minx <- Inf + maxx <- -Inf + # Fetch the packit data if wanted if (!is.null(packit_id)) { @@ -112,8 +116,14 @@ stone_stochastic_graph <- function(base, country, scenarios, outcome, by_cohort) if (xaxis == "age") { central <- aggregate_by_age(central, outcome, filter) + minx <- 0 + maxx <- 100 } else { central <- aggregate_by_year(central, outcome, filter) + miny <- min(central[[outcome]]) + maxy <- max(central[[outcome]]) + minx <- min(central$year) + maxx <- max(central$year) } } @@ -131,11 +141,6 @@ stone_stochastic_graph <- function(base, # Filter and aggregate the data, finding bounds - miny <- Inf - maxy <- -Inf - minx <- Inf - maxx <- -Inf - for (i in 1:n_graphs) { if (xaxis == "age") { data[[i]] <- aggregate_by_age(data[[i]], outcome, filter) @@ -164,7 +169,7 @@ stone_stochastic_graph <- function(base, } if (length(scenarios) == 2) { - outcome_ylab <- paste(outcome_ylab, "avered") + outcome_ylab <- paste(outcome_ylab, "averted") scenario_title <- sprintf("Difference of %s ->\n%s", scenarios[1], scenarios[2]) } @@ -226,6 +231,12 @@ stone_stochastic_graph <- function(base, lines(x = avgs[[xfield]], y = avgs$q95, col = "#202020", lwd = 2) } } + + if (!is.null(packit_id)) { + lines(x = central[[xfield]], y = central[[outcome]], + col = "#2020ff", lwd = 2) + } + res[[i]] <- recordPlot() } res @@ -300,7 +311,10 @@ get_packit_data <- function(packit_id, packit_file, d <- d[order(d$year, d$age), ] data[[s]] <- d } - get_burden_difference(data, outcome) + d <- get_burden_difference(data, outcome) + d <- as.data.frame(d[, c("year", "age", outcome)]) + d$run_id <- 1 + d } ##' Launch a Shiny app to allow interactive plotting of diff --git a/tests/testthat/test_stochastic_graphs.R b/tests/testthat/test_stochastic_graphs.R index ca167cd..0496eb5 100644 --- a/tests/testthat/test_stochastic_graphs.R +++ b/tests/testthat/test_stochastic_graphs.R @@ -79,7 +79,8 @@ test_that("stochastic_graph data transforms", { # Packit gets called if needed - fake_result <- mockery::mock("fake_result") + fake_data <- data.frame(year = 2000, age = 10, deaths = 25, run_id = 1) + fake_result <- mockery::mock(fake_data) mockery::stub(stone_stochastic_graph, "get_packit_data", fake_result) expect_no_error(stone_stochastic_graph( @@ -89,7 +90,7 @@ test_that("stochastic_graph data transforms", { mockery::expect_called(fake_result, 1) mockery::expect_args(fake_result, 1, "123", "file.csv", country, - scenario, "deaths", NULL, FALSE) + scenario, "deaths", FALSE) }) @@ -128,37 +129,3 @@ test_that("Filter formats are reasonable", { expect_equal(filter_string(c(5,4,3,2,1,5,4,3,2,1), "ages"), "ages 1..5") expect_equal(filter_string(c(2,4,6,8), "potatoes"), "selected potatoes") }) - -test_that("Parsing central from packit works", { - # Packit gets called if needed - - fake <- data.frame( - scenario_type = "RSV-rout", scenario = "RSV-rout", - year = c(rep(2000, 4), rep(2001, 4), rep(2000, 4), rep(2001, 4)), - age = c(rep(0, 8), rep(1, 8)), - country = "RFP", - burden_outcome = rep(c("cases", "dalys", "deaths", "yll"), 2), - value = 1:16) - - rds <- tempfile(fileext = ".rds") - saveRDS(fake, rds) - - fetch_fake <- function(id, file) rds - mockery::stub(get_packit_data, "fetch_packit", fetch_fake) - - res <- prepare_central_data("123", "file.csv", - "RFP", "RSV-rout", "deaths", 0:5, TRUE) - - # Data in for death is: (year, age, deaths) - # 2000, 0, 3 - # 2000, 1, 11 - # 2001, 0, 7 - # 2001, 1, 15 - # For cohort - this should become... - # 1999, 11 (2000 year 1, were born in 1999) - # 2000, 18 (2000 year 0, and 2001 year 1 born in 2000) - # 2001, 7 (2001 year 0) - - expect_true(all.equal(res$year, c(1999, 2000, 2001))) - expect_true(all.equal(res$deaths, c(11, 18, 7))) -}) From ac7cbb79221a16800ed7364e347b131b57d302ca Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Sat, 2 May 2026 17:07:50 +0100 Subject: [PATCH 11/14] Fix tests --- tests/testthat/test_stochastic_files.R | 32 +++++++ tests/testthat/test_stochastic_graphs.R | 109 +++++++++++++++++++++++- 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test_stochastic_files.R b/tests/testthat/test_stochastic_files.R index ac19da8..c7b92a2 100644 --- a/tests/testthat/test_stochastic_files.R +++ b/tests/testthat/test_stochastic_files.R @@ -330,3 +330,35 @@ test_that("Different file count per scenario is handled", { expect_true("north_pole_lurgy_fatalistic_NOR.pq" %in% files) expect_true("north_pole_lurgy_fatalistic_LAP.pq" %in% files) }) + +test_that("Can produce metadata", { + tmpout <- file.path(tempdir(), "base") + d <- fake_data() + dir.create(file.path(tmpout, "T1", "elf-piles_G1"), recursive = TRUE) + dir.create(file.path(tmpout, "T2", "elf-piles_G2"), recursive = TRUE) + arrow::write_parquet(d, file.path(tmpout, "T1", "elf-piles_G1", "G1_S1_LAP.pq")) + d$country <- "NPL" + arrow::write_parquet(d, file.path(tmpout, "T1", "elf-piles_G1", "G1_S1_NPL.pq")) + d$yll <- NULL + d$potatoes <- sample(nrow(d)) + arrow::write_parquet(d, file.path(tmpout, "T2", "elf-piles_G2", "G1_S1_ZAP.pq")) + arrow::write_parquet(d, file.path(tmpout, "T2", "elf-piles_G2", "G1_S1_ZIP.pq")) + stone_stochastic_make_meta(tmpout) + + expect_true(file.exists(file.path(tmpout, "meta.csv"))) + meta <- read.csv(file.path(tmpout, "meta.csv")) + expect_equal(nrow(meta), 2) + expect_true("T1" %in% meta$touchstone) + t1 <- meta[meta$touchstone %in% "T1", ] + expect_equal(t1$group, "G1") + expect_equal(t1$scenario, "S1") + expect_equal(t1$countries, "LAP;NPL") + expect_equal(t1$outcomes, "cases;dalys;deaths;yll") + expect_true("T2" %in% meta$touchstone) + t2 <- meta[meta$touchstone %in% "T2", ] + expect_equal(t2$group, "G2") + expect_equal(t2$scenario, "S1") + expect_equal(t2$countries, "ZAP;ZIP") + expect_equal(t2$outcomes, "cases;dalys;deaths;potatoes") +}) + diff --git a/tests/testthat/test_stochastic_graphs.R b/tests/testthat/test_stochastic_graphs.R index 0496eb5..4ab224e 100644 --- a/tests/testthat/test_stochastic_graphs.R +++ b/tests/testthat/test_stochastic_graphs.R @@ -63,7 +63,23 @@ test_that("stochastic_graph data transforms", { expect_equal(min(res2$year), min(data$year) - max(data$age)) expect_equal(max(res2$year), max(data$year) - min(data$age)) - # Test graph - we can't really, but just check it doesn't crash. + # Aggregate by age, for all years + + res2 <- aggregate_by_age(data, "deaths", NULL) + expect_equal(nrow(res2), 25) + expect_equal(res2$deaths[res2$run_id == 2 & res2$age == 10], + sum(data$deaths[data$age == 10 & data$run_id == 2])) + + # Aggregate by age, for some years + + res2 <- aggregate_by_age(data, "deaths", c(2002, 2003)) + expect_equal(nrow(res2), 25) + expect_equal(res2$deaths[res2$run_id == 2 & res2$age == 10], + sum(data$deaths[data$age == 10 & data$run_id == 2 & + data$year %in% c(2002, 2003)])) + + # Test graph - we can't really, but just check it doesn't crash; we've + # already tested the functions being called. expect_no_error(stone_stochastic_graph( base, touchstone, disease, group, country, @@ -80,7 +96,7 @@ test_that("stochastic_graph data transforms", { # Packit gets called if needed fake_data <- data.frame(year = 2000, age = 10, deaths = 25, run_id = 1) - fake_result <- mockery::mock(fake_data) + fake_result <- mockery::mock(fake_data, cycle = TRUE) mockery::stub(stone_stochastic_graph, "get_packit_data", fake_result) expect_no_error(stone_stochastic_graph( @@ -92,8 +108,20 @@ test_that("stochastic_graph data transforms", { mockery::expect_args(fake_result, 1, "123", "file.csv", country, scenario, "deaths", FALSE) + expect_no_error(stone_stochastic_graph( + base, touchstone, disease, group, country, + scenario, "deaths", xaxis = "age")) + + expect_no_error(stone_stochastic_graph( + base, touchstone, disease, group, country, + scenario, "deaths", xaxis = "age", + packit_id = "123", + packit_file = "file.csv")) + + }) + test_that("stochastic_explorer data_dir handling", { expect_error(stochastic_explorer(file.path(tempdir(), "potato", "salad")), "Cannot access the path/mount") @@ -129,3 +157,80 @@ test_that("Filter formats are reasonable", { expect_equal(filter_string(c(5,4,3,2,1,5,4,3,2,1), "ages"), "ages 1..5") expect_equal(filter_string(c(2,4,6,8), "potatoes"), "selected potatoes") }) + +test_that("Arguments are tested", { + + expect_error(stone_stochastic_graph( + "b", c("T1", "T2", "T3"), "d", "g", "c", "s", "o"), + "Only specify one or two touchstones") + + expect_error(stone_stochastic_graph( + "b", NULL, "d", "g", "c", "s", "o"), + "Only specify one or two touchstones") + + expect_error(stone_stochastic_graph( + "b", "t", "d", NULL, "c", "s", "o"), + "Only specify one or two modelling groups") + + expect_error(stone_stochastic_graph( + "b", "t", "d", c("g1", "g2", "g3"), "c", "s", "o"), + "Only specify one or two modelling groups") + + expect_error(stone_stochastic_graph( + "b", "t", "d", "g", "c", NULL, "o"), + "Only specify one or two scenarios") + + expect_error(stone_stochastic_graph( + "b", "t", "d", "g", "c", c("s1", "s2", "s3"), "o"), + "Only specify one or two scenarios") + + expect_error(stone_stochastic_graph( + "b", c("T1", "T2"), "d", c("g1", "g2"), "c", "s", "o"), + "Only one of `touchstones` or `groups` can be plural") + + expect_error(stone_stochastic_graph( + "b", c("T1", "T2"), "d", c("g1", "g2"), "c", c("s1", "s2"), "o"), + "Only one of `touchstones` or `groups` can be plural") + + expect_error(stone_stochastic_graph( + "b", c("T1", "T2"), "d", "g", "c", c("s1", "s2"), "o", xaxis = "potato"), + "`xaxis` must be either `time` or `age`") + + expect_error(stone_stochastic_graph( + "b", c("T1", "T2"), "d", "g", "c", c("s1", "s2"), "o", xaxis = "age"), + "Couldn't find file") +}) + + +test_that("Packit data is arranged correctly", { + fake <- data.frame( + scenario_type = "RSV-rout", scenario = "RSV-rout", + year = c(rep(2000, 4), rep(2001, 4), rep(2000, 4), rep(2001, 4)), + age = c(rep(6, 8), rep(7, 8)), + country = "RFP", + burden_outcome = rep(c("cases", "dalys", "deaths", "yll"), 2), + value = 1:16) + fake2 <- fake + fake2$country <- "POT" + fake2$scenario <- "XYZ-rout" + fake <- rbind(fake, fake2) + + rds <- tempfile(fileext = ".rds") + saveRDS(fake, rds) + + fetch_fake <- function(id, file) rds + mockery::stub(get_packit_data, "fetch_packit", fetch_fake) + + res <- get_packit_data("", "", "RFP", "RSV-rout", "cases", TRUE) + expect_true(unique(res$run_id) == 1) + fake$year <- fake$year - fake$age + fake <- fake[fake$country == "RFP", ] + fake <- fake[fake$scenario == "RSV-rout", ] + fake <- fake[fake$burden_outcome == "cases", ] + fake <- fake[order(fake$year, fake$age), ] + res <- res[order(res$year, res$age), ] + expect_equal(nrow(fake), nrow(res)) + expect_all_true(fake$year ==res$year) + expect_all_true(fake$age == res$age) + expect_all_true(fake$value == res$cases) +}) From 61dde98edc23589803f31dffd2bde049cec7e484 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Tue, 5 May 2026 12:04:07 +0100 Subject: [PATCH 12/14] Export/document make_meta --- NAMESPACE | 1 + R/stochastic_files.R | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 0fa8961..50b8f44 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,6 +8,7 @@ export(stone_load) export(stone_stochastic_central) export(stone_stochastic_cert_verify) export(stone_stochastic_graph) +export(stone_stochastic_make_meta) export(stone_stochastic_process) export(stone_stochastic_standardise) export(stone_stochastic_upload) diff --git a/R/stochastic_files.R b/R/stochastic_files.R index 64fe58e..b27e578 100644 --- a/R/stochastic_files.R +++ b/R/stochastic_files.R @@ -269,6 +269,26 @@ stone_stochastic_central <- function(base, touchstone, disease, group, +##' Create a `meta.csv` file in the root of the standardised +##' stochastics. The columns contain scalars of `touchstone`, +##' `disease`, `group`, `scenario` - and for each row, a +##' semi-colon-separated lists for `countries` and `outcomes`. +##' This is useful for making the stochastic explorer faster +##' on startup (otherwise it has to sample all of the files +##' each time you run it) - and also it is a good general +##' record of all the stochastic data we have. +##' +##' This does mean that we should re-create the meta data +##' each time we make changes to the standardised stochastic +##' data though. +##' +##' @export +##' @title Produce `meta.csv` summary of the structure and +##' content of a standardised stochastic data folder. +##' @importFrom data.table rbindlist +##' @importFrom utils write.csv +##' @param path The root folder of the stochastic data. + stone_stochastic_make_meta <- function(path) { explore_files <- function(touchstone, folder, disease, group) { From 76ab53c41ccfae7548a6200ab8ee27c1ac87871a Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Tue, 5 May 2026 12:18:35 +0100 Subject: [PATCH 13/14] Add Rd --- man/stone_stochastic_make_meta.Rd | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 man/stone_stochastic_make_meta.Rd diff --git a/man/stone_stochastic_make_meta.Rd b/man/stone_stochastic_make_meta.Rd new file mode 100644 index 0000000..6a4b797 --- /dev/null +++ b/man/stone_stochastic_make_meta.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/stochastic_files.R +\name{stone_stochastic_make_meta} +\alias{stone_stochastic_make_meta} +\title{Produce \code{meta.csv} summary of the structure and +content of a standardised stochastic data folder.} +\usage{ +stone_stochastic_make_meta(path) +} +\arguments{ +\item{path}{The root folder of the stochastic data.} +} +\description{ +Create a \code{meta.csv} file in the root of the standardised +stochastics. The columns contain scalars of \code{touchstone}, +\code{disease}, \code{group}, \code{scenario} - and for each row, a +semi-colon-separated lists for \code{countries} and \code{outcomes}. +This is useful for making the stochastic explorer faster +on startup (otherwise it has to sample all of the files +each time you run it) - and also it is a good general +record of all the stochastic data we have. +} +\details{ +This does mean that we should re-create the meta data +each time we make changes to the standardised stochastic +data though. +} From 09896a5d058760807d1102beceda5cea24d72475 Mon Sep 17 00:00:00 2001 From: Wes Hinsley Date: Wed, 6 May 2026 13:43:06 +0100 Subject: [PATCH 14/14] Update vignette --- scripts/imports.R | 1498 +++++++++++++++++ scripts/process_stoch_201710gavi.R | 4 +- scripts/process_stoch_201910gavi.R | 4 +- scripts/process_stoch_202110gavi.R | 12 +- scripts/process_stoch_202310gavi.R | 2 + scripts/process_stoch_202409malaria.R | 2 + scripts/process_stoch_202602yf.R | 1 + vignettes/Stochastics.Rmd | 489 ++++-- vignettes/figures/stoch_burden_diff.png | Bin 0 -> 16700 bytes vignettes/figures/stoch_byage.png | Bin 0 -> 14790 bytes vignettes/figures/stoch_byage_filter.png | Bin 0 -> 15355 bytes .../figures/stoch_byage_filter_cohort.png | Bin 0 -> 17344 bytes vignettes/figures/stoch_cohort.png | Bin 0 -> 15181 bytes vignettes/figures/stoch_compare_mg_1.png | Bin 0 -> 17474 bytes vignettes/figures/stoch_compare_mg_2.png | Bin 0 -> 8607 bytes vignettes/figures/stoch_compare_ts_1.png | Bin 0 -> 12503 bytes vignettes/figures/stoch_compare_ts_2.png | Bin 0 -> 19676 bytes vignettes/figures/stoch_example_1.png | Bin 27811 -> 20663 bytes vignettes/figures/stoch_explorer.png | Bin 0 -> 178468 bytes vignettes/figures/stoch_packit.png | Bin 0 -> 18479 bytes vignettes/figures/stoch_packit_diff.png | Bin 0 -> 17821 bytes vignettes/figures/stoch_u5.png | Bin 0 -> 23747 bytes 22 files changed, 1884 insertions(+), 128 deletions(-) create mode 100644 scripts/imports.R create mode 100644 vignettes/figures/stoch_burden_diff.png create mode 100644 vignettes/figures/stoch_byage.png create mode 100644 vignettes/figures/stoch_byage_filter.png create mode 100644 vignettes/figures/stoch_byage_filter_cohort.png create mode 100644 vignettes/figures/stoch_cohort.png create mode 100644 vignettes/figures/stoch_compare_mg_1.png create mode 100644 vignettes/figures/stoch_compare_mg_2.png create mode 100644 vignettes/figures/stoch_compare_ts_1.png create mode 100644 vignettes/figures/stoch_compare_ts_2.png create mode 100644 vignettes/figures/stoch_explorer.png create mode 100644 vignettes/figures/stoch_packit.png create mode 100644 vignettes/figures/stoch_packit_diff.png create mode 100644 vignettes/figures/stoch_u5.png diff --git a/scripts/imports.R b/scripts/imports.R new file mode 100644 index 0000000..89472c8 --- /dev/null +++ b/scripts/imports.R @@ -0,0 +1,1498 @@ +get_con <- function() { + vault <- vaultr::vault_client(login = "github") + password <- vault$read("/secret/vimc/database/production/users/readonly")$password + DBI::dbConnect(RPostgres::Postgres(), + dbname = "montagu", + host = "montagu.vaccineimpact.org", + port = 5432, password = password, + user = "readonly") +} + +get_annex <- function() { + vault <- vaultr::vault_client(login = "github") + password <- vault$read("/secret/vimc/annex/users/vimc")$password + DBI::dbConnect(RPostgres::Postgres(), + dbname = "montagu", + host = "annex.montagu.dide.ic.ac.uk", + port = 15432, + password = password, + user = "vimc") + +} + + +upload4 <- function(out_path, modelling_group, disease, touchstone, + con, annex) { + stub <- file.path(out_path, sprintf("%s_%s_", modelling_group, disease)) + + stoner::stone_stochastic_upload( + paste0(stub, "calendar.qs"), + con, annex, modelling_group, disease, touchstone, + is_cohort = FALSE, is_under5 = FALSE) + + stoner::stone_stochastic_upload( + paste0(stub, "calendar_u5.qs"), + con, annex, modelling_group, disease, touchstone, + is_cohort = FALSE, is_under5 = TRUE) + + stoner::stone_stochastic_upload( + paste0(stub, "cohort.qs"), + con, annex, modelling_group, disease, touchstone, + is_cohort = TRUE, is_under5 = FALSE) + + stoner::stone_stochastic_upload( + paste0(stub, "cohort_u5.qs"), + con, annex, modelling_group, disease, touchstone, + is_cohort = TRUE, is_under5 = TRUE) +} + +do_stochastics_2024 <- function() { + con <- get_con() + annex <- get_annex() + in_path <- "D:/Dropbox (SPH Imperial College)/File requests/latest/202409malaria-1/" + out_path <- "D:/stochastic_2024/" + + # UAC-Kakai + + stoner::stone_stochastic_process(con, "UAC-Glele_Kakai", "Malaria", "202409malaria-1", + c("malaria-no-vaccination", + "malaria-r3-r4-default", "malaria-rts3-rts4-default"), + file.path(in_path, "Malaria-UAC-Kakai"), + c("Stochastic_Burden_Estimates_Glele_Kakai_No_Vaccine_:index.csv.xz", + "Stochastic_Burden_Estimates_Glele_Kakai_Default_r34_:index.csv.xz", + "Stochastic_Burden_Estimates_Glele_Kakai_Default_rts34_:index.csv.xz"), + "", 1, 200, out_path, bypass_cert_check = TRUE) + + upload4(out_path, "UAC-Glele_Kakai", "Malaria", "202409malaria-1", con, annex) + + # IC-Okell + in_path <- "D:/Dropbox (SPH Imperial College)/File requests/latest/202409malaria-1/Malaria-IC-Okell/" + out_path <- "D:/stochastic_2024/" + stoner::stone_stochastic_process(con, + "IC-Okell", "Malaria", "202409malaria-1", + c("malaria-no-vaccination", + "malaria-r3-r4-default", "malaria-rts3-rts4-default"), + in_path, + c("stochastic-burden-est-no-vaccination_:index Lydia Haile.csv.xz", + "stochastic-burden-est-malaria-r3-r4-default_:index Lydia Haile.csv.xz", + "stochastic-burden-est-malaria-rts3-rts4-default_:index Lydia Haile.csv.xz"), + "", 1, 200, out_path, bypass_cert_check = TRUE) + + upload4(out_path, "IC-Okell", "Malaria", "202409malaria-1", con, annex) + + # TKI-Penny + + in_path <- "D:/Dropbox (SPH Imperial College)/File requests/latest/202409malaria-1/Malaria-TKI-Penny/" + + stoner::stone_stochastic_process(con, + "TKI-Penny", "Malaria", "202409malaria-1", + c("malaria-no-vaccination", + "malaria-r3-r4-default", + "malaria-rts3-rts4-default"), + in_path, + c("stochastic_burden_est_malaria_:index_novaccine Josephine Malinga.csv.xz", + "stochastic_burden_est_malaria_:index_r21_d4_default Josephine Maasd linga.csv.xz", + "stochastic_burden_est_malaria_:index_rtss_d4_default Josephine asd fgMalinga.csv.xz"), + "", 1, 31, out_path, bypass_cert_check = TRUE) + + upload4(out_path, "TKI-Penny", "Malaria", "202409malaria-1", con, annex) + +} + + +do_stochastics_2023 <- function() { + con <- get_con() + annex <- get_annex() + in_path <- "D:/Dropbox (SPH Imperial College)/File requests/latest/202310gavi/" + out_path <- "D:/stochastic_2023/" + + ###################### HepB ######################################### + + stoner::stone_stochastic_process(con, "Li", "HepB", "202310gavi-4", + c( "hepb-hepb3-bd-bluesky", + "hepb-hepb3-bd-default", + "hepb-hepb3-bd-ia2030", + "hepb-hepb3-bluesky", + "hepb-hepb3-default", + "hepb-hepb3-ia2030", + "hepb-no-vaccination" + ), + file.path(in_path, "HepB-Li"), + paste0(":scenario:index.csv.xz"), + "cert156", 1, 200, out_path, NULL, + "deaths", "cases", "dalys") + + upload4(out_path, "Li", "HepB", "202310gavi-4", con, annex) + + stoner::stone_stochastic_process( + con, "IC-Hallett", "HepB", "202310gavi-4", + c( "hepb-hepb3-bd-bluesky", + "hepb-hepb3-bd-default", + "hepb-hepb3-bd-ia2030", + "hepb-hepb3-bluesky", + "hepb-hepb3-default", + "hepb-hepb3-ia2030", + "hepb-no-vaccination"), + file.path(in_path, "HepB-IC-Hallett"), + c("stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bd_bluesky_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bd_default_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bd_ia2030_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bluesky_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_default_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_ia2030_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_no_vaccination_:index.csv.xz"), + "", 1, 200, out_path, NULL, + "deaths", "cases", "dalys", bypass_cert_check = TRUE ) + + upload4(out_path, "IC-Hallett", "HepB", "202310gavi-4", con, annex) + + + stoner::stone_stochastic_process( + con, "IC-Hallett", "HepB", "202310gavi-4", + c( "hepb-hepb3-bd-bluesky", + "hepb-hepb3-bd-default", + "hepb-hepb3-bd-ia2030", + "hepb-hepb3-bluesky", + "hepb-hepb3-default", + "hepb-hepb3-ia2030", + "hepb-no-vaccination"), + file.path(in_path, "HepB-IC-Hallett"), + c("stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bd_bluesky_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bd_default_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bd_ia2030_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_bluesky_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_default_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_hepb3_ia2030_:index.csv.xz", + "stochastic_burden_est_HepB-IC-Hallett_hepb_no_vaccination_:index.csv.xz"), + "", 1, 200, out_path, NULL, + "deaths", "cases", "dalys", bypass_cert_check = TRUE ) + + upload4(out_path, "IC-Hallett", "HepB", "202310gavi-4", con, annex) + + + + ###################### YF ######################################### + + in_path <- "//fi--didenas1-app/Test/YF" + out_path <- in_path + + stoner::stone_stochastic_process( + con, "IC-Garske", "YF", "202310gavi-4", + c("yf-no-vaccination", + "yf-routine-bluesky", + "yf-routine-campaign-bluesky", + "yf-routine-campaign-default", + "yf-routine-campaign-ia2030", + "yf-routine-default", + "yf-routine-ia2030"), + in_path, + "burden_results_stochastic_202310gavi-3_:scenario Keith Fraser.csv.xz", + "", NA, NA, out_path, outcomes = list(cases = "cases", deaths = "deaths", + dalys = "dalys", yll = "yll"), bypass_cert_check = TRUE ) + + upload4(out_path, "IC-Garske", "YF", "202310gavi-4", con, annex) + + + stoner::stone_stochastic_process( + con, "UND-Perkins", "YF", "202310gavi-7", + c("yf-no-vaccination", + "yf-routine-bluesky", + "yf-routine-campaign-bluesky", + "yf-routine-campaign-default", + "yf-routine-campaign-ia2030", + "yf-routine-default", + "yf-routine-ia2030"), + file.path(in_path, "YF-UND-Perkins"), + c("stochastic_burden_est_YF_UND-Perkins_yf-no_vaccination_:index.csv.xz", + "stochastic_burden_est_YF_UND-Perkins_yf-routine_bluesky_:index.csv.xz", + "stochastic_burden_est_YF_UND-Perkins_yf-routine_campaign_bluesky_:index.csv.xz", + "stochastic_burden_est_YF_UND-Perkins_yf-routine_campaign_default_:index.csv.xz", + "stochastic_burden_est_YF_UND-Perkins_yf-routine_campaign_ia2030_:index.csv.xz", + "stochastic_burden_est_YF_UND-Perkins_yf-routine_default_:index.csv.xz", + "stochastic_burden_est_YF_UND-Perkins_yf-routine_ia2030_:index.csv.xz"), + + "", 1, 200, out_path, outcomes = list(cases = "cases", deaths = "deaths", + dalys = "dalys", yll = "yll"), bypass_cert_check = TRUE ) + + upload4(out_path, "UND-Perkins", "YF", "202310gavi-7", con, annex) + + + + ###################### HPV ######################################### + + stoner::stone_stochastic_process( + con, "Harvard-Sweet", "HPV", "202310gavi-4", + c("hpv-no-vaccination", + "hpv-campaign-default", + "hpv-campaign-bluesky", + "hpv-campaign-routine-bluesky", + "hpv-campaign-routine-default", + "hpv-campaign-default_transition_hpv_1d", + "hpv-campaign-routine-default_transition_hpv_1d", + "hpv-campaign-ia2030", + "hpv-campaign-routine-ia2030"), + file.path(in_path, ""), + c("stochastic-burden-est.coverage_202310gavi-4_:scenario_run_:index Allison Portnoy.csv.xz"), + "", 1, 200, out_path, NULL, + "deaths", "cases", "dalys", "yll", bypass_cert_check = TRUE ) + + upload4(out_path, "Harvard-Sweet", "HPV", "202310gavi-4", con, annex) + + in_path <- "//fi--didenas1-app/Test/hpv-jit" + stoner::stone_stochastic_process( + con, "LSHTM-Jit", "HPV", "202310gavi-7", + c("hpv-no-vaccination", + "hpv-campaign-default", + "hpv-campaign-bluesky", + "hpv-campaign-routine-bluesky", + "hpv-campaign-routine-default", + "hpv-campaign-default_transition_hpv_1d", + "hpv-campaign-routine-default_transition_hpv_1d", + "hpv-campaign-ia2030", + "hpv-campaign-routine-ia2030"), + file.path(in_path, ""), + c("stochastic-burden-novaccination_all_202310gavi-7_hpv-no-vaccination Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-default Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-bluesky Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-routine-bluesky Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-routine-default Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-default_transition_hpv_1d Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-routine-default_transition_hpv_1d Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-ia2030 Kaja Abbas.csv.xz", + "stochastic-burden-vaccination_all_202310gavi-7_hpv-campaign-routine-ia2030 Kaja Abbas.csv.xz"), + "", NA, NA, in_path, NULL, + "deaths", "cases", "dalys", "yll", bypass_cert_check = TRUE ) + + upload4(in_path, "LSHTM-Jit", "HPV", "202310gavi-7", con, annex) + + + ###################### Cholera ######################################### + + in_path <- "D:/Dropbox (SPH Imperial College)/File requests/latest/202310gavi/Cholera-IVI-Kim" + out_path <- "D:/stochastic_2023/" + stoner::stone_stochastic_process(con, + "IVI-Kim", "Cholera", "202310gavi-7", + c("cholera-no-vaccination", "cholera-ocv1-default", "cholera-ocv1-ocv2-default"), + file.path(in_path), + c("stoch_Cholera_novacc_20250704T130601 Jong-Hoon Kim.csv.xz", + "stoch_Cholera_campaign_default_ocv1_20250704T130601 Jong-Hoon Kim.csv.xz", + "stoch_Cholera_campaign_default_ocv12_20250704T130601 Jong-Hoon Kim.csv.xz"), + + "", NA, NA, out_path, NULL, bypass_cert_check = TRUE ) + upload4(out_path, "IVI-Kim", "Cholera", "202310gavi-7", con, annex) + + + in_path <- "K:/jhu-chol" + stoner::stone_stochastic_process(con, + "JHU-Lee", "Cholera", "202310gavi-7", + c("cholera-no-vaccination", "cholera-ocv1-default", "cholera-ocv1-ocv2-default"), + file.path(in_path), + c("stochastic-burden-template.202310gavi-4.Cholera_standard_template.529.no-vaccination Christina Alam.csv.xz", + "stochastic-burden-template.202310gavi-4.Cholera_standard_template.529.ocv1-default_one Christina Alam.csv.xz", + "stochastic-burden-template.202310gavi-4.Cholera_standard_template.529.ocv1-ocv2-default_two Christina Alam.csv.xz"), + "cert172 Christina Alam", NA, NA, out_path, NULL, + "deaths", "cases", "dalys", "yll", bypass_cert_check = TRUE ) + upload4(out_path, "JHU-Lee", "Cholera", "202310gavi-4", con, annex) + + + ###################### COVID ######################################### + + in_path <- "//fi--didenas1-app/Test/covid-liu" + stoner::stone_stochastic_process(con, + "LSHTM-Liu", "COVID", "202310gavi-7", + c("covid-no-vaccination", + "covid-no-vaccination_severe", + "covid-primary-bluesky", + "covid-primary-bluesky_severe", + "covid-primary-booster-bluesky", + "covid-primary-booster-bluesky_severe", + "covid-primary-booster-default", + "covid-primary-booster-default_severe", + "covid-primary-default", + "covid-primary-default_severe"), + in_path, + c("stochastic_burden_est_covid-LSHTM-Liu_covid-no-vaccination Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-no-vaccination_severe Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-bluesky Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-bluesky_severe Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-booster-bluesky Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-booster-bluesky_severe Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-booster-default Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-booster-default_severe Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-default Yang Liu.csv.xz", + "stochastic_burden_est_covid-LSHTM-Liu_covid-primary-default_severe Yang Liu.csv.xz"), + "", NA, NA, in_path, NULL, "deaths", "cases", "dalys", "yll", bypass_cert_check = TRUE) + upload4(in_path, "LSHTM-Liu", "COVID", "202310gavi-7", con, annex) + + + in_path <- "//fi--didenas1-app/Test/cov" + stoner::stone_stochastic_process(con, + "IC-Ghani", "COVID", "202310gavi-7", + c("covid-no-vaccination", + "covid-no-vaccination_severe", + "covid-primary-bluesky", + "covid-primary-bluesky_severe", + "covid-primary-booster-bluesky", + "covid-primary-booster-bluesky_severe", + "covid-primary-booster-default", + "covid-primary-booster-default_severe", + "covid-primary-default", + "covid-primary-default_severe"), + in_path, + c("covid-no-vaccination Gemma Gilani_:index.csv.xz", + "covid-no-vaccination_severe Gemma Gilani_:index.csv.xz", + "covid-primary-bluesky Daniela Olivera_:index.csv.xz", + "covid-primary-bluesky_severe Daniela Olivera_:index.csv.xz", + "covid-primary-booster-bluesky Daniela Olivera_:index.csv.xz", + "covid-primary-booster-bluesky_severe Daniela Olivera_:index.csv.xz", + "covid-primary-booster-default Gemma Gilani_:index.csv.xz", + "covid-primary-booster-default_severe Gemma Gilani_:index.csv.xz", + "covid-primary-default Gemma Gilani_:index.csv.xz", + "covid-primary-default_severe Gemma Gilani_:index.csv.xz"), + "", 1, 200, in_path, NULL, "deaths", "cases", "dalys", "yll", bypass_cert_check = TRUE) + upload4(in_path, "IC-Ghani", "COVID", "202310gavi-7", con, annex) + + + + + ###################### Measles ######################################### + + # Updated June 2025 + + stoner::stone_stochastic_process( + con, + "PSU-Ferrari", "Measles", "202310gavi-7", + c("measles-no-vaccination", + "measles-mcv1-bluesky", + "measles-mcv1-default", + "measles-mcv1-ia2030", + "measles-mcv1-mcv2-bluesky", + "measles-mcv1-mcv2-campaign-bluesky", + "measles-mcv1-mcv2-campaign-default", + "measles-mcv1-mcv2-campaign-default_under5sia", + "measles-mcv1-mcv2-campaign-default_update", + "measles-mcv1-mcv2-campaign-ia2030", + "measles-mcv1-mcv2-default", + "measles-mcv1-mcv2-ia2030"), + file.path(in_path), + c("no-vaccination.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-bluesky.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-default.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-ia2030.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-mcv2-bluesky.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-mcv2-campaign-bluesky.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-mcv2-campaign-default.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "measles-mcv1-mcv2-campaign-default_under5sia_PSU_Ferrari_Stochastic_Runs_Revised_05102025.csv.xz", + "measles-mcv1-mcv2-campaign-default_update_PSU_Ferrari_Stochastic_Runs_Revised_05102025.csv.xz", + "mcv1-mcv2-campaign-ia2030.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-mcv2-default.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz", + "mcv1-mcv2-ia2030.202310gavi-6.Measles_PSU-Ferrari_standard.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + upload4(out_path, "PSU-Ferrari", "Measles", "202310gavi-7", con, annex) + + + stoner::stone_stochastic_process( + con, + "LSHTM-Jit", "Measles", "202310gavi-7", + c("measles-no-vaccination", + "measles-mcv1-bluesky", + "measles-mcv1-default", + "measles-mcv1-ia2030", + "measles-mcv1-mcv2-bluesky", + "measles-mcv1-mcv2-campaign-bluesky", + "measles-mcv1-mcv2-campaign-default", + "measles-mcv1-mcv2-campaign-default_under5sia", + "measles-mcv1-mcv2-campaign-default_update", + "measles-mcv1-mcv2-campaign-ia2030", + "measles-mcv1-mcv2-default", + "measles-mcv1-mcv2-ia2030"), + in_path, + c("stochastic_burden_estimate_measles-LSHTM-Jit-no-vaccination Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-bluesky Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-default Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-ia2030 Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-bluesky Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-campaign-bluesky Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-campaign-default.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-campaign-default_under5sia.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-campaign-default_update.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-campaign-ia2030 Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-default Han Fu.csv.xz", + "stochastic_burden_estimate_measles-LSHTM-Jit-mcv1-mcv2-ia2030 Han Fu.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + upload4(out_path, "LSHTM-Jit", "Measles", "202310gavi-7", con, annex) + + + ###################### Rubella ######################################### + + in_path <- "//fi--didenas1-app/Test/rub-uga" + stoner::stone_stochastic_process(con, + "JHU-Lessler", "Rubella", "202310gavi-7", + c("rubella-no-vaccination", + "rubella-campaign-default", + "rubella-campaign-bluesky", + "rubella-campaign-rcv1-rcv2-bluesky", + "rubella-campaign-rcv1-bluesky", + "rubella-campaign-rcv1-rcv2-default", + "rubella-campaign-rcv1-default", + "rubella-campaign-ia2030", + "rubella-campaign-rcv1-rcv2-ia2030", + "rubella-campaign-rcv1-ia2030"), + in_path, + c("stochastic_burden_est-rubella-no-vaccination_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-default_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-bluesky_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-rcv1-rcv2-bluesky_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-rcv1-bluesky_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-rcv1-rcv2-default_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-rcv1-default_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-ia2030_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-rcv1-rcv2-ia2030_:index Amy Winter.csv.xz", + "stochastic_burden_est-rubella-campaign-rcv1-ia2030_:index Amy Winter.csv.xz"), + "", 1, 12, in_path, NULL, + "rubella_deaths_congenital", + "rubella_cases_congenital", "dalys", "yll", bypass_cert_check = TRUE) + upload4(in_path, "JHU-Lessler", "Rubella", "202310gavi-7", con, annex) + + in_path <- "//fi--didenas1-app/Test/rub-ukhsa" + stoner::stone_stochastic_process(con, + "PHE-Vynnycky", "Rubella", "202310gavi-7", + c("rubella-no-vaccination", + "rubella-campaign-default", + "rubella-campaign-bluesky", + "rubella-campaign-rcv1-rcv2-bluesky", + "rubella-campaign-rcv1-bluesky", + "rubella-campaign-rcv1-rcv2-default", + "rubella-campaign-rcv1-default", + "rubella-campaign-ia2030", + "rubella-campaign-rcv1-rcv2-ia2030", + "rubella-campaign-rcv1-ia2030"), + in_path, + c("imp.c:indexs401.csv.xz", + "stochastic_burden_est_rubella_Vynnycky_rubella-campaign-default_:index.csv.xz", + "stochastic_burden_est_rubella_Vynnycky_rubella-campaign-bluesky_:index.csv.xz", + "Vynnycky-camp-rcv1-rcv2-bluesky_country:index.csv.xz", + "Vynnycky-camp-rcv1-bluesky_country:index.csv.xz", + "stochastic_burden_est_rubella_Vynnycky_rubella-campaign-rcv1-rcv2-default_:index.csv.xz", + "stochastic_burden_est_rubella_Vynnycky_rubella-campaign-rcv1-default_:index.csv.xz", + "stochastic_burden_est_rubella_Vynnycky_rubella-campaign-ia2030_:index.csv.xz", + "Vynnycky-camp-rcv1-rcv2-ia2030_country:index.csv.xz", + "Vynnycky-camp-rcv1-ia2030_country:index.csv.xz"), + "", 1, 117, in_path, NULL, + "rubella_deaths_congenital", + "rubella_cases_congenital", "dalys", "yll", bypass_cert_check = TRUE) + upload4(in_path, "PHE-Vynnycky", "Rubella", "202310gavi-7", con, annex) + + + + ###################### Typhoid ######################################### + + in_path <- "//fi--didenas1-app/Test/ivi-typhoid" + stoner::stone_stochastic_process(con, "IVI-Kim", "Typhoid", "202310gavi-7", + c("typhoid-no-vaccination", + "typhoid-campaign-default", "typhoid-campaign-routine-default", + "typhoid-campaign-bluesky", "typhoid-campaign-routine-bluesky"), + in_path, + c("stoch_Typhoid_novacc_20240314T233526 Jong-Hoon Kim.csv.xz", + "stoch_Typhoid_campaign_default_20240314T233526 Jong-Hoon Kim.csv.xz", + "stoch_Typhoid_campaign_routine_default_20240314T233526 Jong-Hoon Kim.csv.xz", + "stoch_Typhoid_campaign_bluesky_20240314T233526 Jong-Hoon Kim.csv.xz", + "stoch_Typhoid_campaign_routine_bluesky_20240314T233526 Jong-Hoon Kim.csv.xz"), + "", NA, NA, in_path, bypass_cert_check = TRUE) + upload4(in_path, "IVI-Kim", "Typhoid", "202310gavi-7", con, annex) + + + ###################### Malaria R2 ######################################### + + in_path <- "//fi--didenas1-app/Test/uac-malaria" + stoner::stone_stochastic_process(con, "UAC-Glele_Kakai", "Malaria", "202310gavi-7", + c("malaria-no-vaccination", + "malaria-rts3-default", "malaria-rts3-rts4-default", + "malaria-rts3-bluesky", "malaria-rts3-rts4-bluesky" + ), + in_path, + c("Stochastic_Burden_Estimates_Glele_Kakai_No_Vaccine_:index.csv.xz", + "Stochastic_Burden_Estimates_Glele_Kakai_Default_rts3_:index.csv.xz", + "Stochastic_Burden_Estimates_Glele_Kakai_Default_rts3_4_:index.csv.xz", + "stochastic_Burden_Estimates_Glele_Kakai_Blue_Sky_rts3_:index.csv.xz", + "Stochastic_Burden_Estimates_Glele_Kakai_Blue_Sky_rts34_:index.csv.xz"), + "", 1, 200, in_path, bypass_cert_check = TRUE) + + upload4(in_path, "UAC-Glele_Kakai", "Malaria", "202310gavi-7", con, annex) + + in_path <- "//fi--didenas1-app/Test/mal" + stoner::stone_stochastic_process(con, "IC-Okell", "Malaria", "202310gavi-7", + c("malaria-no-vaccination", + "malaria-rts3-default", "malaria-rts3-rts4-default", + "malaria-rts3-bluesky", "malaria-rts3-rts4-bluesky" + ), + in_path, + c("stochastic-burden-est.202310gavi-7.Malaria_IC-Okell_no-vaccination_draw_:index Lydia Haile.csv.xz", + "stochastic-burden-est.202310gavi-7.Malaria_IC-Okell_malaria-rts3-default_draw_:index Lydia Haile.csv.xz", + "stochastic-burden-est.202310gavi-7.Malaria_IC-Okell_malaria-rts3-rts4-default_draw_:index Lydia Haile.csv.xz", + "stochastic-burden-est.202310gavi-7.Malaria_IC-Okell_malaria-rts3-bluesky_draw_:index Lydia Haile.csv.xz", + "stochastic-burden-est.202310gavi-7.Malaria_IC-Okell_malaria-rts3-rts4-bluesky_draw_:index Lydia Haile.csv.xz" + ), + "", 1, 200, in_path, bypass_cert_check = TRUE) + upload4(in_path, "IC-Okell", "Malaria", "202310gavi-7", con, annex) + + + + in_path <- "//fi--didenas1-app/Test/tki" + stoner::stone_stochastic_process(con, "TKI-Penny", "Malaria", "202310gavi-7", + c("malaria-no-vaccination", + "malaria-rts3-default", "malaria-rts3-rts4-default", + "malaria-rts3-bluesky", "malaria-rts3-rts4-bluesky" + ), + in_path, + c("stochastic_burden_est_malaria_:index_novaccine Josephine Malinga.csv.xz", + "stochastic_burden_est_malaria_:index_rtss_d3_default Josephine Malinga.csv.xz", + "stochastic_burden_est_malaria_:index_rtss_d4_default Josephine Malinga.csv.xz", + "stochastic_burden_est_malaria_:index_rtss_d3_bluesky Josephine Malinga.csv.xz", + "stochastic_burden_est_malaria_:index_rtss_d4_bluesky Josephine Malinga.csv.xz"), + "", 1, 31, in_path, bypass_cert_check = TRUE) + upload4(in_path, "TKI-Penny", "Malaria", "202310gavi-7", con, annex) + + + ################################################################ + # MENA + # NB - mena-campaign-default and mena-campaign-ia2030 are identical, + # so same file used below. + + in_path <- "//fi--didenas1-app/Test/MenA" + out_path <- in_path + + + stub <- "stochastic-burden-template202310gavi-7MenA_Cambridge-Trotter_mena-" + stoner::stone_stochastic_process(con, + "Cambridge-Trotter", "MenA", "202310gavi-7", + c("mena-no-vaccination", "mena-campaign-default", "mena-campaign-routine-default", + "mena-campaign-bluesky", "mena-campaign-routine-bluesky", + "mena-campaign-ia2030", "mena-campaign-routine-ia2030"), + in_path, + c(paste0(stub, "novaccination (:index) Andromachi Karachaliou.csv"), + paste0(stub, "campaign-default (:index) Andromachi Karachaliou.csv"), + paste0(stub, "routine-default (:index) Andromachi Karachaliou.csv"), + paste0(stub, "campaign-bluesky (:index) Andromachi Karachaliou.csv"), + paste0(stub, "routine-bluesky (:index) Andromachi Karachaliou.csv"), + paste0(stub, "campaign-default (:index) Andromachi Karachaliou.csv"), + paste0(stub, "routine-ia2030 (:index) Andromachi Karachaliou.csv")), + "", 1, 26, out_path, outcomes = list(cases = "cases", deaths = "deaths", + dalys = "dalys", yll = "yll", cases_cwyx = "cases_cwyx", + deaths_cwyx = "deaths_cwyx", dalys_cwyx = "dalys_cwyx", yll_cwyx = "yll_cwyx"), + bypass_cert_check = TRUE) + upload4(in_path, "Cambridge-Trotter", "MenA", "202310gavi-7", con, annex) + +} + +do_stochastics_2021 <- function(con) { + out_path <- "E:/stochastic_2023/" + in_path <- "E:/Dropbox (SPH Imperial College)/File requests/latest/202110gavi/" + + + stub <- "Andromachi Karachaliou - stochastic-burden.202110gavi-2.MenA_Cambridge-Trotter_" + stone_stochastic_process(con, + "Cambridge-Trotter", "MenA", "202110gavi-3", + c("mena-no-vaccination", "mena-campaign-default", "mena-routine-default", + "mena-booster-default", "mena-campaign-ia2030_target", + "mena-routine-ia2030_target"), + file.path(in_path, "Cambridge-Trotter"), + c(paste0(stub, "no_vaccination_:index.csv.xz"), + paste0(stub, "campaign_default_:index.csv.xz"), + paste0(stub, "routine_default_:index.csv.xz"), + paste0(stub, "booster_:index.csv.xz"), + paste0(stub, "campaign-ia2030_target_:index.csv.xz"), + paste0(stub, "routine-ia2030_target_:index.csv.xz")), + "", 1, 26, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "Aniruddha Deshpande - stochastic_burden_est_lopman_" + stone_stochastic_process(con, + "Emory-Lopman", "Rota", "202110gavi-3", + c("rota-no-vaccination", + "rota-routine-default", + "rota-routine-ia2030_target"), + file.path(in_path, "Emory-Lopman"), + c(paste0(stub, "no_vaccination_2022_01_31.csv.xz"), + paste0(stub, "routine_2022_01_31.csv.xz"), + paste0(stub, "ia2030_target_2022_01_31.csv.xz")), + "", NA, NA, out_path, allow_missing_disease = TRUE, bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "Allison Portnoy - stochastic-burden-est." + stone_stochastic_process(con, + "Harvard-Sweet", "HPV", "202110gavi-3", + c("hpv-no-vaccination", + "hpv-campaign-default", + "hpv-campaign-ia2030_target", + "hpv-routine-default", + "hpv-routine-ia2030_target"), + in_path, + c(paste0(stub, "novacc_run_:index.csv.xz"), + paste0(stub, "coverage_202110gavi-3_hpv-campaign-default_run_:index.csv.xz"), + paste0(stub, "coverage_202110gavi-3_hpv-campaign-ia2030_target_run_:index.csv.xz"), + paste0(stub, "coverage_202110gavi-3_hpv-routine-default_run_:index.csv.xz"), + paste0(stub, "coverage_202110gavi-3_hpv-routine-ia2030_target_run_:index.csv.xz")), + "", 1, 200, out_path, + runid_from_file = TRUE, bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "Keith Fraser - stochastic-burden-estimates.202110gavi-3_YF_IC-Garske_" + stoner::stone_stochastic_process(con, + "IC-Garske", "YF", "202110gavi-3", + c("yf-no-vaccination", + "yf-preventive-default", + "yf-preventive-ia2030_target", + "yf-routine-default", + "yf-routine-ia2030_target"), + file.path(in_path, "IC-Garske-YF"), + paste0(stub, ":scenario_:index.csv.xz"), + "", 1, 200, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + stone_stochastic_process(con, "IVI-Kim", "Typhoid", "202110gavi-3", + c("typhoid-no-vaccination", + "typhoid-campaign-default", "typhoid-campaign-ia2030_target", + "typhoid-routine-default", "typhoid-routine-ia2030_target"), + in_path, + c("Jong-Hoon Kim - stoch_Typhoid_novacc_20211217T1.csv.xz", + "Jong-Hoon Kim - stoch_Typhoid_campaign-default_20211217T1.csv.xz", + "Jong-Hoon Kim - stoch_Typhoid_campaign-ia2030_20211217T1.csv.xz", + "Jong-Hoon Kim - stoch_Typhoid_routine-default_20211217T1.csv.xz", + "Jong-Hoon Kim - stoch_Typhoid_routine-ia2030_20211217T1.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + ##################################################### + + stub <- "stochastic_burden_est_HepB-IC-Hallett_" + stone_stochastic_process(con, + "IC-Hallett", "HepB", "202110gavi-3", + c("hepb-bd-default-hepb-routine-default", + "hepb-bd-routine-default", + "hepb-bd-routine-ia2030_target-hepb-routine-ia2030_target", + "hepb-bd-routine-ia2030_target", + "hepb-hepb-routine-default", + "hepb-hepb-routine-ia2030_target", + "hepb-no-vaccination"), + in_path, + paste0(stub, ":scenario_:index.csv.xz"), + "Margaret de Villiers - cert115", 1, 200, out_path, + "deaths", c("hepb_cases_acute_severe","hepb_cases_comp_cirrh", + "hepb_cases_hcc_no_cirrh"), "dalys") + + ############################################################################# + + stoner::stone_stochastic_process(con, + "IVI-Kim", "Cholera", "202110gavi-3", + c("cholera-no-vaccination", "cholera-campaign-default"), + file.path(in_path, "IVI-Kim-Cholera"), + c("Jong-Hoon Kim - stoch_Cholera_novacc_20211221T00.csv.xz", + "Jong-Hoon Kim - stoch_Cholera_campaign_20211222T212131.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + stone_stochastic_process(con, + "JHU-Lee", "Cholera", "202110gavi-3", + c("cholera-no-vaccination", "cholera-campaign-default"), + file.path(in_path, "JHU-Lee-Cholera"), + c("Kaiyue Zou - no-vaccination.csv.xz", + "Kaiyue Zou - campaign-default.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + + stub <- "Amy Winter - stochastic_burden_est-rubella-" + stone_stochastic_process(con, + "JHU-Lessler", "Rubella", "202110gavi-3", + c("rubella-routine-no-vaccination", + "rubella-campaign-default", + "rubella-rcv1-default", + "rubella-rcv2-default", + "rubella-rcv1-rcv2-default", + "rubella-campaign-ia2030_target", + "rubella-rcv1-ia2030_target", + "rubella-rcv2-ia2030_target", + "rubella-rcv1-rcv2-ia2030_target"), + in_path, + c(paste0(stub, "routine-no-vaccination_:index.csv.xz"), + paste0(stub, "campaign-default_:index.csv.xz"), + paste0(stub, "rcv1-default_:index.csv.xz"), + paste0(stub, "rcv2-default_:index.csv.xz"), + paste0(stub, "rcv1-rcv2-default_:index.csv.xz"), + paste0(stub, "campaign-ia2030_target_:index.csv.xz"), + paste0(stub, "rcv1-ia2030_target_:index.csv.xz"), + paste0(stub, "rcv2-ia2030_target_:index.csv.xz"), + paste0(stub, "rcv1-rcv2-ia2030_target_:index.csv.xz")), + "", 1, 11, out_path, + "rubella_deaths_congenital", + "rubella_cases_congenital", "dalys", bypass_cert_check = TRUE) + + ############################################################################# + + list_params_hib_pcv <- data.frame( + outcome = c("cases_men", "cases_men", "cases_men", "cases_men", "cases_men", + "cases_pneumo", "cases_pneumo", "deaths_men", "deaths_pneumo"), + proportion = c(1, 0.014, 0.045, 0.021, 0.017, 1, 0.06, 1, 1), + average_duration = c(0.04,1000,1000,1000,1000,0.02,1000,1000,1000), + disability_weight = c(0.133, 0.043, 0.027, 0.552, 0.61, 0.051, 0.019, 1, 1) + ) + + hib_scenarios <- c("hib-no-vaccination-LiST", + "hib-routine-default-LiST", + "hib-routine-ia2030_target-LiST") + + stone_stochastic_process(con, + "JHU-Tam", "Hib", "202110gavi-3", + hib_scenarios, + file.path(in_path, "JHU-Tam-Carter-Hib"), ":scenario.csv.xz", + "", NA, NA, out_path, + outcomes <- list(deaths = c("deaths_men", "deaths_pneumo"), + cases = c("cases_men", "cases_pneumo"), + dalys = list_params_hib_pcv), + bypass_cert_check = TRUE) + + # And to sort out DALYs on the centrals: + + for (hib_scenario in hib_scenarios) { + stoner::stoner_dalys_for_db(con, list_params_hib_pcv, "JHU-Tam", + "Hib", "202110gavi-3", hib_scenario, + output_file = file.path(out_path, sprintf("%s_central_dalys.csv", + hib_scenario))) + } + + pcv_scenarios <- c("pcv-no-vaccination-LiST", + "pcv-routine-default-LiST", + "pcv-routine-ia2030_target-LiST") + + stone_stochastic_process(con, + "JHU-Tam", "PCV", "202110gavi-3", + pcv_scenarios, + file.path(in_path, "JHU-Tam-Carter-PCV"), ":scenario.csv.xz", + "", NA, NA, out_path, + deaths = c("deaths_men", "deaths_pneumo"), + cases = c("cases_men", "cases_pneumo"), + dalys = list_params_hib_pcv, + bypass_cert_check = TRUE) + + # And to sort out DALYs on the centrals: + + for (pcv_scenario in pcv_scenarios) { + stoner::stoner_dalys_for_db(con, list_params_hib_pcv, "JHU-Tam", + "PCV", "202110gavi-3", pcv_scenario, + output_file = file.path(out_path, sprintf("%s_central_dalys.csv", + pcv_scenario))) + } + + + list_params_rota <- data.frame( + outcome = c("cases", "deaths"), + proportion = c(1, 1), + average_duration = c(0.01, 1000), + disability_weight = c(0.247, 1) + ) + + rota_scenarios <- c("rota-no-vaccination-LiST", + "rota-routine-default-LiST", + "rota-routine-ia2030_target-LiST") + + stone_stochastic_process(con, + "JHU-Tam", "Rota", "202110gavi-3", + rota_scenarios, + file.path(in_path, "JHU-Tam-Carter-Rota"), ":scenario.csv.xz", + "", NA, NA, out_path, + dalys = list_params_rota, + bypass_cert_check = TRUE) + + + # And to sort out DALYs on the centrals: + + for (rota_scenario in rota_scenarios) { + stoner::stoner_dalys_for_db(con, list_params_rota, "JHU-Tam", + "Rota", "202110gavi-3", rota_scenario, + output_file = file.path(out_path, sprintf("%s_central_dalys.csv", + rota_scenario))) + } + + #################################################################################### + + stub <- "Eric Johnson - " + stone_stochastic_process(con, + "KPW-Jackson", "MenA", "202110gavi-3", + c("mena-booster-default", + "mena-campaign-default", + "mena-campaign-ia2030_target", + "mena-no-vaccination", + "mena-routine-default", + "mena-routine-ia2030_target"), + file.path(in_path, "KPW-Jackson-MenA"), + paste0(stub, ":scenario-202111gavi-2.MenA_KPW-Johnson.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + stoner::stone_stochastic_process(con, + "Li", "HepB", "202110gavi-2", + c( "hepb-bd-default-hepb-routine-default", + "hepb-bd-routine-default", + "hepb-bd-routine-ia2030_target-hepb-routine-ia2030_target", + "hepb-bd-routine-ia2030_target", + "hepb-hepb-routine-default", + "hepb-hepb-routine-ia2030_target", + "hepb-no-vaccination" + + ), + file.path(in_path, "Li"), + paste0(":scenario:index.csv"), + "cert105", 1, 200, out_path, + c("hepb_deaths_acute", "hepb_deaths_total_cirrh", "hepb_deaths_hcc"), + c("hepb_cases_acute_symp", "hepb_cases_fulminant", + "hepb_cases_chronic", "hepb_chronic_symptomatic_in_acute_phase"), + "dalys" + ) + + ############################################################################# + + stub <- "Kaja Abbas - PSA_202110gavi-3_" + stone_stochastic_process(con, + "LSHTM-Clark", "Hib", "202110gavi-3", + c("hib-no-vaccination","hib-routine-default","hib-routine-ia2030_target"), + file.path(in_path, "LSHTM-Clark_Hib"), + c(paste0(stub, ":scenario.csv.xz")), + "Kaja Abbas - hib_cert116", NA, NA, out_path) + + ############################################################################# + + + stub <- "Kaja Abbas - PSA_202110gavi-3_" + stone_stochastic_process(con, + "LSHTM-Clark", "Rota", "202110gavi-3", + c("rota-no-vaccination","rota-routine-default","rota-routine-ia2030_target"), + file.path(in_path, "LSHTM-Clark_Rota"), + c(paste0(stub, ":scenario.csv.xz")), + "Kaja Abbas - rota_cert117", NA, NA, out_path) + + ############################################################################# + + + stub <- "stochastic-burden-" + stoner::stone_stochastic_process(con, + "LSHTM-Jit", "HPV", "202110gavi-3", + c("hpv-no-vaccination", + "hpv-campaign-default", + "hpv-routine-default", + "hpv-campaign-ia2030_target", + "hpv-routine-ia2030_target"), + "E:/Dropbox (SPH Imperial College)/File requests/latest/202110gavi/LSHTM-Jit_HPV", + c(paste0(stub, "novaccination_all_202110gavi-3_hpv-no-vaccination.csv.xz"), + paste0(stub, "vaccination_all_202110gavi-3_hpv-campaign-default.csv.xz"), + paste0(stub, "vaccination_all_202110gavi-3_hpv-routine-default.csv.xz"), + paste0(stub, "vaccination_all_202110gavi-3_hpv-campaign-ia2030_target.csv.xz"), + paste0(stub, "vaccination_all_202110gavi-3_hpv-routine-ia2030_target.csv.xz")), + "cert104", NA, NA, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "Han Fu - stochastic_burden_estimate_measles-LSHTM-Jit-" + stone_stochastic_process(con, "LSHTM-Jit", "Measles", "202110gavi-2", + c("measles-no-vaccination", + "measles-campaign-default", + "measles-campaign-only-default", + "measles-mcv1-default", + "measles-mcv2-default", + "measles-campaign-ia2030_target", + "measles-campaign-only-ia2030_target", + "measles-mcv1-ia2030_target", + "measles-mcv2-ia2030_target"), + file.path(in_path, "LSHTM-Jit_Measles"), + c(paste0(stub, "no-vaccination.csv.xz"), + paste0(stub, "campaign-default.csv.xz"), + paste0(stub, "campaign-only-default.csv.xz"), + paste0(stub, "mcv1-default.csv.xz"), + paste0(stub, "mcv2-default.csv.xz"), + paste0(stub, "campaign-ia2030_target.csv.xz"), + paste0(stub, "campaign-only-ia2030_target.csv.xz"), + paste0(stub, "mcv1-ia2030_target.csv.xz"), + paste0(stub, "mcv2-ia2030_target.csv.xz")), + "Han Fu - cert107", NA, NA, out_path) + + ############################################################################# + + stub <- "stochastic_burden_est_" + stone_stochastic_process(con, + "NUS-Chen", "PCV", "202110gavi-3", + c("pcv-no-vaccination","pcv-routine-default","pcv-routine-ia2030_target"), + file.path(in_path, "LSHTM-NUS-Chen_PCV"), + paste0(stub, ":scenario.csv.xz"), + "Jemima Koh - cert121", NA, NA, "E:/Stochastic_2021") + + + + stoner::stone_stochastic_process(con, + "PHE-Vynnycky", "Rubella", "202110gavi-3", + c("rubella-routine-no-vaccination", + "rubella-campaign-default", + "rubella-rcv1-default", + "rubella-rcv2-default", + "rubella-rcv1-rcv2-default", + "rubella-campaign-ia2030_target", + "rubella-rcv1-ia2030_target", + "rubella-rcv2-ia2030_target", + "rubella-rcv1-rcv2-ia2030_target"), + file.path(in_path, "PHE-Vynnycky_Rubella"), + c("VIMC_NV_RCV1RCV2Camp_country:index.csv.xz", + "VIMC_DF_Camp_country:index.csv.xz", + "VIMC_DF_RCV1Camp_country:index.csv.xz", + "VIMC_DF_RCV1RCV2Camp_country:index.csv.xz", + "VIMC_DF_RCV1RCV2_country:index.csv.xz", + "VIMC_IA_Camp_country:index.csv.xz", + "VIMC_IA_RCV1Camp_country:index.csv.xz", + "VIMC_IA_RCV1RCV2Camp_country:index.csv.xz", + "VIMC_IA_RCV1RCV2_country:index.csv.xz"), + "", 1, 112, out_path, + "rubella_deaths_congenital", + "rubella_cases_congenital", "dalys", bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "Sean Moore - stochastic_burden_est_JE_UND-Moore_" + stone_stochastic_process(con, + "UND-Moore", "JE", "202110gavi-2", + c("je-routine-no-vaccination", + "je-campaign-default", + "je-routine-default", + "je-campaign-ia2030_target", + "je-routine-ia2030_target" + ), + file.path(in_path, "UND-Moore-JE"), + paste0(stub, ":scenario.csv.xz"), + "Sean Moore - cert108", NA, NA, out_path) + + stub <- "stochastic_burden_est_YF_UND-Perkins_" + stoner::stone_stochastic_process(con, + "UND-Perkins", "YF", "202110gavi-3", + c("yf-no-vaccination", + "yf-preventive-default", + "yf-preventive-ia2030_target", + "yf-routine-default", + "yf-routine-ia2030_target"), + file.path(in_path, "UND-Perkins-YF"), + paste0(stub, ":scenario_:index.csv.xz"), + "", 1, 200, out_path, bypass_cert_check = TRUE) + + stub <- "Holly Burrows - stochastic_burden_est_TF-Yale-Burrows" + stone_stochastic_process(con, + "Yale-Pitzer", "Typhoid", "202110gavi-3", + c("typhoid-no-vaccination", + "typhoid-campaign-default", "typhoid-campaign-ia2030_target", + "typhoid-routine-default", "typhoid-routine-ia2030_target"), + file.path(in_path, "Yale-Pitzer-Typhoid"), + c(paste0(stub, "-novacc_202110.csv.xz"), + paste0(stub, "_campaign-default_202110.csv.xz"), + paste0(stub, "_campaign-IA2030_202110.csv.xz"), + paste0(stub, "_routine-default_202110.csv.xz"), + paste0(stub, "_routine-IA2030_202110.csv.xz")), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + + upload4(out_path, "Cambridge-Trotter", "MenA", "202110gavi-3", con, annex) + upload4(out_path, "Emory-Lopman", "Rota", "202110gavi-3", con, annex) + upload4(out_path, "IC-Garske", "YF", "202110gavi-3", con, annex) + upload4(out_path, "IC-Hallett", "HepB", "202110gavi-3", con, annex) + upload4(out_path, "IVI-Kim", "Cholera", "202110gavi-3", con, annex) + upload4(out_path, "IVI-Kim", "Typhoid", "202110gavi-3", con, annex) + upload4(out_path, "JHU-Lee", "Cholera", "202110gavi-3", con, annex) + upload4(out_path, "JHU-Lessler", "Rubella", "202110gavi-3", con, annex) + upload4(out_path, "JHU-Tam", "Hib", "202110gavi-3", con, annex) + upload4(out_path, "JHU-Tam", "PCV", "202110gavi-3", con, annex) + upload4(out_path, "JHU-Tam", "Rota", "202110gavi-3", con, annex) + upload4(out_path, "Li", "HepB", "202110gavi-2", con, annex) + upload4(out_path, "LSHTM-Clark", "Hib", "202110gavi-3", con, annex) + upload4(out_path, "LSHTM-Clark", "Rota", "202110gavi-3", con, annex) + upload4(out_path, "LSHTM-Jit", "HPV", "202110gavi-3", con, annex) + upload4(out_path, "LSHTM-Jit", "Measles", "202110gavi-2", con, annex) + upload4(out_path, "NUS-Chen", "PCV", "202110gavi-3", con, annex) + upload4(out_path, "PHE-Vynnycky", "Rubella", "202110gavi-2", con, annex) + upload4(out_path, "UND-Moore", "JE", "202110gavi-3", con, annex) + upload4(out_path, "UND-Perkins", "YF", "202110gavi-3", con, annex) + upload4(out_path, "Yale-Pitzer", "Typhoid", "202110gavi-3", con, annex) + + + +} + +do_stochastics_2019 <- function() { + in_path <- "E:/Dropbox (SPH Imperial College)/File requests/latest/201910gavi" + out_path <- "E:/Stocastic_2019" + + ############################################################################# + + stub <- "Andromachi Karachaliou - stochastic-burden.201910gavi-4.MenA_Cambridge-Trotter_" + stone_stochastic_process(con, + "Cambridge-Trotter", "MenA", "201910gavi-5", + c("mena-campaign-bestcase", "mena-campaign-default", "mena-no-vaccination", + "mena-routine-bestcase", "mena-routine-default"), + file.path(in_path, "Cambridge-Trotter"), + c(paste0(stub, "campaign-bestcase_:index.csv.xz"), + paste0(stub, "campaign-default_:index.csv.xz"), + paste0(stub, "no-vaccination_:index.csv.xz"), + paste0(stub, "routine-bestcase_:index.csv.xz"), + paste0(stub, "routine-default_:index.csv.xz")), + "cert60", 1, 52, out_path) + + ############################################################################# + + stub <- "Ivane Gamkrelidze - stochastic-burden-template.201910gavi-4.HepB_CDA-Razavi_" + stone_stochastic_process(con, + "CDA-Razavi", "HepB", "201910gavi-5", + c("hepb-bd-default-hepb-routine-default", + "hepb-bd-routine-bestcase-hepb-routine-bestcase", + "hepb-no-vaccination", + "hepb-stop", + "hepb-bd-routine-bestcase", + "hepb-bd-routine-default", + "hepb-hepb-routine-bestcase", + "hepb-hepb-routine-default" + ), + file.path(in_path, "CDA-Razavi"), + c(paste0(stub, "all_:scenario.csv.xz"), + paste0(stub, "all_:scenario.csv.xz"), + paste0(stub, "all_:scenario.csv.xz"), + paste0(stub, "all_:scenario.csv.xz"), + paste0(stub, "bd_:scenario.csv.xz"), + paste0(stub, "bd_:scenario.csv.xz"), + paste0(stub, "non_bd_:scenario.csv.xz"), + paste0(stub, "non_bd_:scenario.csv.xz")), + "Ivane Gamkrelidze - cert68", NA, NA, out_path, + c("hepb_deaths_acute","hepb_deaths_dec_cirrh","hepb_deaths_hcc"), + c("hepb_cases_acute_severe","hepb_cases_dec_cirrh","hepb_cases_hcc"), + "dalys" + ) + + ############################################################################# + + stub <- "Molly Steele - stochastic-burden.201910gavi-4.Rota_Emory-Lopman_" + stone_stochastic_process(con, + "Emory-Lopman", "Rota", "201910gavi-5", + c("rota-no-vaccination", + "rota-routine-bestcase", + "rota-routine-default"), + file.path(in_path, "Emory-Lopman"), + paste0(stub, ":scenario.csv.xz"), + "Molly Steele - cert66", NA, NA, out_path, + allow_missing_disease = TRUE) + + ############################################################################# + + stub <- "stochastic-burden-est.201910gavi-5.HPV_Harvard-Sweet_" + stone_stochastic_process(con, + "Harvard-Sweet", "HPV", "201910gavi-5", + c("hpv-campaign-bestcase", + "hpv-campaign-default", + "hpv-no-vaccination", + "hpv-routine-bestcase", + "hpv-routine-default"), + file.path(in_path, "Harvard-Sweet"), + c(paste0(stub, "campaign-bestcase_run_:index.csv.xz"), + paste0(stub, "campaign-default_run_:index.csv.xz"), + paste0(stub, "novacc_run_:index.csv.xz"), + paste0(stub, "routine-bestcase_run_:index.csv.xz"), + paste0(stub, "routine-default_run_:index.csv.xz")), + "", 1, 200, out_path, + runid_from_file = TRUE, bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "stochastic-burden-estimates.201910gavi-4_YF_IC-Garske_" + stone_stochastic_process(con, + "IC-Garske", "YF", "201910gavi-5", + c("yf-no-vaccination", + "yf-preventive-bestcase", + "yf-preventive-default", + "yf-routine-bestcase", + "yf-routine-default", + "yf-stop"), + file.path(in_path, "IC-Garske2"), + paste0(stub, ":scenario_:index.csv.xz"), + "Katy Gaythorpe - cert62", 1, 200, out_path) + + ############################################################################# + + stub <- "stochastic_burden_est_HepB-IC-Hallett_" + stone_stochastic_process(con, + "IC-Hallett", "HepB", "201910gavi-5", + c("hepb-bd-default-hepb-routine-default", + "hepb-bd-routine-bestcase-hepb-routine-bestcase", + "hepb-no-vaccination", + "hepb-stop", + "hepb-bd-routine-bestcase", + "hepb-bd-routine-default", + "hepb-hepb-routine-bestcase", + "hepb-hepb-routine-default"), + file.path(in_path, "IC-Hallett"), + paste0(stub, ":scenario_:index.csv.xz"), + "Margaret de Villiers - cert73", 1, 200, out_path, + "deaths", c("hepb_cases_acute_severe","hepb_cases_comp_cirrh", + "hepb_cases_hcc_no_cirrh"), "dalys") + + ############################################################################# + + stone_stochastic_process(con, + "IVI-Kim", "Cholera", "201910gavi-5", + c("cholera-no-vaccination", "cholera-campaign-default"), + file.path(in_path, "IVI-Kim-Cholera"), + c("Jong-Hoon Kim - stoch_output_Cholera_novacc_20210902.csv.xz", + "Jong-Hoon Kim - stoch_output_Cholera_campaign_20210902.csv.xz"), + "Jong-Hoon Kim - cert89", NA, NA, out_path) + + ############################################################################# + + stone_stochastic_process(con, + "IVI-Kim", "Typhoid", "201910gavi-5", + c("typhoid-no-vaccination", "typhoid-campaign-default", "typhoid-routine-default"), + file.path(in_path, "IVI-Kim-Typhoid"), + c("Jong-Hoon Kim - stoch_Typhoid_novacc.csv.xz", + "Jong-Hoon Kim - stoch_Typhoid_campaign.csv.xz", + "Jong-Hoon Kim - stoch_Typhoid_routine.csv.xz"), + "Jong-Hoon Kim - cert90", NA, NA, out_path) + + ############################################################################# + + stub <- "Amy Winter - stochastic_burden_est-" + stone_stochastic_process(con, + "JHU-Lessler", "Rubella", "201910gavi-5", + c("rubella-campaign-bestcase", + "rubella-campaign-default", + "rubella-routine-no-vaccination", + "rubella-rcv1-bestcase", + "rubella-rcv1-default", + "rubella-rcv1-rcv2-bestcase", + "rubella-rcv1-rcv2-default", + "rubella-rcv2-bestcase", + "rubella-rcv2-default", + "rubella-stop"), + file.path(in_path, "JHU-Lessler"), + c(rep(paste0(stub, ":scenario_:index.csv.xz"), 2), + paste0(stub, "rubella-no-vaccination_:index.csv.xz"), + rep(paste0(stub, ":scenario_:index.csv.xz"), 7)), + "Amy Winter - cert70", 1, 12, out_path, + "rubella_deaths_congenital", + "rubella_cases_congenital", "dalys") + + ############################################################################# + + stub <- "Michael Jackson - stochastic_burden_est_MenA_KPWA_" + stone_stochastic_process(con, + "KPW-Jackson", "MenA", "201910gavi-5", + c("mena-routine-bestcase", + "mena-routine-default", + "mena-campaign-bestcase", + "mena-campaign-default", + "mena-no-vaccination"), + file.path(in_path, "KPW-Jackson"), + c(paste0(stub, "both_bestcase_:index.csv.xz"), + paste0(stub, "both_default_:index.csv.xz"), + paste0(stub, "campaign_bestcase_:index.csv.xz"), + paste0(stub, "campaign_default_:index.csv.xz"), + paste0(stub, "none_default_:index.csv.xz")), + "cert61", 1, 26, out_path) + + ############################################################################# + + stone_stochastic_process(con, + "JHU-Lee", "Cholera", "201910gavi-5", + c("cholera-no-vaccination", "cholera-campaign-default"), + file.path(in_path, "JHU-Lee"), + c("Kaiyue Zou - stochastic-burden-template.201910gavi-5.Cholera_no-vaccination.csv.xz", + "Kaiyue Zou - stochastic-burden-template.201910gavi-5.Cholera_campaign-default.csv.xz"), + "", NA, NA, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + list_params_hib_pcv <- data_frame( + outcome = c("cases_men", "cases_men", "cases_men", "cases_men", "cases_men", + "cases_pneumo", "cases_pneumo", "deaths_men", "deaths_pneumo"), + proportion = c(1, 0.014, 0.045, 0.021, 0.017, 1, 0.06, 1, 1), + average_duration = c(0.04,1000,1000,1000,1000,0.02,1000,1000,1000), + disability_weight = c(0.133, 0.043, 0.027, 0.552, 0.61, 0.051, 0.019, 1, 1) + ) + + stone_stochastic_process(con, + "JHU-Tam", "Hib", "201910gavi-5", + c("hib-no-vaccination-LiST", "hib-routine-default-LiST", "hib-routine-bestcase-LiST"), + file.path(in_path, "JHU-Tam-Hib"), + c("novac:index.csv.xz", "default:index.csv.xz", "best:index.csv.xz"), + "", 1, 14, out_path, + deaths = c("deaths_men", "deaths_pneumo"), + cases = c("cases_men", "cases_pneumo"), + dalys = list_params_hib_pcv, + bypass_cert_check = TRUE) + + # And to add DALYs to the existing + + stone_stochastic_process(con, + "JHU-Tam", "PCV", "201910gavi-5", + c("pcv-no-vaccination-LiST", "pcv-routine-default-LiST", "pcv-routine-bestcase-LiST"), + file.path(in_path, "JHU-Tam-PCV"), + c("novac:index.csv.xz", "default:index.csv.xz", "best:index.csv.xz"), + "", 1, 14, out_path, + deaths = c("deaths_men", "deaths_pneumo"), + cases = c("cases_men", "cases_pneumo"), + dalys = list_params_hib_pcv, + bypass_cert_check = TRUE) + + list_params_rota <- data_frame( + outcome = c("cases", "deaths"), + proportion = c(1, 1), + average_duration = c(0.01, 1000), + disability_weight = c(0.247, 1) + ) + + stone_stochastic_process(con, + "JHU-Tam", "Rota", "201910gavi-5", + c("rota-no-vaccination-LiST", "rota-routine-default-LiST", "rota-routine-bestcase-LiST"), + file.path(in_path, "JHU-Tam-Rota"), + c("novac:index.csv.xz", "default:index.csv.xz", "best:index.csv.xz"), + "", 1, 14, out_path, + dalys = list_params_rota, + bypass_cert_check = TRUE) + + ############################################################################# + + stone_stochastic_process(con, + "Li", "HepB", "201910gavi-5", + c("hepb-bd-default-hepb-routine-default", + "hepb-bd-routine-bestcase-hepb-routine-bestcase", + "hepb-no-vaccination", + "hepb-stop", + "hepb-bd-routine-bestcase", + "hepb-bd-routine-default", + "hepb-hepb-routine-bestcase", + "hepb-hepb-routine-default" + ), + file.path(in_path, "Li"), + paste0(":scenario:index.csv.xz"), + "cert74", 1, 200, out_path, + c("hepb_deaths_acute", "hepb_deaths_total_cirrh", "hepb_deaths_hcc"), + c("hepb_cases_acute_symp", "hepb_cases_fulminant", + "hepb_cases_chronic", "hepb_chronic_symptomatic_in_acute_phase"), + "dalys" + ) + + ############################################################################# + + stub <- "VIMC_Hib_PSA_" + stone_stochastic_process(con, + "LSHTM-Clark", "Hib", "201910gavi-5", + c("hib-no-vaccination","hib-routine-bestcase","hib-routine-default"), + file.path(in_path, "LSHTM-Clark_Hib"), + c(paste0(stub, "NoVax.csv.xz"), + paste0(stub, "Best.csv.xz"), + paste0(stub, "Default.csv.xz")), + "cert81", NA, NA, out_path) + + stub <- "VIMC_Sp_PSA_" + stone_stochastic_process(con, + "LSHTM-Clark", "PCV", "201910gavi-5", + c("pcv-no-vaccination","pcv-routine-bestcase","pcv-routine-default"), + file.path(in_path, "LSHTM-Clark_PCV"), + c(paste0(stub, "NoVax.csv.xz"), + paste0(stub, "Best.csv.xz"), + paste0(stub, "Default.csv.xz")), + "cert82", NA, NA, "E:/Stochastic_2019") + + stub <- "Hira Tanvir - VIMC_Rota_PSA_" + stone_stochastic_process(con, + "LSHTM-Clark", "Rota", "201910gavi-5", + c("rota-no-vaccination","rota-routine-bestcase","rota-routine-default"), + file.path(in_path, "LSHTM-Clark_Rota"), + c(paste0(stub, "NoVax.csv.xz"), + paste0(stub, "Best.csv.xz"), + paste0(stub, "Default.csv.xz")), + "", NA, NA, "E:/Stochastic_2019") + + ############################################################################# + + stub <- "stochastic-burden-" + stone_stochastic_process(con, + "LSHTM-Jit", "HPV", "201910gavi-5", + c("hpv-campaign-bestcase", + "hpv-campaign-default", + "hpv-no-vaccination", + "hpv-routine-bestcase", + "hpv-routine-default"), + file.path(in_path, "LSHTM-Jit_HPV"), + c(paste0(stub, "vaccination_201910gavi-4_hpv-campaign-bestcase.csv.xz"), + paste0(stub, "vaccination_201910gavi-4_hpv-campaign-default.csv.xz"), + paste0(stub, "novaccination_201910gavi-4_hpv-no-vaccination.csv.xz"), + paste0(stub, "vaccination_201910gavi-4_hpv-routine-bestcase.csv.xz"), + paste0(stub, "vaccination_201910gavi-4_hpv-routine-default.csv.xz")), + "Kaja Abbas - stochastic_parameters_certificate_HPV_LSHTM-Jit_201910gavi-4", + NA, NA, out_path, bypass_cert_check = TRUE) + + ############################################################################# + + stub <- "stochastic_burden_estimate_measles-LSHTM-Jit-" + stone_stochastic_process(con, "LSHTM-Jit", "Measles", "201910gavi-5", + c("measles-no-vaccination", + "measles-campaign-default","measles-campaign-bestcase", + "measles-campaign-only-default","measles-campaign-only-bestcase", + "measles-mcv1-default","measles-mcv1-bestcase", + "measles-mcv2-default","measles-mcv2-bestcase", + "measles-stop"), + file.path(in_path, "LSHTM-Jit_Measles"), + c(paste0(stub, "no-vaccination_Portnoy.csv.xz"), + paste0(stub, "campaign-default_Portnoy.csv.xz"), paste0(stub, "campaign-bestcase_Portnoy.csv.xz"), + paste0(stub, "campaign-only-default_Portnoy.csv.xz"), paste0(stub, "campaign-only-bestcase_Portnoy.csv.xz"), + paste0(stub, "mcv1-default_Portnoy.csv.xz"), paste0(stub, "mcv1-bestcase_Portnoy.csv.xz"), + paste0(stub, "mcv2-default_Portnoy.csv.xz"), paste0(stub, "mcv2-bestcase_Portnoy.csv.xz"), + paste0(stub, "stop_Portnoy.csv.xz")), + "cert83", NA, NA, out_path) + + ############################################################################# + + stub <- "Template_Stochastic_" + stone_stochastic_process(con, + "OUCRU-Clapham", "JE", "201910gavi-5", + c("je-campaign-bestcase", + "je-campaign-default", + "je-routine-no-vaccination", + "je-routine-bestcase", + "je-routine-default"), + file.path(in_path, "OUCRU-Clapham"), + c(paste0(stub, "Campaign_Best4_correcting_:index.csv.xz"), + paste0(stub, "Campaign_Default4_correcting_:index.csv.xz"), + paste0(stub, "Naive4_correcting_:index.csv.xz"), + paste0(stub, "Routine_Best4_correcting_:index.csv.xz"), + paste0(stub, "Routine_Default4_correcting_:index.csv.xz")), + "cert76", 1, 200, out_path) + + ############################################################################# + + stub <- "stochastic_burden_est_" + stone_stochastic_process(con, + "PHE-Vynnycky", "Rubella", "201910gavi-5", + c("rubella-campaign-bestcase", + "rubella-campaign-default", + "rubella-routine-no-vaccination", + "rubella-rcv1-bestcase", + "rubella-rcv1-default", + "rubella-rcv1-rcv2-bestcase", + "rubella-rcv1-rcv2-default", + "rubella-rcv2-bestcase", + "rubella-rcv2-default", + "rubella-stop"), + file.path(in_path, "PHE-Vynnycky"), + paste0(stub, ":scenario_country:index.csv.xz"), + "", 1, 112, out_path, + "rubella_deaths_congenital", + "rubella_cases_congenital", "dalys") + + ############################################################################# + + stub <- "Heather Santos - " + stone_stochastic_process(con, + "PSU-Ferrari", "Measles", "201910gavi-5", + c("measles-no-vaccination", + "measles-mcv1-bestcase", + "measles-mcv2-bestcase", + "measles-campaign-bestcase", + "measles-mcv1-default", + "measles-mcv2-default", + "measles-campaign-default", + "measles-stop", + "measles-campaign-only-bestcase", + "measles-campaign-only-default"), + file.path(in_path, "PSU-Ferrari"), + c(paste0(stub, "novax_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "bestcase_mcv1_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "bestcase_mcv2_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + "stochastic:index_burden_Measles-PSU-Ferrari.csv.xz", + paste0(stub, "default_mcv1_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "default_mcv2_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "default_campaign_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "stop_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "bestcase_campaign_only_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz"), + paste0(stub, "default_campaign_only_stochastic:index_burden_Measles-PSU-Ferrari.csv.xz")), + "Heather Santos - cert80", 1, 8, out_path) + + ############################################################################# + + stub <- "Sean Moore - stochastic_burden_est_JE_UND-Moore_" + stone_stochastic_process(con, + "UND-Moore", "JE", "201910gavi-5", + c("je-campaign-bestcase", + "je-campaign-default", + "je-routine-no-vaccination", + "je-routine-bestcase", + "je-routine-default"), + file.path(in_path, "UND-Moore"), + c(paste0(stub, ":scenario.csv.xz"), + paste0(stub, ":scenario.csv.xz"), + paste0(stub, "je-no-vaccination.csv.xz"), + paste0(stub, ":scenario.csv.xz"), + paste0(stub, ":scenario.csv.xz")), + "Sean Moore - cert58", NA, NA, out_path) + + ############################################################################# + + stub <- "stochastic_burden_est_YF_UND-Perkins_" + stone_stochastic_process(con, + "UND-Perkins", "YF", "201910gavi-5", + c("yf-no-vaccination", + "yf-preventive-bestcase", + "yf-preventive-default", + "yf-routine-bestcase", + "yf-routine-default", + "yf-stop"), + file.path(in_path, "UND-Perkins"), + paste0(stub, ":scenario_:index.csv.xz"), + "John Huber - cert85", 1, 200, out_path) + + ############################################################################# + + stone_stochastic_process(con, + "Yale-Pitzer", "Typhoid", "201910gavi-5", + c("typhoid-no-vaccination", "typhoid-campaign-default", "typhoid-routine-default"), + file.path(in_path, "Yale-Pitzer"), + c("Virginia Pitzer - 2021-02-18 17.00.26 - stochastic_output_TF-Yale-Pitzer_novacc.csv.xz", + "Virginia Pitzer - 2021-02-18 16.58.03 - stochastic_output_TF-Yale-Pitzer_campaign.csv.xz", + "Virginia Pitzer - 2021-02-18 16.59.14 - stochastic_output_TF-Yale-Pitzer_camproutine.csv.xz"), + "Virginia Pitzer - cert88", NA, NA, out_path) + + ############################################################################# + + upload4(out_path, "Cambridge-Trotter", "MenA", "201910gavi-5", con, annex) + upload4(out_path, "CDA-Razavi", "HepB", "201910gavi-5", con, annex) + upload4(out_path, "Emory-Lopman", "Rota", "201910gavi-5", con, annex) + upload4(out_path, "Harvard-Sweet", "HPV", "201910gavi-5", con, annex) + upload4(out_path, "IC-Garske", "YF", "201910gavi-5", con, annex) + upload4(out_path, "IC-Hallett", "HepB", "201910gavi-5", con, annex) + upload4(out_path, "IVI-Kim", "Cholera", "201910gavi-5", con, annex) + upload4(out_path, "IVI-Kim", "Typhoid", "201910gavi-5", con, annex) + upload4(out_path, "KPW-Jackson", "MenA", "201910gavi-5", con, annex) + upload4(out_path, "JHU-Lee", "Cholera", "201910gavi-5", con, annex) + upload4(out_path, "JHU-Tam", "Hib", "201910gavi-5", con, annex) + upload4(out_path, "JHU-Tam", "PCV", "201910gavi-5", con, annex) + upload4(out_path, "JHU-Tam", "Rota", "201910gavi-5", con, annex) + upload4(out_path, "JHU-Lessler", "Rubella", "201910gavi-5", con, annex) + upload4(out_path, "Li", "HepB", "201910gavi-5", con, annex) + upload4(out_path, "LSHTM-Clark", "Hib", "201910gavi-5", con, annex) + upload4(out_path, "LSHTM-Clark", "PCV", "201910gavi-5", con, annex) + upload4(out_path, "LSHTM-Clark", "Rota", "201910gavi-5", con, annex) + upload4(out_path, "LSHTM-Jit", "HPV", "201910gavi-5", con, annex) + upload4(out_path, "LSHTM-Jit", "Measles", "201910gavi-5", con, annex) + upload4(out_path, "OUCRU-Clapham", "JE", "201910gavi-5", con, annex) + upload4(out_path, "PHE-Vynnycky", "Rubella", "201910gavi-5", con, annex) + upload4(out_path, "PSU-Ferrari", "Measles", "201910gavi-5", con, annex) + upload4(out_path, "UND-Moore", "JE", "201910gavi-5", con, annex) + upload4(out_path, "UND-Perkins", "YF", "201910gavi-5", con, annex) + upload4(out_path, "Yale-Pitzer", "Typhoid", "201910gavi-5", con, annex) + +} diff --git a/scripts/process_stoch_201710gavi.R b/scripts/process_stoch_201710gavi.R index bcb89f4..3c2534d 100644 --- a/scripts/process_stoch_201710gavi.R +++ b/scripts/process_stoch_201710gavi.R @@ -22,7 +22,7 @@ fetch_scenarios <- function(disease, touchstone='201710gavi-5') { # Let's unleash the cow setwd("Q:/testcow") -writeLines("vimc/stoner@VIMC-9230", "pkgdepends.txt") +writeLines("vimc/stoner", "pkgdepends.txt") hipercow::hipercow_init(driver = "dide-windows") hipercow::hipercow_provision() # Network/memory might be too much for more than a job per node. @@ -296,3 +296,5 @@ stoner::stone_stochastic_standardise( "John Huber - stochastic_burden_est_YF_UND-Perkins_yf-preventive-gavi_:index.csv.xz", "John Huber - stochastic_burden_est_YF_UND-Perkins_yf-routine-gavi_:index.csv.xz"), index = 1:200)) + +stoner::stone_stochastic_make_meta(base_out_path) diff --git a/scripts/process_stoch_201910gavi.R b/scripts/process_stoch_201910gavi.R index 1ed1318..48e2019 100644 --- a/scripts/process_stoch_201910gavi.R +++ b/scripts/process_stoch_201910gavi.R @@ -22,7 +22,7 @@ fetch_scenarios <- function(disease) { # Let's unleash the cow setwd("Q:/testcow") -writeLines("vimc/stoner@VIMC-9230", "pkgdepends.txt") +writeLines("vimc/stoner", "pkgdepends.txt") hipercow::hipercow_init(driver = "dide-windows") hipercow::hipercow_provision() # Network/memory might be too much for more than a job per node. @@ -417,3 +417,5 @@ stoner::stone_stochastic_standardise( scenarios = scenarios, files = "stochastic_burden_est_YF_UND-Perkins_:scenario_:index.csv.xz", index = 1:200)) + +stoner::stone_stochastic_make_meta(base_out_path) diff --git a/scripts/process_stoch_202110gavi.R b/scripts/process_stoch_202110gavi.R index 210d016..1abb596 100644 --- a/scripts/process_stoch_202110gavi.R +++ b/scripts/process_stoch_202110gavi.R @@ -20,7 +20,7 @@ fetch_scenarios <- function(disease) { # Let's unleash the cow setwd("Q:/testcow") -writeLines("vimc/stoner@VIMC-9230", "pkgdepends.txt") +writeLines("vimc/stoner", "pkgdepends.txt") hipercow::hipercow_init(driver = "dide-windows") hipercow::hipercow_provision() # Network/memory might be too much for more than a job per node. @@ -226,7 +226,6 @@ stoner::stone_stochastic_standardise( sprintf("%s_booster_:index.csv.xz", stub)), index = 1:26)) -hipercow::task_create_expr(resources = hres, expr = stoner::stone_stochastic_standardise( group = "KPW-Jackson", in_path = file.path(base_in_path, "KPW-Jackson-MenA"), @@ -238,7 +237,7 @@ stoner::stone_stochastic_standardise( "stochastic_burden_est_MenA_KPWA_routine_default_:index.csv.xz", "stochastic_burden_est_MenA_KPWA_routine_ia2030_target_:index.csv.xz", "stochastic_burden_est_MenA_KPWA_booster_default_:index.csv.xz"), - index = 1:26)) + index = 1:26) ############### # PCV @@ -377,10 +376,13 @@ stoner::stone_stochastic_standardise( files = sprintf("%s_:scenario_:index.csv.xz", stub), index = 1:200) -stoner::stone_stochastic_standardise( +hipercow::task_create_expr(resources = hres, expr = + stoner::stone_stochastic_standardise( group = "UND-Perkins", in_path = file.path(base_in_path, "UND-Perkins-YF"), out_path = file.path(base_out_path, "YF_UND-Perkins"), scenarios = scenarios, files = "stochastic_burden_est_YF_UND-Perkins_:scenario_:index.csv.xz", - index = 1:200) + index = 1:200)) + +stoner::stone_stochastic_make_meta(base_out_path) diff --git a/scripts/process_stoch_202310gavi.R b/scripts/process_stoch_202310gavi.R index 49349bd..ad320b7 100644 --- a/scripts/process_stoch_202310gavi.R +++ b/scripts/process_stoch_202310gavi.R @@ -360,3 +360,5 @@ stoner::stone_stochastic_standardise( "stochastic_burden_est_malaria_:index_rtss_d3_bluesky Josephine Malinga.csv.xz", "stochastic_burden_est_malaria_:index_rtss_d4_bluesky Josephine Malinga.csv.xz"), index = 1:31) + +stoner::stone_stochastic_make_meta(base_out_path) diff --git a/scripts/process_stoch_202409malaria.R b/scripts/process_stoch_202409malaria.R index c150f99..b17d724 100644 --- a/scripts/process_stoch_202409malaria.R +++ b/scripts/process_stoch_202409malaria.R @@ -35,3 +35,5 @@ stoner::stone_stochastic_standardise( "stochastic_burden_est_malaria_:index_rtss_d4_default Josephine Malinga.csv.xz"), index = 1:31 ) + +stoner::stone_stochastic_make_meta(base_out_path) diff --git a/scripts/process_stoch_202602yf.R b/scripts/process_stoch_202602yf.R index f203742..5e18778 100644 --- a/scripts/process_stoch_202602yf.R +++ b/scripts/process_stoch_202602yf.R @@ -25,3 +25,4 @@ stoner::stone_stochastic_standardise( files = c("stochastic_burden_est_YF_UND-Perkins_yf-scenario-1-default-Routine_and_Campaign_:index.csv.xz"), index = 1:200) +stoner::stone_stochastic_make_meta(base_out_path) diff --git a/vignettes/Stochastics.Rmd b/vignettes/Stochastics.Rmd index 9be11c9..e9b63ec 100644 --- a/vignettes/Stochastics.Rmd +++ b/vignettes/Stochastics.Rmd @@ -5,8 +5,11 @@ date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Stochastics} - %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} + %\VignetteEngine{knitr::rmarkdown} +editor_options: + markdown: + wrap: 80 --- ```{r setup, include = FALSE} @@ -19,59 +22,65 @@ data_frame <- function(...) { data.frame(stringsAsFactors = FALSE, ...) } ``` -## Stochastics -VIMC modelling groups produce stochastic data for us, which is currently -200 runs of their models varying key parameters, to express uncertainty. -These files get uploaded to Box, and the groups are provided with -guidance to produce files that we can process easily. +## Stochastics -Stoner can process these stochastic files into a standard format to -make the files more predictable to work with. For a good compromise -between ease-of-use, performance and compression, we have used the -`Parquet` format and the `arrow` package. This vignette covers -how to process the data, how to create a central from a set of -stochastics, and how to see a graph of the stochastics for quick -diagnosis. +VIMC modelling groups produce stochastic data for us, which is currently 200 +runs of their models varying key parameters, to express uncertainty. These files +get uploaded to Box, and the groups are provided with guidance to produce files +that we can process easily. +Stoner can process these stochastic files into a standard format to make the +files more predictable to work with. For a good compromise between ease-of-use, +performance and compression, we have used the `Parquet` format and the `arrow` +package. This vignette covers how to process the data, how to create a central +from a set of stochastics, how to produce some graphs of the stochastics for +quick diagnosis, and also how to use the stochastic explorer, a local shiny app +for exploring and comparing stochastic datasets. ### Creating standard stochastic files -This has superceded `stoner_stochastic_process`, which will be -removed and undocumented soon. - -Here, we are mostly likely working with two folders or network shares, -one of which contains the stochastics that groups have uploaded, and the -other is a destination where we want to write our standardised files. These -I'll call `in_path` and `out_path`. The `out_path` folders I am assuming -will be only written to by stoner; paths and filenames will adhere to the -following format:- - -* Within the root of the share we will have the **touchstone**, without -a version. At time of writing, we have `202310gavi` and `202409malaria`. -I am not anticipating having separate stochastics for separate versions -of the stochastics at this point. - -* Within those touchstone folders, we have more folders in the form -`Disease_Modelling-Group` - such as `COVID_IC-Ghani` or `YF_IC-Garske`. -The groups and diseases set up here copy the historic contents of the -Montagu database. - -* Within each of these folders, we will be writing a file per -country per scenario, containing all run_ids, named in the format -`Modelling-Group_scenario_country.pq` - for example, -`IC-Garske_yf-routine-ia2030_KEN.pq`. If we also create a -average (central) of the stochastics (see later), that will be called -`IC-Garske_yf-routine-ia2030_central.pq`, containing all the countries, -and no run_id. +This has superseded `stoner_stochastic_process`, which will be removed and +undocumented soon. + +Here, we are mostly likely working with two folders or network shares, one of +which contains the stochastics that groups have uploaded, and the other is a +destination where we want to write our standardised files. These I'll call +`in_path` and `out_path`. The `out_path` folders I am assuming will be only +written to by stoner; paths and filenames will adhere to the following format:- + +- Within the root of the share we will have the **touchstone**, without a + version. At time of writing, we have `202310gavi` and `202409malaria`. I am + not anticipating having separate stochastics for separate versions of the + stochastics at this point. + +- Within those touchstone folders, we have more folders in the form + `Disease_Modelling-Group` - such as `COVID_IC-Ghani` or `YF_IC-Garske`. The + groups and diseases set up here copy the historic contents of the Montagu + database. + +- Within each of these folders, we will be writing a file per country per + scenario, containing all run_ids, named in the format + `Modelling-Group_scenario_country.pq` - for example, + `IC-Garske_yf-routine-ia2030_KEN.pq`. If we also create a average (central) + of the stochastics (see later), that will be called + `IC-Garske_yf-routine-ia2030_central.pq`, containing all the countries, and + no run_id. + +- Finally, in the root of the standardised folder, we'll keep a file + `meta.csv` to describe in one file the range of stochastic data we have by + touchstone, disease, group and scenario, and for each the outcomes and + countries provided. This is useful for speeding up the stochastic explorer + shiny app, but also may be a useful quick reference of our inventory in + general. #### Example Usage - One file per scenario -Examples used so far are in the `scripts` folder in the root of the -stoner repo - files such as `process_stoch_202310gavi.R`. Here is an -example of the Yellow Fever standardisation:- +Examples used so far are in the `scripts` folder in the root of the stoner +repo - files such as `process_stoch_202310gavi.R`. Here is an example of the +Yellow Fever standardisation:- -``` +``` scenarios = c("yf-no-vaccination", "yf-routine-bluesky", "yf-routine-campaign-bluesky", "yf-routine-campaign-default", "yf-routine-campaign-ia2030", "yf-routine-default", @@ -86,27 +95,30 @@ stoner::stone_stochastic_standardise( ``` `base_in_path` and `base_out_path` are defined elsewhere, and are my drive -mappings to the incoming, and destination network shares. We provide the -group name, the paths to the uploaded files (note the original dropbox path -was a little incorrect with its dashes instead of underscores!) - and the -destination path in the format described above. +mappings to the incoming, and destination network shares. We'll briefly talk +about those later. + +We provide the group name, the paths to the uploaded files and the destination +we want to write files to. Note that the `in_path` is not managed, and might be +arbitrary - here it has dashes instead of underscores; but the `out_path`, we +need to be careful to get right, in the format `disease` then underscore then +the modelling group. We then provide the vector of scenarios, and because this group follow the -guidance very well, they have included the exact scenario in the filenames. -We can therefore use the `:scenario` glob in the `files` argument, and -stoner will handle each of the seven scenarios in the right order. +guidance very well, they have included the exact scenario in the filenames. We +can therefore use the `:scenario` glob in the `files` argument, and stoner will +handle each of the seven scenarios in the right order. #### Example Usage - One file per run -Other groups provide a file per run, which is also fine. Here, we can -specify an additional `index` parameter, usually `1:200`, and use the glob -`:index` in the files argument. Here is an example of that - but note for -this group, they didn't _quite_ include the scenarios exactly in the -filenames, so we have to explicitly name the files, rather than using -the `:scenario` glob. This is still yellow fever, so using the same -`scenarios` vector as before. +Other groups provide a file per run, which is also fine. Here, we can specify an +additional `index` parameter, usually `1:200`, and use the glob `:index` in the +files argument. Here is an example of that - but note for this group, they +didn't *quite* include the scenarios exactly in the filenames, so we have to +explicitly name the files, rather than using the `:scenario` glob. This is still +yellow fever, so using the same `scenarios` vector as before. -``` +``` stoner::stone_stochastic_standardise( group = "UND-Perkins", in_path = file.path(base_in_path, "YF-UND-Perkins"), @@ -122,103 +134,338 @@ stoner::stone_stochastic_standardise( index = 1:200) ``` -The subtle difference is that the group used underscores instead of dashes in the scenario -names. So here, I have to be careful and ensure the order of `files` matches the order of -`scenarios` manually. After that, the `:index` glob, and the `index` argument deal with -the fact we have 200 files per scenario. +The subtle difference is that the group used underscores instead of dashes in +the scenario names. So here, I have to be careful and ensure the order of +`files` matches the order of `scenarios` manually. After that, the `:index` +glob, and the `index` argument deal with the fact we have 200 files per +scenario. #### Example Usage - One file per country -If groups want to supply a file per country, then at present, we need them to do so -including the country in their uploads as normal, but number their files using the -`index` method above, rather than having country names or ISO codes in the filename. -We could do better if this proves to be a popular method. +If groups want to supply a file per country, then at present, we need them to do +so including the country in their uploads as normal, but number their files +using the `index` method above, rather than having country names or ISO codes in +the filename. We could do better if this proves to be a popular method. + +### Updating the meta-data + +Having run `stone_stochastic_standardise` and made changes to the structure, to +enable the stochastic explorer shiny app to use the data (and to keep a useful +file up-to-date), we need to run a command to update the metadata. + +``` +stoner::stone_stochastic_make_meta(out_path) +``` + +which will run for a few seconds, recreating the entire file. The columns are +touchstone, disease, group, scenario, countries and outcomes; the first four +will be single elements, whereas countries and outcomes will be semi-colon +separated lists. ### Advanced Usage -You shouldn't have to worry about these things, but this is just so that you know -they are there. We have some historic fixes that have made it possible for us to -process some groups' stochastics with some formatting issues, rather than asking -them to resubmit. These fixes are enabled by default; they won't do anything bad -for groups where the fixes are not relevant, and are strictly required for the -groups where they are relevant. +You shouldn't have to worry about these things, but this is just so that you +know they are there. We have some historic fixes that have made it possible for +us to process some groups' stochastics with some formatting issues, rather than +asking them to resubmit. These fixes are enabled by default; they won't do +anything bad for groups where the fixes are not relevant, and are strictly +required for the groups where they are relevant. #### The Rubella Fix An argument `rubella_fix`, by default `TRUE`, deals with the fact that rubella -groups have traditionally uploaded with different outcome names. Instead of `deaths`, -`rubella_deaths_congenital`, and instead of `cases`, `rubella_cases_congenital`. -Some groups additionally supplied the `rubella_infections` outcome. +groups have traditionally uploaded with different outcome names. Instead of +`deaths`, `rubella_deaths_congenital`, and instead of `cases`, +`rubella_cases_congenital`. Some groups additionally supplied the +`rubella_infections` outcome. -The `rubella_fix` argument causes stoner to omit `rubella_infections`, and rename -the others to `deaths` and `cases` respectively, standardising them against the -other diseases. +The `rubella_fix` argument causes stoner to omit `rubella_infections`, and +rename the others to `deaths` and `cases` respectively, standardising them +against the other diseases. #### The Missing Run Id fix One group also is in the habit of sending us one file per run_id, but omitting -the `run_id` field from their uploads. The argument `missing_run_id_fix`, default -`TRUE` lets Stoner spot this, and specifically if `index` is `1:200` (ie, we are -expecting 200 uploads), it will transplant the index into the file to allow -processing to be done normally. +the `run_id` field from their uploads. The argument `missing_run_id_fix`, +default `TRUE` lets Stoner spot this, and specifically if `index` is `1:200` +(ie, we are expecting 200 uploads), it will transplant the index into the file +to allow processing to be done normally. ### Other Considerations: -* Stoner rounds of all the outcomes, (deaths, cases, dalys, yll and -cohort_size) to be integer-like, which can create some confusion with small -countries with a very small number of cases, but we expect those confusions. -Some groups still provide us with 16 decimal places, which creates some -problems, and the integer-like rounding is in the guidance. +- Stoner rounds of all the outcomes, (deaths, cases, dalys, yll and + cohort_size) to be integer-like, which can create some confusion with small + countries with a very small number of cases, but we expect those confusions. + Some groups still provide us with 16 decimal places, which creates some + problems, and the integer-like rounding is in the guidance. + +- Note that for some groups with very large files (especially those with 16 + decimal places), the processing takes **a lot of memory** - towards 128Gb. + So run these on a large machine or perhaps a cluster node. + +### More about the paths + +If at all possible, you should perform Stoner tasks on a computer within DIDE, +connected with a cable. Stochastic processing involves large volumes of data, +and ZScaler from outside, or even WiFi from inside may not copy that well with +these tasks. -* Note that for some groups with very large files (especially those with -16 decimal places), the processing takes **a lot of memory** - towards -128Gb. So run these on a large machine or perhaps a cluster node. +On a DIDE Windows machine, `in_path` and `out_path` can be fully-qualified paths +to DIDE network shares, which only members of the VIMC team have access to. This +is probably the easiest case. + +On Linux or Mac, you have to mount the DIDE network shares, providing your DIDE +details, and it is up to you what you call your local mountpoints. On linux, +they may start with `/mnt/` and on Mac `/Volume/`. Talk to IT, if you need help +setting up those mounts. + +And lastly, if you're on a windows machine but for some reason need to work +externally, then use ZScaler, map drive letters to the network shares, and pass +those in, but as we said, this is not recommended as it's not particularly +stable. If at all possible, use an internal machine to do the heavy data work, +and remote desktop into that if you need to be external. ## Creating a central estimate -For many groups, their central estimates are the average of their -stochastic runs. Historically groups have provided their centrals before -their stochastics, as a means of early review and detection of problems. -Having standardised a group's stochastic files, we can then generate -an averaged central for a single touchstone, disease, group and scenario, -with the following call:- +For many groups, their central estimates are the average of their stochastic +runs. Historically groups have provided their centrals before their stochastics, +as a means of early review and detection of problems. Having standardised a +group's stochastic files, we can then generate an averaged central for a single +touchstone, disease, group and scenario, with the following call:- -``` +``` stone_stochastic_central(base, "202310gavi", "YF", "IC-Garske", "yf-routine-campaign-ia2030") ``` -`base` here is the root of the share where our standardised stochastic -outputs have been put, and since those were created by stoner, it knows -how to build the paths correctly. +`base` here is the root of the share where our standardised stochastic outputs +have been put, and since those were created by stoner, it knows how to build the +paths correctly. -The result will be a new file in the form `group_scenario_central.pq` so -in this case `IC-Garske_yf-routine-campaign-ia2030_central.pq` which -matches the other standardised filenames, except for the country; this -one central file will contain all countries. +The result will be a new file in the form `group_scenario_central.pq` so in this +case `IC-Garske_yf-routine-campaign-ia2030_central.pq` which matches the other +standardised filenames, except for the country; this one central file will +contain all countries. -The default averaging function is `mean` - if we really want the `median` -of the stochastics, then add the argument `avg_method = mean` to the -function call. +The default averaging function is `mean` - if we really want the `median` of the +stochastics, then add the argument `avg_method = mean` to the function call. +Note the lack of quotes - we are sending a function, not the name of a function. ## Creating stochastic graphs -It can be useful to quickly dig out a graph showing all the stochastic -lines together, to see what sort of spread they have. Again, with -`base` set to the root of the share where we are putting our -standardised outputs, here is an example function call, and -the graph that it produces. +The `stone_stochastuc_graph` function can dig out a range of different graphs. +If you want to explore the stochastics interactively, then try the shiny app +(below) - but for programmatic plotting, read on. -``` +### Basic burdens. + +With `base` set to the root of the share where we are putting our standardised +outputs, here is an example function call, and the graph that it produces. + +``` stone_stochastic_graph(base, "202310gavi", "YF", "IC-Garske", - "KEN", "yf-routine-campaign-ia2030", + "KEN", "yf-no-vaccination", "deaths") ``` -![Stochastic graph of IC-Garske, YF, Kenya, 202310gavi deaths.](figures/stoch_example_1.png) +![](figures/stoch_example_1.png) + +The grey lines are each and every stochastic run. Red is the mean, and green is +the median of the stochastics. The thicker black lines are the 5% and 95% +quantiles. We can toggle the presence of each of those line types with the +optional arguments `include_stochastics`, `include_quantiles`, `include_mean` +and `include_median`, which can be set to `FALSE`, or left as the default +`TRUE`. + +### Burden Differences + +If we specify two scenarios instead of one, then the plot will be the burden of +the first scenario, subtract the burden of the second - i.e., the difference +made by applying a scenario. Below is an example - I'll also add the +`log = TRUE` argument to get a logged y-axis. + +``` +stone_stochastic_graph(base, "202310gavi", "YF", "IC-Garske", + "KEN", c("yf-no-vaccination", "yf-routine-campaign-ia2030"), + "deaths", log = TRUE) +``` + +![](figures/stoch_burden_diff.png) + +The y-axis now reads "deaths averted" instead of just deaths. + +### Comparing touchstones + +You may have noticed that `stone_stochastic_graph` when you run it causes a plot +to appear, but is actually returning you a list of plots - one so far. If we +specify two touchstones, then stoner will instead return us a list of two +graphs - assuming that the same disease, group and scenarios are present in both +touchstones. The y-axis will be matched between the two, so we can see the pair +of graphs for comparison. + +Below is an example returning to just a single scenario, although you can +provide two and compare burden differences across two touchstones too. Here I am +collecting both resulting graphs, and then plotting them one by one. + +``` +res <- stone_stochastic_graph(base, c("202110gavi", "202310gavi"), "YF", "IC-Garske", + "ETH", "yf-no-vaccination", + "deaths", log = TRUE) +res[[1]] +res[[2]] +``` + +::: {style="display: flex; gap: 10px;"} + + +::: + +### Comparing modelling groups + +Similarly, within the same touchstone, we can compare two modelling groups, with +either a single scenario, or pair of scenarios. The disease, scenarios and +outcomes must be compatible between the two groups. Here's a comparison of +burden difference: + +``` +res <- stone_stochastic_graph(base, "202310gavi", "YF", + c("IC-Garske", "UND-Perkins"), + "UGA", + c("yf-no-vaccination", "yf-routine-default"), + "cases") +res[[1]] +res[[2]] +``` + +::: {style="display: flex; gap: 10px;"} + + +::: + +### Filtering by age + +The stochastic data contains both years and ages. Our plots so far have been +using time on the x-axis, and all the ages have been summed together for each +calendar year. We can also filter by age before plotting. The example below +filters to ages between 0 and 4 inclusive (ie, under 5 year-olds). + +``` +res <- stone_stochastic_graph(base, "202310gavi", "YF", "IC-Garske", "UGA", +"yf-no-vaccination", "deaths", filter = 0:4) +``` + +![](figures/stoch_u5.png) + +### Plotting by birth cohort + +The `by_cohort` parameter lets us subtract age from year before plotting, so we +can see burden specific to people who were born in a certain year, rather than +the previous plots, which have all treated the x-axis as calendar year. + +``` +res <- stone_stochastic_graph(base, "202310gavi", "YF", "IC-Garske", "UGA", + "yf-no-vaccination", "deaths", by_cohort = TRUE) +``` + +![](figures/stoch_cohort.png) + +### Age on the x-axis + +If instead we want to see how burden affects people of different ages throughout +the time series, we can set `xaxis` to `age` (its default is `time`). + +``` +res <- stone_stochastic_graph(base, "202602yf", "YF", "IC-Gaythorpe", "UGA", + "yf-no-vaccination", "deaths", xaxis = "age") +``` + +![](figures/stoch_byage.png) + +This sums over the entire range of years. If `xaxis` is age, then the `filter` +argument will let you select which years are to be included. This example shows +the burden by age in the years 2040-2050. + +``` +res <- stone_stochastic_graph(base, "202602yf", "YF", "IC-Gaythorpe", "UGA", + "yf-no-vaccination", "deaths", xaxis = "age", + filter = 2040:2050) +``` + +![](figures/stoch_byage_filter.png) + +And if we additionally set the `by_cohort` flag, then we are asking for the +burden breakdown by age, for people who were born within the filtered range, +rather than selecting a calendar year range. + +``` +res <- stone_stochastic_graph(base, "202602yf", "YF", "IC-Gaythorpe", "UGA", + "yf-no-vaccination", "deaths", xaxis = "age", + filter = 2040:2050, by_cohort = TRUE) +``` + +![](figures/stoch_byage_filter_cohort.png) The graph has this shape because the +stochastics here only continue until 2100, hence the last 60-year olds will be +born in 2040. + +### Including data from packit. + +Lastly, if a central burden estimate has been included in a packit in Montagu's +reporting portal, we can include that on a graph by specifying the id, and the +name of the file within that packit. For example, this graph shows both the +stochastics from the file share, and the central from a packit, which is drawn +in blue. + +``` +res <- stone_stochastic_graph(base, "202602yf", "YF", "IC-Gaythorpe", "UGA", + "yf-no-vaccination", "deaths", + packit_id = "20260421-090519-e4a90228", + packit_file = "burden-estimates-disaggregated.rds") + +Visit and enter code DNLH-NMRL + +✔ Waiting for response from server [10.6s] +``` + +![](figures/stoch_packit.png) + +And here's an example of a burden difference graph, showing how much difference +the `yf-routine-default` scenario makes in both the stochastics, and the central +from packit. + +``` +res <- stone_stochastic_graph(base, "202602yf", "YF", "IC-Gaythorpe", "UGA", + c("yf-no-vaccination", "yf-routine-default"), "deaths", + packit_id = "20260421-090519-e4a90228", + packit_file = "burden-estimates-disaggregated.rds") + +Visit and enter code MBWV-NBBC +✔ Waiting for response from server [10.9s] +``` + +![](figures/stoch_packit_diff.png) \## The stochastic explorer shiny app + +All of the graphs above (except currently the packit graphs) can be visualised +interactively using a shiny app that is part of stoner. We call it providing the +path to the root of the standardised stochastic data, where the `meta.csv` file +should also be found. + +``` +stoner::stochastic_explorer(path) +``` -Red is the mean, and green is the median of the stochastics, and the -thicker black lines are the 5% and 95% quantiles. Other useful arguments for -graphs are `by_cohort` which plots a graph with birth cohort on the x-axis instead -of calendar year, `log` which causes the y-axis to be log-scale, and `ages` can be -a vector of ages to filter to, for example `0:4` to plot stochastics of under 5s. +![](figures/stoch_explorer.png) + +The features in the GUI closely map onto the parameters for `stochastic_graph` +in the following ways: + +- The tabs along the top let you choose a single graph, a comparison by + touchstone, or a comparison by modelling group, firstly for simply burdens, + and secondly for differences between scenarios. Different tabs therefore + cause different components in the sidebar to be available. +- One or two touchstones, groups, or scenarios will be available depending on + the tab. +- Whether the x-axis is time or age, and whether by cohort, are all selected + in the x-axis dropdown. +- The filter text, whether for time or for age, is a comma-separated list of + numbers or dash-separated ranges. So in a contrived example, `0-5,50,95-100` + would select the very young, very old, and exactly 50-year olds. diff --git a/vignettes/figures/stoch_burden_diff.png b/vignettes/figures/stoch_burden_diff.png new file mode 100644 index 0000000000000000000000000000000000000000..8be0700393d91b9e76f7ea7b4c0f26d610fcd796 GIT binary patch literal 16700 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 t+JyIlH4<+7|7rJs6)%*J{104%Xd?gs literal 0 HcmV?d00001 diff --git a/vignettes/figures/stoch_byage.png b/vignettes/figures/stoch_byage.png new file mode 100644 index 0000000000000000000000000000000000000000..4b4afe8a462112f06e84cff2cf7ee67753524134 GIT binary patch literal 14790 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 INty=#Kl9MIegFUf literal 0 HcmV?d00001 diff --git a/vignettes/figures/stoch_byage_filter.png b/vignettes/figures/stoch_byage_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..d14adf68200d66afae78489f0c679c1c68436446 GIT binary patch literal 15355 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