Guidance on how to generate figures and tables in a for loop (DRY principle)
Opened this issue · 5 comments
To follow the DRY (don't repeat yourself) principle, it is sometimes helpful to generate figures and tables in a for loop. For example, in the B-cell reports we are usually generating the same tables and figures repeatedly for different endpoints.
I think this challenge probably also applies in other assays. One approach is as follows:
``{r prepare-for-iterating-over-endpoints, results='asis'}
insert_fig_subchunk <- function(fig, fig_chunk_name, fig_caption_short, fig_caption_long) {
fig_deparsed <- paste0(deparse(function(){fig}), collapse = '')
fig_sub_chunk <- paste0(
"\n```{r fig-", fig_chunk_name, ', ',
"fig.scap='", fig_caption_short, "', ",
"fig.cap='", fig_caption_long, "'}",
"\n(", fig_deparsed, ")()", "\n```\n")
cat(knit(text = knit_expand(text = fig_sub_chunk), quiet = TRUE))
}
fig_caption_common_text <- 'Some description of the figure content that applies to every figure, such as the color schemes/shapes/methods used'
endpoint_list <- c('Percent of IgG B-cells that are antigen-specific', 'Percent of IgG B-cells that are VRC01-class')
``
``{r iterate-over-endpoints, results='asis'}
for (endpoint in endpoint_list) {
cat('## ', endpoint, ' \n') # subsection title
# main figure
fig_caption_short <- endpoint
fig_caption_long <- paste0(endpoint, ". ", fig_caption_common_text)
fig <- plot_responses(endpoint) # this function defined elsewhere
fig_chunk_name <- gsub(' ', '-', endpoint)
insert_fig_subchunk(fig, fig_chunk_name, fig_caption_short, fig_caption_long)
cat("\n\n\\pagebreak\n")
# tables
tabulate_response_timepoint_comparisons(endpoint) # this function defined elsewhere
tabulate_response_group_comparisons(endpoint) # this function defined elsewhere
cat("\n\n\\pagebreak\n")
}
``
Is this broadly applicable enough that it would be helpful to incorporate some version of this into the report template?
If so, any improvements to suggest?
A better example from conversation with Bryan on July 31st:
insert_fig_subchunk <- function(fig, fig_chunk_name, fig_caption_short, fig_caption_long) {
fig_deparsed <- paste0(deparse(function(){fig}), collapse = '')
fig_sub_chunk <- paste0(
"\n```{r fig-", fig_chunk_name, ', ',
"fig.scap='", fig_caption_short, "', ",
"fig.cap='", fig_caption_long, "'}",
"\n(", fig_deparsed, ")()", "\n```\n")
cat(knit(text = knit_expand(text = fig_sub_chunk), quiet = TRUE))
}
for (endpoint in endpoint_list) {
cat('## ', endpoint, ' \n')
### main figure
fig <- plot_responses(endpoint)
fig_chunk_name <- gsub(' ', '-', endpoint)
fig_caption_short <- endpoint
fig_caption_long <- paste0(endpoint, ". ", plot_description_details)
insert_fig_subchunk(fig, fig_chunk_name, fig_caption_short, fig_caption_long)
### table
tab_label <- endpoint
tab_caption_short <- endpoint
tab_caption_long <- paste0(endpoint, ". ", tab_description_details)
tab <- df %>%
kable(format = output_type,
caption = tab_caption_long,
caption_short = tab_caption_short,
label = tab_label)
if (output_type == "latex") {
cat(tab)
} else {
cat(knit_print(tab))
}
cat("\n\n \\clearpage \n")
}
Thank you to Kellie's great TIOT on Oct 24, 2024 (and @asatofh for organizing) we had some great brainstorming related to this topic! I'm tagging folks who can share the relevant code, packages and/or challenges so we can provide guidance on creating multiple similar figures and/or tables at once. Knowing other people's blockers and challenges is really helpful so we don't all repeat the same issues in silos:
@at-crx Amanda shared some of her experiences with the challenges trying to create figures in a loop (issues: trying to reference figures, and the possibility of mismatching inputs when they're in the form of independent lists. for example a list of captions and a list of plot titles, etc.)
@brborate Bhavesh mentioned the use of purrr::map function to get all the figures as a list (re: ways to create objects to input into the for loops)
@bneradil Moni discussed HVTN's practices which include an alternative way to (is there code that can be shared to see this example?)
@valduran18 shared you can create dataframes with a column of lists of datasets, a column of text captions, and a column of list of figures. I think this is a great idea for debugging and viewing. and that this would be relevant for STPs (can you link git issues across repos? do we want to do that to create visibility for any specific STP repos?)
Would love to see relevant code! So please share! I am currently working on a report using these methods so any code or further thoughts shared sooner rather than later are appreciated and will be referenced for specific reports as well as general guidance for all of us. Thank you all and especially @kelliemac for the presentation, and great conversation!
Courtesy of @bneradil
As described hvtnReports has a vignette on caption creation here:https://github.com/FredHutch/hvtnReports/blob/main/vignettes/tlf_captions.md
And our RMD skeleton is here: https://github.com/FredHutch/hvtnReports/blob/main/inst/rmarkdown/templates/vtn_report/skeleton/skeleton.Rmd