Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions R/constants.R
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,11 @@ DEF_TOUCHSTONE_OLD_OLD <- "202110"
#'
#' @export
COLOUR_VIMC <- "#008080"

#' @name constants
#'
#' @examples
#' pine
#'
#' @export
pine <- c("PAK", "IND", "NGA", "ETH")
240 changes: 240 additions & 0 deletions R/fn_plotting_impact.R
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall comment on these fns - since they prepare data before plotting it, I would separate the preparation steps from the plotting steps, and have paired functions prep_plot_X() and plot_X(), which is the pattern we've adopted in {vimcheck}. In a report, these would/should be pipe-able, data %>% prep_plot_X() %>% plot_X(),

Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#' Plot central impact estimates by cohort and year.
#' TODO: need to add importFrom ... to avoid package issues with testing?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The way @importFrom is used is to import functions from packages that are used repeatedly, really only to avoid writing out the explicit namespacing each time (i.e., ggplot2::fn()). Tests might fail if you neither import nor explicitly namespace a function, but the tag isn't otherwise linked to test failures.

#'
#' Produces faceted plots of central impact estimates for priority countries,
#' stratified either by birth cohort or by year of vaccination.
#' Impact metrics include cases, deaths, DALYs, and YLLs.
#'
#' @param data A tibble containing impact estimates.
#' @param burden_type Burden metric used to evaluate impact. burden_type can be: cases, deaths, dalys, yll.
#' @param title Title of the plot to be rendered
#' @param view Charactar scalar. The way impact is assigned, either by birth cohort ("cohort") or by year of vaccination ("year").
#'
#' @return ggplot object showing central impact estimates
#'
#' @examples
#' plot_impact(
#' data = impact_data,
#' burden_type = "cases",
#' title = "Cases averted",
#' view = "year"
#' )
Comment on lines +16 to +21
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This example will fail as impact_data hasn't been defined. Each @example block is standalone, so you'll need to create impact_data here.

#'
#' @export
plot_impact <- function(
data,
burden_type,
title,
view
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The convention for a small number of fixed argument options is to display them in the function signature (i.e., this bit).

So prefer f = function(arg = c("option_1", "option_2")). This way when users pull up the help page they can immediately see the valid options.

This should be paired with some argument checking which you already have. checkmate::assert_choice() is fine; in {vimcheck} I've gone with rlang::arg_match() whose error messages are a bit more informative.

){
checkmate::assert_tibble(data, min.rows = 1L, min.cols = 1L)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Might be good to check that data has at least as many cols as are used in this fn; would also be good to add checks on the specific column names. See e.g. fns in R/fn_plotting_prep_bur_diag.R which use checkmate::assert_names() for this.

checkmate::assert_character(burden_type, len = 1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Prefer using rlang::arg_match() here too. If keeping assert_choice(), this assert_character() isn't needed. assert_character(..., len = 1) is better replaced with assert_string() (in other use cases, can simply be removed here).

checkmate::assert_character(title, len = 1)

checkmate::assert_choice(
burden_type,
choices = c("cases", "deaths", "dalys", "yll")
)

checkmate::assert_choice(
view,
choices = c("cohort", "year")
)

Impact <-
data %>%
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So I would recommend against using pipes in functions, because any errors that occur can only be traced to the top of the pipeline (here, L. 44), and not to the line where they actually are. It's tedious, but it's better to replace each step in the pipeline with an explicit assignment step.

dplyr::filter(.data$country %in% pine) %>%
dplyr::filter(
.data$burden_outcome == burden_type & .data$impact != 0) #%>%
Comment on lines +46 to +48
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's possible to combine multiple filter conditions into a single call to filter(); they are combined using & by default.

if(nrow(Impact) > 0){
# ---- Cohort view ----
if(view == "cohort"){
Impact <- Impact %>% dplyr::rename(cohort = .data$birth_cohort) %>%
dplyr::select(
.data$country,
.data$cohort,
.data$impact,
.data$short_name
)
p <- ggplot(
Impact,
aes(
x = .data$cohort,
y = .data$impact,
ymin = .data$impact,
ymax = .data$impact,
fill = as.character(.data$short_name)
)
) +
ggplot::geom_ribbon(alpha = 0.3) +
ggplot::geom_line(aes(colour = .data$short_name), size = 0.5)+
ggplot::geom_point(aes(colour = .data$short_name), size = 0.5)+
theme_vimc() + #TODO: to check where the theme definition is saved as may not be right for this plot
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The theme is in R/fn_plotting_helpers.R - you could either modify the default theme or build off it if you want this figure to look different.

facet_wrap(country~., scales = "free_y") +
labs(
x = "Birth cohort",
y = paste(burden_type, "averted"),
title = title
) +
theme(
legend.position="bottom",
legend.key.size= unit(0.5, 'cm'),
legend.key.width = unit(0.3, 'cm')
)

} else { # ---- Year (non-cohort) view ----
Impact <- Impact %>%
dplyr::select(
.data$country,
.data$year,
.data$impact,
.data$short_name
)

p <- ggplot (
Impact,
aes(
x = .data$year,
y = .data$impact,
ymin = .data$impact,
ymax = .data$impact,
fill = .data$short_name
)
) +
ggplot::geom_ribbon(alpha = 0.3)+
ggplot::geom_line(aes(colour = .data$short_name), size = 0.5)+
ggplot::geom_point(aes(colour = .data$short_name), size = 0.5)+
theme_vimc() + #TODO: same note as above re theme definition
facet_wrap(country~., scales = "free_y")+
labs(
x = "Year",
y = paste(burden_type, "averted"),
title = title
) +
theme(
legend.position="bottom",
legend.key.size= unit(0.5, 'cm'),
legend.key.width = unit(0.3, 'cm')
)
}
} else {
p <- "No estimates in the data." #TODO: both here and in the below plot returning p may be an issue? Can you think of a better way?
}
Comment on lines +120 to +122
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This could be handled in a plotting preparation function that errors if there is no PINE data? That way the plotting fn always returns a plot.

return(p)

}

#' Plot coverage and fully vaccinated persons (FVPs)
#'
#' Generates plots of routine vaccine coverage and fully vaccinated
#' persons (FVPs) over time for selected countries.
#'
#' @param fvps A tibble showing the number of fvps (fully vaccinated persons)
#' by country, year and scenario/activity type.
#'
#' @return A named list with two ggplot objects:
#' \describe{
#' \item{coverage}{A plot of routine vaccine coverage over time.}
#' \item{fvps}{A plot of fully vaccinated persons over time.}
#' }
#' @examples
#' plots <- plot_coverage_fvps(fvps)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Need to define the data fvps.

#' plots$coverage
#' plots$fvps
#'
#' @export
plot_coverage_fvps <- function(fvps){
checkmate::assert_tibble(fvps, min.rows = 1L, min.cols = 1L)
Comment on lines +146 to +147
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pretty much the same general suggestions as for the preceding fn.


fvps <- fvps %>%
dplyr::filter(.data$country %in% pine)

cov <- fvps %>%
dplyr::filter(.data$activity_type == "routine") %>%
dplyr::mutate(
vaccine_delivery = paste(.data$scenario_type, .data$vaccine, sep = "_"),
coverage_adjusted = round(.data$coverage_adjusted*100, 2)
) %>%
dplyr::select(
.data$country,
.data$vaccine_delivery,
.data$year,
.data$coverage_adjusted) %>%
Comment on lines +158 to +162
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure how advisable this syntax is. I would define the cols to select as a character vector and pass them to select using all_of() or {{ }}.

dplyr::rename(coverage = .data$coverage_adjusted)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here it might be safer to pass a named vector giving the renaming map. See 2nd example in rename docs: https://dplyr.tidyverse.org/reference/rename.html


fvp <- fvps %>%
dplyr::mutate(
vaccine_delivery = paste(.data$scenario_type, .data$activity_type, sep = "_")
) %>%
dplyr::select(
.data$country,
.data$vaccine_delivery,
.data$year,
.data$fvps
) %>%
dplyr::group_by(
.data$country,
.data$vaccine_delivery,
.data$year) %>%
dplyr::summarise(
fvps = round(sum(.data$fvps)/1e6, 2),
.groups = "drop"
)
if(nrow(cov) > 0){
p <- ggplot(
cov,
aes(
x = .data$year,
y = .data$coverage,
ymin = 0,
ymax = 1,
fill = .data$vaccine_delivery)
) +
ggplot::geom_line(aes(colour = .data$vaccine_delivery), size = 0.5) +
theme_vimc() + #TODO: same note as above
facet_wrap(country~., scales = "free_y")+
labs(
x = "Year",
y = "Coverage (%)",
title = "Routine vaccine coverage"
) +
theme(
legend.position="bottom",
legend.key.size= unit(0.5, 'cm'),
legend.key.width = unit(0.3, 'cm')
)

} else {
p <- "There is no routine coverage in the database."
}


q <- ggplot(
fvp,
aes(
x = .data$year,
y = .data$fvps,
ymin = .data$fvps,
ymax = .data$fvps, #TODO: min/max both here and above seem to be the same so may be irrelevant to define
fill = .data$vaccine_delivery
)
) +
geom_point(aes(colour = .data$vaccine_delivery), size = 0.5) +
theme_vimc()+ #TODO: same note above on theme
facet_wrap(country~., scales = "free_y") +
labs(
x = "Year",
y = "FVPs (in millions)",
title = "FVPs"
) +
theme(
legend.position="bottom",
legend.key.size = unit(0.5, 'cm'),
legend.key.width = unit(0.3, 'cm')
)

return(list(
coverage = p,
fvps = q
))
}
7 changes: 7 additions & 0 deletions man/constants.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions man/plot_coverage_fvps.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions man/plot_impact.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading