diff --git a/R/tm_a_regression.R b/R/tm_a_regression.R index 40892de2a..8641bc62b 100644 --- a/R/tm_a_regression.R +++ b/R/tm_a_regression.R @@ -43,22 +43,50 @@ #' #' @inherit shared_params return #' -#' @section Decorating module's output: -#' Outputs produced by the module can be modified for any application. To do so, one needs to provide -#' [teal_transform_module()] with `server` modifying outputed object in `data` (`teal_data`). Each -#' module might have a different output object name and it's type, so there is no standard code to achieve this. -#' However, `teal` provides couple wrappers which can simplify the process of decorating module's outputs: -#' - `decorator` as a `language`: provide a simple expression to modify object of interests. For example -#' `g <- g + ggtitle("Custom Title")`. Provided expression must be a working expression within module internals. -#' - `decorator` as a `function`: provide a function which will take the output object and modify it in desired way. -#' When function is provided object names don't need to match module's internal naming. For example -#' `function(x) x <- x + ggtitle("Custom Title")`. +#' @section Decorating Module Outputs: +#' +#' Decorating module outputs involves modifying the tables and plots generated by a module. This module provides the +#' ability to execute custom R code to adjust the visual or structural properties of objects displayed within the +#' application. +#' +#' The code specified in [`teal_transform_module`] is executed prior to rendering the outputs in the application. This +#' allows developers to modify attributes such as titles, labels, sizes, limits, and other features of rendered tables +#' and plots. However, decorators should be applied with careful consideration of the module's internal object names +#' to ensure compatibility. +#' +#' To customize an output, developers need to identify the name of the table or plot to be modified within the +#' `teal_transform_module`. This requires specifying a `server` function that modifies the targeted object in the +#' `data` object (of class `teal_data`). Since each module may use different internal object names or types for its +#' outputs, there is no universal code to achieve this. However, `teal` provides convenient wrappers to simplify the +#' process of decorating module outputs: +#' +#' - **Decorator as a Language/Expression**: Specify a simple R expression to modify the object of interest. +#' For example,`plot <- plot + ggtitle("Custom Title")`. The expression must be valid and compatible with the module's +#' internal environment. +#' - **Decorator as a Function**: Provide a function that accepts the output object and modifies it as desired. +#' When using this approach, the function does not need to align with the module’s internal naming conventions for its +#' objects. +#' +#' For additional details and examples of decorators, refer to the vignette +#' `vignette("decorate-modules-output", package = "teal")` or the [`teal_transform_module()`] documentation. +#' +#' @section Decorating `tm_a_regression`: +#' +#' This module creates below objects that can be modified with decorators: +#' - `plot` (`ggplot2`)#' #' #' @examplesShinylive #' library(teal.modules.general) #' interactive <- function() TRUE #' {{ next_example }} #' @examples +#' +#' footnote_regression <- teal_transform_module( +#' server = make_teal_transform_server(expression( +#' plot <- plot + labs(caption = deparse(summary(fit)[[1]])) +#' )) +#' ) +#' #' # general data example #' data <- teal_data() #' data <- within(data, { @@ -90,7 +118,8 @@ #' multiple = TRUE, #' fixed = FALSE #' ) -#' ) +#' ), +#' decorators = list(footnote_regression) #' ) #' ) #' ) @@ -135,7 +164,8 @@ #' multiple = TRUE, #' fixed = FALSE #' ) -#' ) +#' ), +#' decorators = list(footnote_regression) #' ) #' ) #' ) @@ -295,37 +325,37 @@ ui_a_regression <- function(id, decorators, ...) { conditionalPanel( condition = "input.plot_type == 'Response vs Regressor'", ns = ns, - ui_transform_data(ns("d_0"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_0"), transformators = decorators[[1]]) ), conditionalPanel( condition = "input.plot_type == 'Residuals vs Fitted'", ns = ns, - ui_transform_data(ns("d_1"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_1"), transformators = decorators[[1]]) ), conditionalPanel( condition = "input.plot_type == 'Normal Q-Q'", ns = ns, - ui_transform_data(ns("d_2"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_2"), transformators = decorators[[1]]) ), conditionalPanel( condition = "input.plot_type == 'Scale-Location'", ns = ns, - ui_transform_data(ns("d_3"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_3"), transformators = decorators[[1]]) ), conditionalPanel( condition = "input.plot_type == 'Cook\\'s distance'", ns = ns, - ui_transform_data(ns("d_4"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_4"), transformators = decorators[[1]]) ), conditionalPanel( condition = "input.plot_type == 'Residuals vs Leverage'", ns = ns, - ui_transform_data(ns("d_5"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_5"), transformators = decorators[[1]]) ), conditionalPanel( condition = "input.plot_type == 'Cook\\'s dist vs Leverage'", ns = ns, - ui_transform_data(ns("d_6"), transforms = decorators[[1]]) + ui_teal_transform_data(ns("d_6"), transformators = decorators[[1]]) ), ), checkboxInput(ns("show_outlier"), label = "Display outlier labels", value = TRUE), @@ -610,6 +640,7 @@ srv_a_regression <- function(id, }) output_plot_0 <- reactive({ + fit <- fit_r()[["fit"]] ANL <- anl_merged_q()[["ANL"]] @@ -677,10 +708,10 @@ srv_a_regression <- function(id, expr = { class(fit$residuals) <- NULL data <- fortify(fit) - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) @@ -721,10 +752,10 @@ srv_a_regression <- function(id, substitute( expr = { smoothy <- smooth(data$.fitted, data$.resid) - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) @@ -780,10 +811,10 @@ srv_a_regression <- function(id, plot_base, substitute( expr = { - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) @@ -823,10 +854,10 @@ srv_a_regression <- function(id, substitute( expr = { smoothy <- smooth(data$.fitted, sqrt(abs(data$.stdresid))) - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) @@ -889,10 +920,10 @@ srv_a_regression <- function(id, plot_base, substitute( expr = { - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) @@ -944,10 +975,10 @@ srv_a_regression <- function(id, substitute( expr = { smoothy <- smooth(data$.hat, data$.stdresid) - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) @@ -994,22 +1025,22 @@ srv_a_regression <- function(id, substitute( expr = { smoothy <- smooth(data$.hat, data$.cooksd) - g <- plot + plot <- graph }, env = list( - plot = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) + graph = Reduce(function(x, y) call("+", x, y), c(plot, parsed_ggplot2_args)) ) ) ) }) - decorated_output_0 <- srv_transform_data(id = "d_0", data = output_plot_0, transforms = decorators[[1]]) - decorated_output_1 <- srv_transform_data(id = "d_1", data = output_plot_1, transforms = decorators[[1]]) - decorated_output_2 <- srv_transform_data(id = "d_2", data = output_plot_2, transforms = decorators[[1]]) - decorated_output_3 <- srv_transform_data(id = "d_3", data = output_plot_3, transforms = decorators[[1]]) - decorated_output_4 <- srv_transform_data(id = "d_4", data = output_plot_4, transforms = decorators[[1]]) - decorated_output_5 <- srv_transform_data(id = "d_5", data = output_plot_5, transforms = decorators[[1]]) - decorated_output_6 <- srv_transform_data(id = "d_6", data = output_plot_6, transforms = decorators[[1]]) + decorated_output_0 <- srv_teal_transform_data(id = "d_0", data = output_plot_0, transformators = decorators[[1]]) + decorated_output_1 <- srv_teal_transform_data(id = "d_1", data = output_plot_1, transformators = decorators[[1]]) + decorated_output_2 <- srv_teal_transform_data(id = "d_2", data = output_plot_2, transformators = decorators[[1]]) + decorated_output_3 <- srv_teal_transform_data(id = "d_3", data = output_plot_3, transformators = decorators[[1]]) + decorated_output_4 <- srv_teal_transform_data(id = "d_4", data = output_plot_4, transformators = decorators[[1]]) + decorated_output_5 <- srv_teal_transform_data(id = "d_5", data = output_plot_5, transformators = decorators[[1]]) + decorated_output_6 <- srv_teal_transform_data(id = "d_6", data = output_plot_6, transformators = decorators[[1]]) output_q <- reactive({ @@ -1026,7 +1057,7 @@ srv_a_regression <- function(id, }) fitted <- reactive(output_q()[["fit"]]) - plot_r <- reactive(output_q()[["g"]]) + plot_r <- reactive(output_q()[["plot"]]) # Insert the plot into a plot_with_settings module from teal.widgets pws <- teal.widgets::plot_with_settings_srv( diff --git a/R/tm_outliers.R b/R/tm_outliers.R index 13de794e2..ec2d4cb17 100644 --- a/R/tm_outliers.R +++ b/R/tm_outliers.R @@ -16,7 +16,7 @@ #' @param boxplot_decorator (`list` of `teal_transform_module`) optional, #' decorator for the box plot. #' @param violin_decorator (`list` of `teal_transform_module`) optional, -#' decorator for the violing plot. +#' decorator for the violin plot. #' @param density_decorator (`list` of `teal_transform_module`) optional, #' decorator for the density plot. #' @param cum_dist_decorator (`list` of `teal_transform_module`) optional, @@ -27,25 +27,27 @@ #' #' @inherit shared_params return #' -#' @section Decorating the tables and plots: -#' The act of decoration means to modify the tables and plots output by this module. -#' The module lets app developers do it by allowing them to execute arbitrary R code -#' that modifies the objects displayed by the module. +#' @inheritSection tm_a_regression Decorating Module Outputs +#' @section Decorating `tm_outliers`: #' -#' The module will execute the code contained in [`teal_transform_module`] before -#' rendering the outputs in the application. This lets app developers modify -#' features like: titles, labels, sizes, limits, etc. of the rendered tables -#' and plots. -#' -#' The app developer should apply decorators carefuly with respect to the module's internal -#' object names. To modify an output, app developer needs to find out the name of the table or plot -#' that is being modified by the code in the `teal_transform_module` list element. +#' This module creates below objects that can be modified with decorators: +#' - `table` (`data.frame`) +#' - `plot` (`ggplot2`) #' #' @examplesShinylive #' library(teal.modules.general) #' interactive <- function() TRUE #' {{ next_example }} #' @examples +#' +#' module_decorator <- teal_transform_module( +#' server = make_teal_transform_server( +#' expression({ +#' plot <- plot + ggplot2::ggtitle("A new title") +#' }) +#' ) +#' ) +#' #' # general data example #' data <- teal_data() #' data <- within(data, { @@ -82,7 +84,8 @@ #' multiple = TRUE #' ) #' ) -#' ) +#' ), +#' boxplot_decorator = module_decorator #' ) #' ) #' ) @@ -95,6 +98,15 @@ #' interactive <- function() TRUE #' {{ next_example }} #' @examples +#' +#' module_decorator <- teal_transform_module( +#' server = make_teal_transform_server( +#' expression({ +#' plot <- plot + ggplot2::ggtitle("A new title") +#' }) +#' ) +#' ) +#' #' # CDISC data example #' data <- teal_data() #' data <- within(data, { @@ -105,6 +117,8 @@ #' fact_vars_adsl <- names(Filter(isTRUE, sapply(data[["ADSL"]], is.factor))) #' vars <- choices_selected(variable_choices(data[["ADSL"]], fact_vars_adsl)) #' +#' +#' #' app <- init( #' data = data, #' modules = modules( @@ -131,7 +145,8 @@ #' multiple = TRUE #' ) #' ) -#' ) +#' ), +#' boxplot_decorator = module_decorator #' ) #' ) #' ) @@ -139,27 +154,6 @@ #' shinyApp(app$ui, app$server) #' } #' -#' # Decorators -#' function_decorator <- function(p) { -#' p <- p + ggplot2::ggtitle("A new title") -#' } -#' -#' quote_decorator <- quote({ -#' g <- g + ggplot2::ggtitle("A new title") -#' }) -#' -#' module_decorator <- teal_transform_module( -#' ui = function(id) NULL, -#' srv = function(id, data) { -#' within( -#' data, -#' { -#' g <- g + ggplot2::ggtitle("A new title") -#' } -#' ) -#' } -#' ) -#' #' @export #' tm_outliers <- function(label = "Outliers Module", @@ -251,7 +245,7 @@ ui_outliers <- function(id, ...) { output = teal.widgets::white_small_well( uiOutput(ns("total_outliers")), DT::dataTableOutput(ns("summary_table")), - ui_transform_data(ns("table_decorator"), transforms = args$table_decorator), + ui_teal_transform_data(ns("table_decorator"), args$table_decorator), uiOutput(ns("total_missing")), tags$br(), tags$hr(), tabsetPanel( @@ -260,22 +254,22 @@ ui_outliers <- function(id, ...) { "Boxplot", conditionalPanel( condition = sprintf("input['%s'] == 'Box plot'", ns("boxplot_alts")), - ui_transform_data(ns("boxplot_decorator"), args$boxplot_decorator) + ui_teal_transform_data(ns("boxplot_decorator"), args$boxplot_decorator) ), conditionalPanel( condition = sprintf("input['%s'] == 'Violin plot'", ns("boxplot_alts")), - ui_transform_data(ns("violin_decorator"), args$violin_decorator) + ui_teal_transform_data(ns("violin_decorator"), args$violin_decorator) ), teal.widgets::plot_with_settings_ui(id = ns("box_plot")) ), tabPanel( "Density Plot", - ui_transform_data(ns("density_decorator"), args$density_decorator), + ui_teal_transform_data(ns("density_decorator"), args$density_decorator), teal.widgets::plot_with_settings_ui(id = ns("density_plot")) ), tabPanel( "Cumulative Distribution Plot", - ui_transform_data(ns("cum_dist_decorator"), args$cum_dist_decorator), + ui_teal_transform_data(ns("cum_dist_decorator"), args$cum_dist_decorator), teal.widgets::plot_with_settings_ui(id = ns("cum_density_plot")) ) ), @@ -827,7 +821,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, teal.code::eval_code( common_code_q(), substitute( - expr = g <- plot_call + + expr = plot <- plot_call + scale_color_manual(values = c("TRUE" = "red", "FALSE" = "black")) + labs + ggthemes + themes, env = list( @@ -888,7 +882,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, teal.code::eval_code( common_code_q(), substitute( - expr = g <- plot_call + labs + ggthemes + themes, + expr = plot <- plot_call + labs + ggthemes + themes, env = list( plot_call = plot_call, labs = parsed_ggplot2_args$labs, @@ -989,7 +983,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, teal.code::eval_code( qenv, substitute( - expr = g <- plot_call + + expr = plot <- plot_call + geom_point(data = outlier_points, aes(x = outlier_var_name, y = y, color = is_outlier_selected)) + scale_color_manual(values = c("TRUE" = "red", "FALSE" = "black")) + labs + ggthemes + themes, @@ -1004,10 +998,14 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, ) }) - decorated_boxplot_q <- srv_transform_data("boxplot_decorator", data = boxplot_q, transforms = decorators$boxplot) - decorated_violin_q <- srv_transform_data("violin_decorator", data = boxplot_q, transforms = decorators$violin) - decorated_density_plot_q <- srv_transform_data("density_decorator", data = density_plot_q, transforms = decorators$density) - decorated_cumulative_plot_q <- srv_transform_data("cum_dist_decorator", data = cumulative_plot_q, transforms = decorators$cum_dist) + decorated_boxplot_q <- + srv_teal_transform_data("boxplot_decorator", data = boxplot_q, transformators = decorators$boxplot_decorator) + decorated_violin_q <- + srv_teal_transform_data("violin_decorator", data = boxplot_q, transformators = decorators$violin_decorator) + decorated_density_plot_q <- + srv_teal_transform_data("density_decorator", data = density_plot_q, transformators = decorators$density_decorator) + decorated_cumulative_plot_q <- + srv_teal_transform_data("cum_dist_decorator", data = cumulative_plot_q, transformators = decorators$cum_dist_decorator) final_q <- reactive({ req(input$tabs) @@ -1030,7 +1028,7 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, setdiff(names(ANL_OUTLIER), c("is_outlier_selected", "order")), table_columns ) - ANL_OUTLIER_EXTENDED[ANL_OUTLIER_EXTENDED$is_outlier_selected, columns_index] + table <- ANL_OUTLIER_EXTENDED[ANL_OUTLIER_EXTENDED$is_outlier_selected, columns_index] }, env = list( table_columns = input$table_ui_columns @@ -1039,6 +1037,12 @@ srv_outliers <- function(id, data, reporter, filter_panel_api, outlier_var, ) }) + + decorated_final_q <- + srv_teal_transform_data("cum_dist_decorator", data = final_q, transformators = decorators$table_decorator) + # TODO: + # reuse decorated_final_q in table generation + # slider text output$ui_outlier_help <- renderUI({ req(input$method) diff --git a/man/tm_a_regression.Rd b/man/tm_a_regression.Rd index 82a40d327..92ee60d00 100644 --- a/man/tm_a_regression.Rd +++ b/man/tm_a_regression.Rd @@ -19,7 +19,7 @@ tm_a_regression( default_plot_type = 1, default_outlier_label = "USUBJID", label_segment_threshold = c(0.5, 0, 10), - decorator = list(default = teal_transform_module()) + decorators = list(default = teal_transform_module()) ) } \arguments{ @@ -97,6 +97,8 @@ It can take the following forms: It takes the form of \code{c(value, min, max)} and it is passed to the \code{value_min_max} argument in \code{teal.widgets::optionalSliderInputValMinMax}. }} + +\item{decorators}{(\code{list} of \code{teal_transform_module})} } \value{ Object of class \code{teal_module} to be used in \code{teal} applications. @@ -111,7 +113,53 @@ visualize residuals, and identify outliers. For more examples, please see the vignette "Using regression plots" via \code{vignette("using-regression-plots", package = "teal.modules.general")}. } +\section{Decorating Module Outputs}{ + + +Decorating module outputs involves modifying the tables and plots generated by a module. This module provides the +ability to execute custom R code to adjust the visual or structural properties of objects displayed within the +application. + +The code specified in \code{\link{teal_transform_module}} is executed prior to rendering the outputs in the application. This +allows developers to modify attributes such as titles, labels, sizes, limits, and other features of rendered tables +and plots. However, decorators should be applied with careful consideration of the module's internal object names +to ensure compatibility. + +To customize an output, developers need to identify the name of the table or plot to be modified within the +\code{teal_transform_module}. This requires specifying a \code{server} function that modifies the targeted object in the +\code{data} object (of class \code{teal_data}). Since each module may use different internal object names or types for its +outputs, there is no universal code to achieve this. However, \code{teal} provides convenient wrappers to simplify the +process of decorating module outputs: +\itemize{ +\item \strong{Decorator as a Language/Expression}: Specify a simple R expression to modify the object of interest. +For example,\code{plot <- plot + ggtitle("Custom Title")}. The expression must be valid and compatible with the module's +internal environment. +\item \strong{Decorator as a Function}: Provide a function that accepts the output object and modifies it as desired. +When using this approach, the function does not need to align with the module’s internal naming conventions for its +objects. +} + +For additional details and examples of decorators, refer to the vignette +\code{vignette("decorate-modules-output", package = "teal")} or the \code{\link[=teal_transform_module]{teal_transform_module()}} documentation. +} + +\section{Decorating \code{tm_a_regression}}{ + + +This module creates below objects that can be modified with decorators: +\itemize{ +\item \code{plot} (\code{ggplot2})#' +} +} + \examples{ + +footnote_regression <- teal_transform_module( + server = make_teal_transform_server(expression( + plot <- plot + labs(caption = deparse(summary(fit)[[1]])) + )) +) + # general data example data <- teal_data() data <- within(data, { @@ -143,7 +191,8 @@ app <- init( multiple = TRUE, fixed = FALSE ) - ) + ), + decorators = footnote_regression ) ) ) @@ -183,7 +232,8 @@ app <- init( multiple = TRUE, fixed = FALSE ) - ) + ), + decorators = footnote_regression ) ) ) @@ -195,14 +245,14 @@ if (interactive()) { \section{Examples in Shinylive}{ \describe{ \item{example-1}{ - \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokqUBiXVIhrqu-lCkULpwAB6wqCJKAUEGxlzUAPoxUDbRgcFGugDutKQAFirsKbi6IEq6uoxwAI6CtNXsPqKkxNREjIoQlQDCAPIATHG6-QNKAL5dSmiowyp57BX+GboAvMtBuEt8QiKia7o7wmKL3ZW6pDCJUInVEtWiolYQp+fn1FD0cH7rCmBYcHuYieJD+WzObweqBIoj06xSiTCpGYGkSolQcAIrze5xS0HgBz+ozBSxxulhIg0BwpmNIaIxWNJZN0Hy+P10fwAyt9abotCxaJ8RIgSRDmQR8kRaAQxISwIJUEEANZwUXM840jRwfhyhXK1V4JlkmDCTSROG6ABiAEEADKc5zg9WVEy0ULag4OFxG85dMlyJ04u4PUQdA4IpEounozHYsl42AWomDNXMzWkak81Exxlisms75y7mUjP8xiC+h7EWGvM4iVSmX7dZlisiRL16UnFLAYDJsZgAC6A9KjMIJAIYI5YDs1UC8DIfzkAZ9b3THt+Y4sqfVJuoZpEnqcjpX51d7p16xt9tctcqfpx97vSy6XVoJl07BU5Cj2jgNls5RnKIhQQKw1roOwMwACT1KU0GwowOidBMShgOMA5AA}{Open in Shinylive} + \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQTRIqQje4AfUY4CSDRUSsIA2Muan9SZghRT0YYfz4hEXYlXV1RGR1GXQBeXRgoAGsAmLiEpKIU-zzGAvY4AA9UUPDrbJzdVGpvKP7B0l0Aal1qKHpRdgI0TRJi3X44VBY89lFBGDK2dhNaUjlgYABGAF1LuUVI3VulO6UAYl0pCDVqVahSKF12rABnAlPxfv8jLpqmC-jZQeDhgB3Y4ACxU7BhUFwuhAvSCAEdBLQguxPqJSMRBow7jkAMIAeQATMMGYylABfZ7QdDDFTHLL3TErTG4XrpYRiFbikRzXo5UipKCBYJdCICvp9ab0ODfEoKMBYFViboQfWi+4a0KoEh5YXg-zteLqUiNVBwAjqjV9THQeArfWss1yr15EQaFah90u0Ruj3Br1TGY6-1gADKOqjui0LFoMxEiCDFoTugIKKItAIkr1YEEqD+lULxb6kY0cH4Kdr9ZBeHjXpgwk0wJWADEAIIAGVTznNTZyRzabZWDhcvZyNK9chnXqCIWN9TtfwdbSdGld7s9CZ9sD01cDPaLGpbYxKT7PcYfXq1yer6bDY2zjC5vQMoFves4lmWFZVlmOZ5gEpblpWcyYmcAZMvq1zYnGhAkAQZq6PqdhBL88BkPqtxbk2T6LtWxAWI2s79tQg4iEuTjTqufTzjRuhjpOrgfmuvabr2azEMwpD1KIKyeN4vjkMqu5hBEwbrncdy0CYujsCo5DMJYOg2LYuL3KIaIQKwo7oOwaCoAAJES2K2XZTQFHc7JKGA7KXEAA}{Open in Shinylive} \if{html}{\out{}} - \if{html}{\out{}} + \if{html}{\out{}} } \item{example-2}{ - \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokqUBiXQGEAIgEkAZS9dfihSKF04AA9YVBElUPCDYy5qAH1EqBsEsIijXQB3WlIACxV2TNxdECVdXUY4AEdBWgb2CDFSYmoiRkUIOoBBHwCAGWT64bGlAF9+gCsiFTSAazhWUQrc23z+OBMoYVI0gn5aUQI0xeW1jeBoeE3MuQBdN2h0CZVi9lqQ3N0AF5-uFcH8+EIRKIgboIcIxL8BnVdKQYGkoGkGhIGqJRFYIIjkcjqFB6HBqDCFGAsHBsWI8SQqWCkUScagSKI9MDMmloqRmBo0qJUHACISicjMg8uboqVNRky-hLdJyRBoYarRcdhaLxcq6iSyRTgVSAuStbotCxaKSRIhFSz9QQSksCGJKWAAEIAWSwAGksABGB365GajRwfgen3+oMh0Owo60OIygBig1GAWczITploUUjMIcLiVEv6yrkOYlWJxol6MJ5fIF2pFYtLRKlsBlcpGCrw7bD5vVwPDLd1A6JhvJHrNatIluttrE9v7juVztd7uBVsYNvoIhOLtobqeuWAwB70zALxeVTbYEGAHFXHhZWAswANJlvrCDLwvuRKwnQc50LE0H2feNQxgJMUyLJxs2AuoTHzMDdHTTNXDXZFyzLUtcP6fpaBMXR2BUchm20OAbFsGokVEMoIFYQZ0HYNBUAAEhaKp2I4zlGB0PpZiUMAZheIA}{Open in Shinylive} + \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokqUBiXQGEAIgEkAZS9dfihSKF04AA9YVBElUPCDYy5qAH1EqBsEsIijXQB3WlIACxV2TNxdECVdXUY4AEdBWgb2CDFSYmoiRkUIOoBBHwCAGWT64bGlAF9+gCsiFTSAazhWUQrc23z+OBMoYVI0gn5aUQI0xeW1jeBoeE3MuQBdN2h0CZVi9lqQ3N0AF5-uFcH8+EIRKIgboIcIxL8BnVdKQYGkoGkGhIGqJRFYIIjkcjqFB6HBqDCFGAsHBsWI8SQqWCkUScagSKI9MDMmloqRmBo0qJUHACISicjMg8uboqVNRky-hLdJyRBoYarRcdhaLxcq6iSyRTgVSAuStbotCxaKSRIhFSz9QQSksCGJKWAAEIAWSwAGksABGB365GajRwfgen3+oMh0Owo60OIygBig1GAWczITploUUjMIcLiVEv6yrkOYlWJxol6MJ5fIF2pFYtLRKlsBlcpGCrw7bD5vVwPDLd1A6JhvJHrNatIluttrE9v7juVztd7uBVsYNvoIhOLtobqeuWAwB70zALxeVTbYEGAHFXHhZWAswANJlvrCDLwvuRKwnQc50LE0H2feNQxgJMUyLJxs2AuoTHzMDdHTTNXDXZFyzLKsO1FXowl6aFgRMIgiFICBKLgTFaVrfFS1w-p+loExdHYFRyGbbQ4BsWwaiRUQyggVhBnQdg0FQAASFoqik6TOUYHQ+lmJQwBmF4gA}{Open in Shinylive} \if{html}{\out{}} - \if{html}{\out{}} + \if{html}{\out{}} } } } diff --git a/man/tm_outliers.Rd b/man/tm_outliers.Rd index 7ab4e6585..940198ad1 100644 --- a/man/tm_outliers.Rd +++ b/man/tm_outliers.Rd @@ -54,19 +54,19 @@ with text placed before the output to put the output into context. For example a \item{post_output}{(\code{shiny.tag}) optional, text or UI element to be displayed after the module's output, adding context or further instructions. Elements like \code{shiny::helpText()} are useful.} -\item{table_decorator}{(\code{teal_transform_module}, \code{language} or \code{function}) optional, +\item{table_decorator}{(\code{list} of \code{teal_transform_module}) optional, decorator for the table.} -\item{boxplot_decorator}{(\code{teal_transform_module}, \code{language} or \code{function}) optional, +\item{boxplot_decorator}{(\code{list} of \code{teal_transform_module}) optional, decorator for the box plot.} -\item{violin_decorator}{(\code{teal_transform_module}, \code{language} or \code{function}) optional, -decorator for the violing plot.} +\item{violin_decorator}{(\code{list} of \code{teal_transform_module}) optional, +decorator for the violin plot.} -\item{density_decorator}{(\code{teal_transform_module}, \code{language} or \code{function}) optional, +\item{density_decorator}{(\code{list} of \code{teal_transform_module}) optional, decorator for the density plot.} -\item{cum_dist_decorator}{(\code{teal_transform_module}, \code{language} or \code{function}) optional, +\item{cum_dist_decorator}{(\code{list} of \code{teal_transform_module}) optional, decorator for the cumulative distribution plot.} } \value{ @@ -77,50 +77,56 @@ Module to analyze and identify outliers using different methods such as IQR, Z-score, and Percentiles, and offers visualizations including box plots, density plots, and cumulative distribution plots to help interpret the outliers. } -\section{Decorating the tables and plots}{ +\section{Decorating \code{tm_outliers}}{ -The act of decoration means to modify the tables and plots output by this module. -The module lets app developers do it by allowing them to execute arbitrary R code -that modifies the objects displayed by the module. -The decorating parameters accept one of the three types of objects as decorators: +This module creates below objects that can be modified with decorators: \itemize{ -\item \code{teal_transform_module} -\item \code{language} -\item \code{function} of the output object. +\item \code{table} (\code{data.frame}) +\item \code{plot} (\code{ggplot2}) +} } -The module will execute the code contained in either of the objects just before -rendering the outputs in the application. This lets app developers modify -features like: titles, labels, sizes, limits, etc. of the rendered tables -and plots. - -The app developer can pass either of the types. See examples for the proper -use of each of the type of the decorator. +\section{Decorating Module Outputs}{ -IMPORTANT -The \code{language} and \code{teal_transform_module} decorators are required by the module -to overwrite the binding of the output, otherwise the effect of the decorator -is not going to be visible. E.g.: -\if{html}{\out{
}}\preformatted{# The module uses `g` variable for the plot +Decorating module outputs involves modifying the tables and plots generated by a module. This module provides the +ability to execute custom R code to adjust the visual or structural properties of objects displayed within the +application. -# Will work -lang_dec <- quote(\{ - g <- g + ggplot2::ggtitle("A new title") -\}) +The code specified in \code{\link{teal_transform_module}} is executed prior to rendering the outputs in the application. This +allows developers to modify attributes such as titles, labels, sizes, limits, and other features of rendered tables +and plots. However, decorators should be applied with careful consideration of the module's internal object names +to ensure compatibility. -# Will not work because the decorater overwrites `plot` instead of `g` -lang_dec <- quote(\{ - plot <- g + ggplot2::ggtitle("A new title") -\}) -}\if{html}{\out{
}} +To customize an output, developers need to identify the name of the table or plot to be modified within the +\code{teal_transform_module}. This requires specifying a \code{server} function that modifies the targeted object in the +\code{data} object (of class \code{teal_data}). Since each module may use different internal object names or types for its +outputs, there is no universal code to achieve this. However, \code{teal} provides convenient wrappers to simplify the +process of decorating module outputs: +\itemize{ +\item \strong{Decorator as a Language/Expression}: Specify a simple R expression to modify the object of interest. +For example,\code{plot <- plot + ggtitle("Custom Title")}. The expression must be valid and compatible with the module's +internal environment. +\item \strong{Decorator as a Function}: Provide a function that accepts the output object and modifies it as desired. +When using this approach, the function does not need to align with the module’s internal naming conventions for its +objects. +} -The app developer can discover the bindings used for the outputs by inspecting -the R code generated by the module. +For additional details and examples of decorators, refer to the vignette +\code{vignette("decorate-modules-output", package = "teal")} or the \code{\link[=teal_transform_module]{teal_transform_module()}} documentation. } \examples{ + +module_decorator <- teal_transform_module( + server = make_teal_transform_server( + expression({ + plot <- plot + ggplot2::ggtitle("A new title") + }) + ) +) + # general data example data <- teal_data() data <- within(data, { @@ -157,7 +163,8 @@ app <- init( multiple = TRUE ) ) - ) + ), + boxplot_decorator = module_decorator ) ) ) @@ -165,6 +172,15 @@ if (interactive()) { shinyApp(app$ui, app$server) } + +module_decorator <- teal_transform_module( + server = make_teal_transform_server( + expression({ + plot <- plot + ggplot2::ggtitle("A new title") + }) + ) +) + # CDISC data example data <- teal_data() data <- within(data, { @@ -175,6 +191,8 @@ join_keys(data) <- default_cdisc_join_keys[names(data)] fact_vars_adsl <- names(Filter(isTRUE, sapply(data[["ADSL"]], is.factor))) vars <- choices_selected(variable_choices(data[["ADSL"]], fact_vars_adsl)) + + app <- init( data = data, modules = modules( @@ -201,7 +219,8 @@ app <- init( multiple = TRUE ) ) - ) + ), + boxplot_decorator = module_decorator ) ) ) @@ -209,39 +228,18 @@ if (interactive()) { shinyApp(app$ui, app$server) } -# Decorators -function_decorator <- function(p) { - p <- p + ggplot2::ggtitle("A new title") -} - -quote_decorator <- quote({ - g <- g + ggplot2::ggtitle("A new title") -}) - -module_decorator <- teal_transform_module( - ui = function(id) NULL, - srv = function(id, data) { - within( - data, - { - g <- g + ggplot2::ggtitle("A new title") - } - ) - } -) - } \section{Examples in Shinylive}{ \describe{ \item{example-1}{ - \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokqUBiXVIhrqu-lCkULpwAB6wqCJKAUEGxlzUAPoxUDbRgcFGugDutKQAFirsKbi6IEq6ugDCAPIATHHV9RVNdcDACmCojLQwLKyJANZwrJ0AumONonAAjokiEOwQjETZ7LV1cooQAL7bAFZEKkMjosUZtlmHx8OsZ9cQJxydG52lL814up3dvf1PnS2bggWhYokaBHyRwIYkS0xEGjg-HYoJ6UHoIkSkOhYnOQXaHzq4zGpQI7E6AAVqFAyG9vmA7KxUHA6Z07Iw4IF4LSwFttko0KhGio8uSIJUUroALz+DK4Fp8IQicEyxXCXEtSqkGCJIiCUh0GRnTWVXR6g20GSJVHS3R0USkMWm52yoKJMKkZgaOHMskml2ummwPQywlvf0u+FwDS2qPe0S+p0Bl3U+hwPyhsAAZXT0dIulRtHRIkQ4fFyed2NoMJVBZYRYxcCxUOruJSBLArzAE1J5MIJAIrLAglQQWGgLk8vLFcqcfI-FtnRHY5ZeAjAZgwk0kRDugAYgBBAAyWecU5nppMtFCSNth5Prmnye2FZfLsnEYIgTgEiIPS-SQ2jK9qOuugbuqEnrqKQPrRkmFYpNA8CLp2nznjOV7UOQjC2ph2GwX6T4VqitYkehF5VjWtqgtQghNpRbYZB2XY9nWjCiAAJHOSIfkRybcQuMo0XRzY4mc7YdKhRLdiSbGcQJvEXpUm5YbQO62g4LhgZUb4Brppr6W+2zbLQJi6OwKjYdB2hwDYtjlOWoiFBArAHug7CChxgi0KUnnTIwOiMNsOxKGAOxjEAA}{Open in Shinylive} + \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQ+QkQH1+cYsykRIwGxlzU3qTMEKImwTDensJw7Eq6uqIyOiEAvLowUADWcJFw3JHRsfHemYzZqRDp6XAAHqiMYqJWEOwgaU3pqNREpKG6QyO6ANS6EhITpABMiIhzmqQiqWAAgroQcADuuusiCmCKjU0AvhfpFxdKAMSz0rK6-FCkULqtsENwSg+XzG4V8nygNkB4LGB1opAAFip2ECoLhdH1LgBhADyizGOMW-QJwGAZ3atAKbG8xVYZwAunSxpkAI7eEQ9CCMIgHdgEuQXG5KABWRBU1LgrFEyPBtiMuhFYppUoVEHFHDOBLOaI1uK1ujJjApLFYarO-PcWhYojGBHhooIYhqcBEGjg-HYlsNUHoPlt9rE0q+JJ1hLADLRBC2AAVqFAyHqznZWKgAXh9WA7B1PvB4+dzRAlGhUGMVHCGukUbo8ijcP0kiJrXl6wH+ulSAkiIINrQZFLW01O92ZN5PVXdHRRKRywMmijvK0oupSDUU5H+zP3uDoPAxyGteuZ5kXaM8kf-MvRKvpxuBrH6M7d2AAMrO8+6T20b0iRD7y43pp+rQDqNu+LCfj6JSAcBgZQMGYCamGdIRlsxAWAmYCCKgXzFGaci1n+-4ZK+rr8I+mHYam+GEU0MDCJo-xjgAYtsAAyT7OFR1GmLQLRukxrHsQeAy3DeInCZxAwEJ8cASMEQHlKOeQTlOQmbl884tIuGgrv4143ii256Hke54Kp6QmLQ1DkLk3FWcOl66WZTSeiBLkSYRUFiGOlrUIIkF2kBAYonBCHhqBjCiAAJGeJF4U56QxeQpF5D5fneJ5UrBaS8G6ohaIudFxFJXFBH-rRVm0AxeQOC4TliTO9V3O56T0EQbTDMufgBJ8wRjs2vj+MEPWMP2In3EotAmLo7AqNZS7aCk-Lov0oiIhArDbOg7BFpFgi0GiO21NkApKGAVx0kAA}{Open in Shinylive} \if{html}{\out{}} - \if{html}{\out{}} + \if{html}{\out{}} } \item{example-2}{ - \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokqUBiXQGEAIgEkAZS9dfihSKF04AA9YVBElUPCDYy5qAH1EqBsEsIijXQB3WlIACxV2TNxdECVdXQBBHwCAGWTdRkaWpQBfRQgAKyIVNIBrOFZRCtzbfP44EyhhUjSCflpRAjTB4bGJ4Gh4Scy5AF03CAWNNK0WUTSoflFqNoOxdgAxWmpyRnZ1hxcVVEaDiHEywGACjAnWaUJOJyq6wwl1IREYcgxShujFEbQIJSGBDEaVEcBEGjg-HY2NoUHoIhWBNoRKOuQhUJhcIRpnUy2xdweT0xECUILaKmK7FqIVyugAvDLwrhpXwhCJcQrVcI3tK6qQYGkiIJSHQZJNdXVdEaTbQZNcWPLdHRRKQpRBLR7FVA0tFSMwrqJUHACG7PZ7Mq9HRymrC8Baw6TyaRHYng8tA8HQ2HPdQ6WSo2AAmS07oaXSRIgocr3dmPfjCWJHWX6XBGQ3WeF2dCY1yqiGOQBxVx4XRQgBCAFksABpLAARihGOrtY9qYp-AL9SHVfj2ZgS1ocT0Cve9WaAWcy5XdRMtCilMdp-Prhrtb6b93civdbCcAkaOZbh7UYR1nVdXc6kyH0oj9XkSSDftX2zCNYGPUduy6OMkLDW9vhkR1cJ+eDMwgz1+SbW5vxXetmUbBUbmoQRWxolkpk7SEMNjeEqn5AASNdyH4L9SNXYt1woxjmKZVjwQ4zkwG40tbn4sTBOE7C9wPI9HQBF9r3fbMDI9IzdAMvo+loExdD+VR-U0HQbFsGoa1EMoIFYep0HYEFeMEWgqh80lGB0dEeiUMBuhOIA}{Open in Shinylive} + \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqajGIgEwCu1OAGcMAcwpxm1AJQAdCLTIyoBUrQBucAAQAeALS6AZoIgbaJdnN0AVLAFUAokrcQ+QkQH1+cYsykRIwGxlzU3qTMEKImwTDensJw7Eq6uqIyOiEAvLowUADWcJFw3JHRsfHemYzZqRDp6XAAHqiMYqJWEOwgaU3pqNREpKG6QyO6ANS6EhITpABMiIhzmqQiqWAAgroQcADuuusiCmCKjU0AvhfpFxdKAMS6AMIAIgCSAMovuvxQpCgulasCGcCU-0BY3CvgBUBsELhYwOtFIAAsVOxIVBcLo+pdtm8vgAZMaMQkkpQ3JQAKyIKm8xVYoixcNsRj+cBMUGEpG8BH4tFEBG8dIZTNEwGg8BZ2LkAF13NyNN4tCxRN4oPxRNQxtKxOwAGK0ajkRjsIUOFy40RoIYcbHAYBnCnEs7y+W4oUYZVBRhyANKNWMURjAho+kEMQ1OAiDRwfjsYO0KD0HzhyMGx3OnZEt1gD2432q9Wa7XyB4Qdx2sYqVENdLY3R5bG4fpJEShvIdg39dKkBJEQQbWgyFl9ppDkcyEu5XR0USkBsDJrY7ytKLqPmiVD+Zcr1dw-XN3QuvNnNuXA-pTJx0Z5W-+be7gj76-paip2Mns5fWNP3Rk1TEREAvCd3wzWgoy7QCWBTNMSkg6DWUBJ0z0pAtPV0V8XQAcVcPBTzAAAhABZLAAGksAARjOANL3fAZH3jfgfx2fCwKvRiYF5WgwRPQ1tmJL5nAYxj0hMWgWgTAShJE8CV1ua8lIGOQxIGAgATgCRgig8pgxPBclwUxs4XXFpNxVHc9xMw9AWPPJ0PzdTr0k00ZBPNyzRqF833fYMYIClyIIjKCxBPNVqEERDQuQ7MnPdLCAoAEmY8h+DU2ymP-FiIu4aL+VirM4TQ3MMMLWCQ1SnL0syrj3x400+JEE8rVcerFNslSmjqg96CINphj5PwAgBYITx7Xx-GCMbGAnJT7iUWgTF0C1VGYSwdBsWx8RvDEIFYbZ0HYO1ksEWhcVO2psguK4lDAK55SAA}{Open in Shinylive} \if{html}{\out{}} - \if{html}{\out{}} + \if{html}{\out{}} } } }