Skip to content

Commit 02f18ab

Browse files
m7prgogonzokpagaczgithub-actions[bot]dependabot-preview[bot]
authored
🗃️ decorators feature branch (#795)
Partner to insightsengineering/teal#1357 Introduces decorators to modules. More about decorators in here insightsengineering/teal#1384 <details><summary>Example with 1 tab per module</summary> ```r pkgload::load_all("../teal") pkgload::load_all(".") # ###################################################### # # _____ _ # | __ \ | | # | | | | ___ ___ ___ _ __ __ _| |_ ___ _ __ ___ # | | | |/ _ \/ __/ _ \| '__/ _` | __/ _ \| '__/ __| # | |__| | __/ (_| (_) | | | (_| | || (_) | | \__ \ # |_____/ \___|\___\___/|_| \__,_|\__\___/|_| |___/ # # # # Decorators # ##################################################### plot_grob_decorator <- function(default_footnote = "I am a good decorator", .var_to_replace = "plot") { teal_transform_module( label = "Caption (grob)", ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_footnote), server = function(id, data) { moduleServer(id, function(input, output, session) { logger::log_info("🟠 plot_grob with default: {default_footnote}!", namespace = "teal.modules.general") reactive({ req(data(), input$footnote) logger::log_info("changing the footnote {default_footnote}", namespace = "teal.modules.general") teal.code::eval_code(data(), substitute( { footnote_grob <- grid::textGrob(footnote, x = 0, hjust = 0, gp = grid::gpar(fontsize = 10, fontface = "italic", col = "gray50")) # Arrange the plot and footnote .var_to_replace <- gridExtra::arrangeGrob( .var_to_replace, footnote_grob, ncol = 1, heights = grid::unit.c(grid::unit(1, "npc") - grid::unit(1, "lines"), grid::unit(1, "lines")) ) }, env = list( footnote = input$footnote, .var_to_replace = as.name(.var_to_replace) ))) }) }) } ) } caption_decorator <- function(default_caption = "I am a good decorator", .var_to_replace = "plot") { teal_transform_module( label = "Caption", ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_caption), server = make_teal_transform_server( substitute({ my_name <- .var_name .var_to_replace <- .var_to_replace + ggplot2::labs(caption = footnote) }, env = list(.var_to_replace = as.name(.var_to_replace), .var_name = .var_to_replace)) ) ) } table_decorator <- function(.color1 = "#f9f9f9", .color2 = "#f0f0f0", .var_to_replace = "table") { teal_transform_module( label = "Table color", ui = function(id) { selectInput( NS(id, "style"), "Table Style", choices = c("Default", "Color1", "Color2"), selected = "Default" ) }, server = function(id, data) { moduleServer(id, function(input, output, session) { logger::log_info("🔵 Table row color called to action!", namespace = "teal.modules.general") reactive({ req(data(), input$style) logger::log_info("changing the Table row color '{input$style}'", namespace = "teal.modules.general") teal.code::eval_code(data(), substitute({ .var_to_replace <- switch( style, "Color1" = DT::formatStyle( .var_to_replace, columns = attr(.var_to_replace$x, "colnames")[-1], target = "row", backgroundColor = .color1 ), "Color2" = DT::formatStyle( .var_to_replace, columns = attr(.var_to_replace$x, "colnames")[-1], target = "row", backgroundColor = .color2 ), .var_to_replace ) }, env = list( style = input$style, .var_to_replace = as.name(.var_to_replace), .color1 = .color1, .color2 = .color2 ))) }) }) } ) } head_decorator <- function(default_value = 6, .var_to_replace = "object") { teal_transform_module( label = "Head", ui = function(id) shiny::numericInput(shiny::NS(id, "n"), "Footnote", value = default_value), server = make_teal_transform_server( substitute({ .var_to_replace <- utils::head(.var_to_replace, n = n) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } treelis_subtitle_decorator <- function(default_caption = "I am a good decorator", .var_to_replace = "plot") { teal_transform_module( label = "Caption", ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_caption), server = make_teal_transform_server( substitute({ .var_to_replace <- update(.var_to_replace, sub = footnote) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } insert_rrow_decorator <- function(default_caption = "I am a good new row", .var_to_replace = "table") { teal_transform_module( label = "New row", ui = function(id) shiny::textInput(shiny::NS(id, "new_row"), "New row", value = default_caption), server = make_teal_transform_server( substitute({ .var_to_replace <- rtables::insert_rrow(.var_to_replace, rtables::rrow(new_row)) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } do_nothing_decorator <- teal_transform_module(server = function(id, data) moduleServer(id, function(input, output, session) data)) # ########################################## # # _ _ _ _ # | | | | | | | | # | |_ ___ __ _| | __| | __ _| |_ __ _ # | __/ _ \/ _` | | / _` |/ _` | __/ _` | # | || __/ (_| | || (_| | (_| | || (_| | # \__\___|\__,_|_| \__,_|\__,_|\__\__,_| # ______ # |______| # # teal_data # ######################################### data <- teal_data(join_keys = default_cdisc_join_keys[c("ADSL", "ADRS")]) data <- within(data, { require(nestcolor) ADSL <- rADSL ADRS <- rADRS }) # For tm_outliers fact_vars_adsl <- names(Filter(isTRUE, sapply(data[["ADSL"]], is.factor))) vars <- choices_selected(variable_choices(data[["ADSL"]], fact_vars_adsl)) # For tm_g_distribution vars1 <- choices_selected( variable_choices(data[["ADSL"]], c("ARM", "COUNTRY", "SEX")), selected = NULL ) init( data = data, modules = modules( # ################################################### # # _ # (_) # _ __ ___ __ _ _ __ ___ ___ ___ _ ___ _ __ # | '__/ _ \/ _` | '__/ _ \/ __/ __| |/ _ \| '_ \ # | | | __/ (_| | | | __/\__ \__ \ | (_) | | | | # |_| \___|\__, |_| \___||___/___/_|\___/|_| |_| # __/ | # |___/ # # regression # ################################################## tm_a_regression( label = "Regression", response = data_extract_spec( dataname = "ADSL", select = select_spec( label = "Select variable:", choices = "BMRKR1", selected = "BMRKR1", multiple = FALSE, fixed = TRUE ) ), regressor = data_extract_spec( dataname = "ADSL", select = select_spec( label = "Select variables:", choices = variable_choices(data[["ADSL"]], c("AGE", "SEX", "RACE")), selected = "AGE", multiple = TRUE, fixed = FALSE ) ), decorators = list(caption_decorator("I am Regression", "plot")) ), # ######################################################### # # _ _ _ _ _ _ _ # | (_) | | (_) | | | (_) # __| |_ ___| |_ _ __ _| |__ _ _| |_ _ ___ _ __ # / _` | / __| __| '__| | '_ \| | | | __| |/ _ \| '_ \ # | (_| | \__ \ |_| | | | |_) | |_| | |_| | (_) | | | | # \__,_|_|___/\__|_| |_|_.__/ \__,_|\__|_|\___/|_| |_| # # # # distribution # ######################################################## tm_g_distribution( dist_var = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1")), selected = "BMRKR1", multiple = FALSE, fixed = FALSE ) ), strata_var = data_extract_spec( dataname = "ADSL", filter = filter_spec( vars = vars1, multiple = TRUE ) ), group_var = data_extract_spec( dataname = "ADSL", filter = filter_spec( vars = vars1, multiple = TRUE ) ), decorators = list( histogram_plot = caption_decorator("I am density!", "histogram_plot"), qq_plot = caption_decorator("I am QQ!", "qq_plot"), summary_table = table_decorator("#FFA500", "#800080", "summary_table"), test_table = table_decorator("#2FA000", "#80FF80", "test_table") ) ), # #################### # # # # _ __ ___ __ _ # | '_ \ / __/ _` | # | |_) | (_| (_| | # | .__/ \___\__,_| # | | # |_| # # pca # ################### tm_a_pca( "PCA", dat = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data = data[["ADSL"]], c("BMRKR1", "AGE", "EOSDY")), selected = c("BMRKR1", "AGE") ) ), decorators = list( elbow_plot = caption_decorator("I am PCA / elbow", "elbow_plot"), circle_plot = caption_decorator("I am a PCA / circle", "circle_plot"), biplot = caption_decorator("I am a PCA / bipot", "biplot"), eigenvector_plot = caption_decorator("I am a PCA / eigenvector", "eigenvector_plot") ) ), ###################################### # # _ _ _ # | | | (_) # ___ _ _| |_| |_ ___ _ __ ___ # / _ \| | | | __| | |/ _ \ '__/ __| # | (_) | |_| | |_| | | __/ | \__ \ # \___/ \__,_|\__|_|_|\___|_| |___/ # # # # outliers # ##################################### tm_outliers( outlier_var = list( data_extract_spec( dataname = "ADSL", select = select_spec( label = "Select variable:", choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1")), selected = "AGE", multiple = FALSE, fixed = FALSE ) ) ), categorical_var = list( data_extract_spec( dataname = "ADSL", filter = filter_spec( vars = vars, choices = value_choices(data[["ADSL"]], vars$selected), selected = value_choices(data[["ADSL"]], vars$selected), multiple = TRUE ) ) ), decorators = list( box_plot = caption_decorator("I am a good decorator", "box_plot"), density_plot = caption_decorator("I am a good decorator", "density_plot"), cumulative_plot = caption_decorator("I am a good decorator", "cumulative_plot"), table = table_decorator("#FFA500", "#800080") ) ), # ####################################################### # # _ _ _ # (_) | | (_) # __ _ ___ ___ ___ ___ _ __ _| |_ _ ___ _ __ # / _` / __/ __|/ _ \ / __| |/ _` | __| |/ _ \| '_ \ # | (_| \__ \__ \ (_) | (__| | (_| | |_| | (_) | | | | # \__,_|___/___/\___/ \___|_|\__,_|\__|_|\___/|_| |_| # # # # association # ###################################################### tm_g_association( ref = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices( data[["ADSL"]], c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2") ), selected = "RACE" ) ), vars = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices( data[["ADSL"]], c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2") ), selected = "BMRKR2", multiple = TRUE ) ), decorators = list(plot_grob_decorator("I am a good grob (association)")) ), # ################################################ # # _ _ _ _ _ # | | | | | | | | | | # __| | __ _| |_ __ _ | |_ __ _| |__ | | ___ # / _` |/ _` | __/ _` || __/ _` | '_ \| |/ _ \ # | (_| | (_| | || (_| || || (_| | |_) | | __/ # \__,_|\__,_|\__\__,_| \__\__,_|_.__/|_|\___| # ______ # |______| # # data_table # ############################################### tm_data_table( variables_selected = list( iris = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species") ), dt_args = list(caption = "IRIS Table Caption"), decorators = list(table_decorator()) ), # ######################################################## # # _ _ _ # | | | | | | # ___ _ __ ___ ___ ___ ______| |_ __ _| |__ | | ___ # / __| '__/ _ \/ __/ __|______| __/ _` | '_ \| |/ _ \ # | (__| | | (_) \__ \__ \ | || (_| | |_) | | __/ # \___|_| \___/|___/___/ \__\__,_|_.__/|_|\___| # # # # cross-table # ####################################################### tm_t_crosstable( label = "Cross Table", x = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], subset = function(data) { idx <- !vapply(data, inherits, logical(1), c("Date", "POSIXct", "POSIXlt")) return(names(data)[idx]) }), selected = "COUNTRY", multiple = TRUE, ordered = TRUE ) ), y = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], subset = function(data) { idx <- vapply(data, is.factor, logical(1)) return(names(data)[idx]) }), selected = "SEX" ) ), decorators = list(insert_rrow_decorator("I am a good new row")) ), # ####################################################################################### # # _ _ _ _ _ _ # | | | | | | | | | | (_) # ___ ___ __ _| |_| |_ ___ _ __ _ __ | | ___ | |_ _ __ ___ __ _| |_ _ __ ___ __ # / __|/ __/ _` | __| __/ _ \ '__| '_ \| |/ _ \| __| | '_ ` _ \ / _` | __| '__| \ \/ / # \__ \ (_| (_| | |_| || __/ | | |_) | | (_) | |_ | | | | | | (_| | |_| | | |> < # |___/\___\__,_|\__|\__\___|_| | .__/|_|\___/ \__| |_| |_| |_|\__,_|\__|_| |_/_/\_\ # | | # |_| # # scatterplot matrix # ###################################################################################### tm_g_scatterplotmatrix( label = "Scatterplot matrix", variables = list( data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]]), selected = c("AGE", "RACE", "SEX"), multiple = TRUE, ordered = TRUE ) ), data_extract_spec( dataname = "ADRS", filter = filter_spec( label = "Select endpoints:", vars = c("PARAMCD", "AVISIT"), choices = value_choices(data[["ADRS"]], c("PARAMCD", "AVISIT"), c("PARAM", "AVISIT")), selected = "INVET - END OF INDUCTION", multiple = TRUE ), select = select_spec( choices = variable_choices(data[["ADRS"]]), selected = c("AGE", "AVAL", "ADY"), multiple = TRUE, ordered = TRUE ) ) ), decorators = list(treelis_subtitle_decorator("I am a Scatterplot matrix", "plot")) ), # ############################################# # # # # _ __ ___ ___ _ __ ___ _ __ ___ ___ # | '__/ _ \/ __| '_ \ / _ \| '_ \/ __|/ _ \ # | | | __/\__ \ |_) | (_) | | | \__ \ __/ # |_| \___||___/ .__/ \___/|_| |_|___/\___| # | | # |_| # # response # ############################################ tm_g_response( label = "Response", response = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("BMRKR2", "COUNTRY"))) ), x = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("SEX", "RACE")), selected = "RACE") ), decorators = list(caption_decorator("I am a Response", "plot")) ), # ############################################ # # _ _ _ _ # | | (_) (_) | | # | |__ ___ ____ _ _ __ _ __ _| |_ ___ # | '_ \| \ \ / / _` | '__| |/ _` | __/ _ \ # | |_) | |\ V / (_| | | | | (_| | || __/ # |_.__/|_| \_/ \__,_|_| |_|\__,_|\__\___| # # # # bivariate # ########################################### tm_g_bivariate( x = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "AGE") ), y = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "SEX") ), row_facet = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "ARM") ), col_facet = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "COUNTRY") ), decorators = list(caption_decorator("I am a Bivariate", "plot")) ), ##################################################### # # _ _ _ _ # | | | | | | | | # ___ ___ __ _| |_| |_ ___ _ __ _ __ | | ___ | |_ # / __|/ __/ _` | __| __/ _ \ '__| '_ \| |/ _ \| __| # \__ \ (_| (_| | |_| || __/ | | |_) | | (_) | |_ # |___/\___\__,_|\__|\__\___|_| | .__/|_|\___/ \__| # | | # |_| # # scatterplot # #################################################### tm_g_scatterplot( label = "Scatterplot", x = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1", "BMRKR2"))) ), y = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1", "BMRKR2")), selected = "BMRKR1" ) ), color_by = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1", "BMRKR2", "RACE", "REGION1")), selected = NULL ) ), size_by = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1"))) ), row_facet = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("BMRKR2", "RACE", "REGION1")), selected = NULL ) ), col_facet = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("BMRKR2", "RACE", "REGION1")), selected = NULL ) ), decorators = list(caption_decorator("I am a scatterplot", "plot")) ), # ############################################################## # # _ _ _ _ # (_) (_) | | | | # _ __ ___ _ ___ ___ _ _ __ __ _ __| | __ _| |_ __ _ # | '_ ` _ \| / __/ __| | '_ \ / _` | / _` |/ _` | __/ _` | # | | | | | | \__ \__ \ | | | | (_| | | (_| | (_| | || (_| | # |_| |_| |_|_|___/___/_|_| |_|\__, | \__,_|\__,_|\__\__,_| # __/ |_____ # |___/______| # # missing_data # ############################################################# tm_missing_data( label = "Missing data", decorators = list( summary_plot = plot_grob_decorator("A", "summary_plot"), combination_plot = plot_grob_decorator("B", "combination_plot"), summary_table = table_decorator("table", .color1 = "#f0000055"), by_subject_plot = caption_decorator("by_subject_plot") ) ), example_module(decorators = list(head_decorator(6))) ) ) |> shiny::runApp() ``` --------- Signed-off-by: Marcin <[email protected]> Signed-off-by: André Veríssimo <[email protected]> Co-authored-by: go_gonzo <[email protected]> Co-authored-by: Konrad Pagacz <[email protected]> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: 27856297+dependabot-preview[bot]@users.noreply.github.com <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: André Veríssimo <[email protected]>
1 parent cd8ac9d commit 02f18ab

37 files changed

+2033
-918
lines changed

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Imports:
3535
DT (>= 0.13),
3636
forcats (>= 1.0.0),
3737
grid,
38+
lifecycle (>= 0.2.0),
3839
scales,
3940
shinyjs,
4041
shinyTree (>= 0.2.8),
@@ -84,7 +85,7 @@ VignetteBuilder:
8485
Config/Needs/verdepcheck: haleyjeppson/ggmosaic, tidyverse/ggplot2,
8586
rstudio/shiny, insightsengineering/teal,
8687
insightsengineering/teal.transform, mllg/checkmate, tidyverse/dplyr,
87-
rstudio/DT, tidyverse/forcats, r-lib/scales, daattali/shinyjs,
88+
rstudio/DT, tidyverse/forcats, r-lib/lifecycle, r-lib/scales, daattali/shinyjs,
8889
shinyTree/shinyTree, rstudio/shinyvalidate, dreamRs/shinyWidgets,
8990
tidyverse/stringr, insightsengineering/teal.code,
9091
insightsengineering/teal.data, insightsengineering/teal.logger,

R/roxygen2_templates.R

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# nocov start
2+
roxygen_decorators_param <- function(module_name) {
3+
paste(
4+
sep = " ",
5+
lifecycle::badge("experimental"),
6+
" (`list` of `teal_transform_module`, named `list` of `teal_transform_module` or",
7+
"`NULL`) optional, if not `NULL`, decorator for tables or plots included in the module.",
8+
"When a named list of `teal_transform_module`, the decorators are applied to the",
9+
"respective output objects.\n\n",
10+
"Otherwise, the decorators are applied to all objects, which is equivalent as using the name `default`.\n\n",
11+
sprintf("See section \"Decorating `%s`\"", module_name),
12+
"below for more details."
13+
)
14+
}
15+
16+
roxygen_ggplot2_args_param <- function(...) {
17+
paste(
18+
sep = " ",
19+
"(`ggplot2_args`) optional, object created by [`teal.widgets::ggplot2_args()`]",
20+
"with settings for all the plots or named list of `ggplot2_args` objects for plot-specific settings.",
21+
"The argument is merged with options variable `teal.ggplot2_args` and default module setup.\n\n",
22+
sprintf(
23+
"List names should match the following: `c(\"default\", %s)`.\n\n",
24+
paste("\"", unlist(rlang::list2(...)), "\"", collapse = ", ", sep = "")
25+
),
26+
"For more details see the vignette: `vignette(\"custom-ggplot2-arguments\", package = \"teal.widgets\")`."
27+
)
28+
}
29+
30+
# nocov end

R/tm_a_pca.R

Lines changed: 120 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,46 @@
1313
#' It controls the font size for plot titles, axis labels, and legends.
1414
#' - If vector of `length == 1` then the font sizes will have a fixed size.
1515
#' - while vector of `value`, `min`, and `max` allows dynamic adjustment.
16-
#' @templateVar ggnames "Elbow plot", "Circle plot", "Biplot", "Eigenvector plot"
17-
#' @template ggplot2_args_multi
16+
#' @param ggplot2_args `r roxygen_ggplot2_args_param("Elbow plot", "Circle plot", "Biplot", "Eigenvector plot")`
17+
#' @param decorators `r roxygen_decorators_param("tm_a_pca")`
1818
#'
1919
#' @inherit shared_params return
2020
#'
21+
#' @section Decorating `tm_a_pca`:
22+
#'
23+
#' This module generates the following objects, which can be modified in place using decorators:
24+
#' - `elbow_plot` (`ggplot2`)
25+
#' - `circle_plot` (`ggplot2`)
26+
#' - `biplot` (`ggplot2`)
27+
#' - `eigenvector_plot` (`ggplot2`)
28+
#'
29+
#' Decorators can be applied to all outputs or only to specific objects using a
30+
#' named list of `teal_transform_module` objects.
31+
#' The `"default"` name is reserved for decorators that are applied to all outputs.
32+
#' See code snippet below:
33+
#'
34+
#' ```
35+
#' tm_a_pca(
36+
#' ..., # arguments for module
37+
#' decorators = list(
38+
#' default = list(teal_transform_module(...)), # applied to all outputs
39+
#' elbow_plot = list(teal_transform_module(...)), # applied only to `elbow_plot` output
40+
#' circle_plot = list(teal_transform_module(...)) # applied only to `circle_plot` output
41+
#' biplot = list(teal_transform_module(...)) # applied only to `biplot` output
42+
#' eigenvector_plot = list(teal_transform_module(...)) # applied only to `eigenvector_plot` output
43+
#' )
44+
#' )
45+
#' ```
46+
#'
47+
#' For additional details and examples of decorators, refer to the vignette
48+
#' `vignette("decorate-modules-output", package = "teal")` or the [`teal_transform_module()`] documentation.
49+
#'
2150
#' @examplesShinylive
2251
#' library(teal.modules.general)
2352
#' interactive <- function() TRUE
2453
#' {{ next_example }}
2554
#' @examples
55+
#'
2656
#' # general data example
2757
#' data <- teal_data()
2858
#' data <- within(data, {
@@ -58,6 +88,7 @@
5888
#' interactive <- function() TRUE
5989
#' {{ next_example }}
6090
#' @examples
91+
#'
6192
#' # CDISC data example
6293
#' data <- teal_data()
6394
#' data <- within(data, {
@@ -102,7 +133,8 @@ tm_a_pca <- function(label = "Principal Component Analysis",
102133
alpha = c(1, 0, 1),
103134
size = c(2, 1, 8),
104135
pre_output = NULL,
105-
post_output = NULL) {
136+
post_output = NULL,
137+
decorators = NULL) {
106138
message("Initializing tm_a_pca")
107139

108140
# Normalize the parameters
@@ -152,6 +184,10 @@ tm_a_pca <- function(label = "Principal Component Analysis",
152184

153185
checkmate::assert_multi_class(pre_output, c("shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE)
154186
checkmate::assert_multi_class(post_output, c("shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE)
187+
188+
available_decorators <- c("elbow_plot", "circle_plot", "biplot", "eigenvector_plot")
189+
decorators <- normalize_decorators(decorators)
190+
assert_decorators(decorators, null.ok = TRUE, available_decorators)
155191
# End of assertions
156192

157193
# Make UI args
@@ -169,7 +205,8 @@ tm_a_pca <- function(label = "Principal Component Analysis",
169205
list(
170206
plot_height = plot_height,
171207
plot_width = plot_width,
172-
ggplot2_args = ggplot2_args
208+
ggplot2_args = ggplot2_args,
209+
decorators = decorators
173210
)
174211
),
175212
datanames = teal.transform::get_extract_datanames(data_extract_list)
@@ -224,6 +261,34 @@ ui_a_pca <- function(id, ...) {
224261
label = "Plot type",
225262
choices = args$plot_choices,
226263
selected = args$plot_choices[1]
264+
),
265+
conditionalPanel(
266+
condition = sprintf("input['%s'] == 'Elbow plot'", ns("plot_type")),
267+
ui_decorate_teal_data(
268+
ns("d_elbow_plot"),
269+
decorators = select_decorators(args$decorators, "elbow_plot")
270+
)
271+
),
272+
conditionalPanel(
273+
condition = sprintf("input['%s'] == 'Circle plot'", ns("plot_type")),
274+
ui_decorate_teal_data(
275+
ns("d_circle_plot"),
276+
decorators = select_decorators(args$decorators, "circle_plot")
277+
)
278+
),
279+
conditionalPanel(
280+
condition = sprintf("input['%s'] == 'Biplot'", ns("plot_type")),
281+
ui_decorate_teal_data(
282+
ns("d_biplot"),
283+
decorators = select_decorators(args$decorators, "biplot")
284+
)
285+
),
286+
conditionalPanel(
287+
condition = sprintf("input['%s'] == 'Eigenvector plot'", ns("plot_type")),
288+
ui_decorate_teal_data(
289+
ns("d_eigenvector_plot"),
290+
decorators = select_decorators(args$decorators, "eigenvector_plot")
291+
)
227292
)
228293
),
229294
teal.widgets::panel_item(
@@ -289,7 +354,7 @@ ui_a_pca <- function(id, ...) {
289354
}
290355

291356
# Server function for the PCA module
292-
srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, plot_width, ggplot2_args) {
357+
srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, plot_width, ggplot2_args, decorators) {
293358
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter")
294359
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI")
295360
checkmate::assert_class(data, "reactive")
@@ -549,7 +614,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
549614
)
550615

551616
cols <- c(getOption("ggplot2.discrete.colour"), c("lightblue", "darkred", "black"))[1:3]
552-
g <- ggplot(mapping = aes_string(x = "component", y = "value")) +
617+
elbow_plot <- ggplot(mapping = aes_string(x = "component", y = "value")) +
553618
geom_bar(
554619
aes(fill = "Single variance"),
555620
data = dplyr::filter(elb_dat, metric == "Proportion of Variance"),
@@ -569,8 +634,6 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
569634
scale_fill_manual(values = c("Cumulative variance" = cols[2], "Single variance" = cols[1])) +
570635
ggthemes +
571636
themes
572-
573-
print(g)
574637
},
575638
env = list(
576639
ggthemes = parsed_ggplot2_args$ggtheme,
@@ -628,7 +691,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
628691
y = sin(seq(0, 2 * pi, length.out = 100))
629692
)
630693

631-
g <- ggplot(pca_rot) +
694+
circle_plot <- ggplot(pca_rot) +
632695
geom_point(aes_string(x = x_axis, y = y_axis)) +
633696
geom_label(
634697
aes_string(x = x_axis, y = y_axis, label = "label"),
@@ -640,7 +703,6 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
640703
labs +
641704
ggthemes +
642705
themes
643-
print(g)
644706
},
645707
env = list(
646708
x_axis = x_axis,
@@ -861,8 +923,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
861923
qenv,
862924
substitute(
863925
expr = {
864-
g <- plot_call
865-
print(g)
926+
biplot <- plot_call
866927
},
867928
env = list(
868929
plot_call = Reduce(function(x, y) call("+", x, y), pca_plot_biplot_expr)
@@ -871,8 +932,8 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
871932
)
872933
}
873934

874-
# plot pc_var ----
875-
plot_pc_var <- function(base_q) {
935+
# plot eigenvector_plot ----
936+
plot_eigenvector <- function(base_q) {
876937
pc <- input$pc
877938
ggtheme <- input$ggtheme
878939

@@ -938,10 +999,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
938999
expr = {
9391000
pca_rot <- pca$rotation[, pc, drop = FALSE] %>%
9401001
dplyr::as_tibble(rownames = "Variable")
941-
942-
g <- plot_call
943-
944-
print(g)
1002+
eigenvector_plot <- plot_call
9451003
},
9461004
env = list(
9471005
pc = pc,
@@ -951,23 +1009,54 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
9511009
)
9521010
}
9531011

954-
# plot final ----
955-
output_q <- reactive({
956-
req(computation())
957-
teal::validate_inputs(iv_r())
958-
teal::validate_inputs(iv_extra, header = "Plot settings are required")
1012+
# qenvs ---
1013+
output_q <- lapply(
1014+
list(
1015+
elbow_plot = plot_elbow,
1016+
circle_plot = plot_circle,
1017+
biplot = plot_biplot,
1018+
eigenvector_plot = plot_eigenvector
1019+
),
1020+
function(fun) {
1021+
reactive({
1022+
req(computation())
1023+
teal::validate_inputs(iv_r())
1024+
teal::validate_inputs(iv_extra, header = "Plot settings are required")
1025+
fun(computation())
1026+
})
1027+
}
1028+
)
9591029

960-
switch(input$plot_type,
961-
"Elbow plot" = plot_elbow(computation()),
962-
"Circle plot" = plot_circle(computation()),
963-
"Biplot" = plot_biplot(computation()),
964-
"Eigenvector plot" = plot_pc_var(computation()),
1030+
decorated_q <- mapply(
1031+
function(obj_name, q) {
1032+
srv_decorate_teal_data(
1033+
id = sprintf("d_%s", obj_name),
1034+
data = q,
1035+
decorators = select_decorators(decorators, obj_name),
1036+
expr = reactive({
1037+
substitute(print(.plot), env = list(.plot = as.name(obj_name)))
1038+
}),
1039+
expr_is_reactive = TRUE
1040+
)
1041+
},
1042+
names(output_q),
1043+
output_q
1044+
)
1045+
1046+
# plot final ----
1047+
decorated_output_q <- reactive({
1048+
switch(req(input$plot_type),
1049+
"Elbow plot" = decorated_q$elbow_plot(),
1050+
"Circle plot" = decorated_q$circle_plot(),
1051+
"Biplot" = decorated_q$biplot(),
1052+
"Eigenvector plot" = decorated_q$eigenvector_plot(),
9651053
stop("Unknown plot")
9661054
)
9671055
})
9681056

9691057
plot_r <- reactive({
970-
output_q()[["g"]]
1058+
plot_name <- gsub(" ", "_", tolower(req(input$plot_type)))
1059+
req(decorated_output_q())[[plot_name]]
9711060
})
9721061

9731062
pws <- teal.widgets::plot_with_settings_srv(
@@ -1034,7 +1123,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
10341123

10351124
teal.widgets::verbatim_popup_srv(
10361125
id = "rcode",
1037-
verbatim_content = reactive(teal.code::get_code(output_q())),
1126+
verbatim_content = reactive(teal.code::get_code(req(decorated_output_q()))),
10381127
title = "R Code for PCA"
10391128
)
10401129

@@ -1057,7 +1146,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
10571146
card$append_text("Comment", "header3")
10581147
card$append_text(comment)
10591148
}
1060-
card$append_src(teal.code::get_code(output_q()))
1149+
card$append_src(teal.code::get_code(req(decorated_output_q())))
10611150
card
10621151
}
10631152
teal.reporter::simple_reporter_srv("simple_reporter", reporter = reporter, card_fun = card_fun)

0 commit comments

Comments
 (0)