| 1 |
templ_ui_constraint <- function(ns, label = "Data Constraint") {
|
|
| 2 | 12x |
tags$div( |
| 3 | 12x |
id = ns("constraint_var_whole"), # give an id to hide it
|
| 4 | 12x |
radioButtons(ns("constraint_var"), label, choices = "NONE"),
|
| 5 | 12x |
shinyjs::hidden(tags$div( |
| 6 | 12x |
id = ns("constraint_range"),
|
| 7 | 12x |
tags$div( |
| 8 | 12x |
class = "inline-block;", |
| 9 | 12x |
numericInput(ns("constraint_range_min"), label = "Min", value = 0, min = 0, max = 0)
|
| 10 |
), |
|
| 11 | 12x |
tags$div( |
| 12 | 12x |
class = "inline-block;", |
| 13 | 12x |
numericInput(ns("constraint_range_max"), label = "Min", value = 0, min = 0, max = 0)
|
| 14 |
) |
|
| 15 |
)), |
|
| 16 | 12x |
shinyjs::hidden(tags$div( |
| 17 | 12x |
id = ns("all_na"),
|
| 18 | 12x |
helpText("All values are missing (NA)")
|
| 19 |
)) |
|
| 20 |
) |
|
| 21 |
} |
|
| 22 | ||
| 23 |
keep_data_const_opts_updated <- function(session, input, data, id_param_var) {
|
|
| 24 |
# use reactiveVal so that it only updates when options actually changed and not just data |
|
| 25 | 6x |
choices <- reactiveVal() |
| 26 | 6x |
observeEvent(data(), {
|
| 27 | 7x |
paramname <- input[[id_param_var]] |
| 28 | 7x |
req(length(paramname) == 1) |
| 29 | ||
| 30 | 7x |
data_filtered <- data()$ANL %>% dplyr::filter(.data$PARAMCD == paramname) |
| 31 | 7x |
choices(c("None" = "NONE", "Screening" = "BASE2", "Baseline" = "BASE")[
|
| 32 | 7x |
c(TRUE, !all(is.na(data_filtered[["BASE2"]])), !all(is.na(data_filtered[["BASE"]]))) |
| 33 |
]) |
|
| 34 |
}) |
|
| 35 | 6x |
observeEvent(choices(), {
|
| 36 | 6x |
updateRadioButtons(session, "constraint_var", choices = choices()) |
| 37 |
# hide when only one option "NONE" |
|
| 38 | 6x |
if (length(choices()) == 1) {
|
| 39 | ! |
shinyjs::hide("constraint_var_whole")
|
| 40 |
} else {
|
|
| 41 | 6x |
shinyjs::show("constraint_var_whole")
|
| 42 |
} |
|
| 43 |
}) |
|
| 44 |
} |
|
| 45 | ||
| 46 | ||
| 47 | ||
| 48 | ||
| 49 |
# param_id: input id that contains values of PARAMCD to filter for |
|
| 50 |
# param_var: currently only "PARAMCD" is supported |
|
| 51 |
constr_anl_q <- function(session, input, data, dataname, param_id, param_var, trt_group, min_rows) {
|
|
| 52 | 6x |
checkmate::assert_class(data, "reactive") |
| 53 | 6x |
checkmate::assert_class(shiny::isolate(data()), "teal_data") |
| 54 | 6x |
dataset_var <- dataname |
| 55 | 6x |
if (!identical(param_var, "PARAMCD")) {
|
| 56 |
# why is there a variable param_id which is provided to this function and always equal to "param"? |
|
| 57 | ! |
stop("param_var must be 'PARAMCD'. Otherwise, we cannot currently guarantee the correctness of the code.")
|
| 58 |
} |
|
| 59 | ||
| 60 | 6x |
anl_param <- reactive({
|
| 61 | 13x |
param_var_value <- input[[param_id]] # value to filter PARAMCD for |
| 62 | 13x |
validate(need(param_var_value, "Please select a biomarker")) |
| 63 | 7x |
checkmate::assert_string(param_var_value) |
| 64 | ||
| 65 | 7x |
ANL <- data()[[dataname]] # nolint |
| 66 | 7x |
validate_has_data(ANL, min_rows) |
| 67 | ||
| 68 | 7x |
validate_has_variable(ANL, param_var) |
| 69 | 7x |
validate_has_variable(ANL, "AVISITCD") |
| 70 | 7x |
validate_has_variable(ANL, "BASE") |
| 71 | 7x |
validate_has_variable(ANL, "BASE2") |
| 72 | 7x |
validate_has_variable(ANL, trt_group) |
| 73 | ||
| 74 |
# analysis |
|
| 75 | 7x |
private_qenv <- data() %>% |
| 76 | 7x |
teal.code::eval_code( |
| 77 | 7x |
substitute(ANL <- dataname, list(dataname = as.name(dataname))) # nolint |
| 78 |
) %>% |
|
| 79 | 7x |
teal.code::eval_code( |
| 80 | 7x |
code = bquote({
|
| 81 | 7x |
ANL <- .(as.name(dataset_var)) %>% # nolint |
| 82 | 7x |
dplyr::filter(.(as.name(param_var)) == .(param_var_value)) |
| 83 |
}) |
|
| 84 |
) |
|
| 85 | 7x |
validate_has_data(private_qenv[["ANL"]], min_rows) |
| 86 | 7x |
list(ANL = ANL, qenv = private_qenv) |
| 87 |
}) |
|
| 88 | ||
| 89 | 6x |
observe({
|
| 90 | 13x |
param_var_value <- input[[param_id]] |
| 91 | 13x |
validate(need(param_var_value, "Please select a biomarker")) |
| 92 | ||
| 93 | 7x |
constraint_var <- input[["constraint_var"]] |
| 94 | 7x |
validate(need(constraint_var, "select a constraint variable")) |
| 95 | ||
| 96 | 7x |
ANL <- data()[[dataname]] # nolint |
| 97 | 7x |
validate_has_data(ANL, min_rows) |
| 98 | ||
| 99 | 7x |
validate_has_variable(ANL, param_var) |
| 100 | 7x |
validate_has_variable(ANL, "AVISITCD") |
| 101 | 7x |
validate_has_variable(ANL, "BASE") |
| 102 | 7x |
validate_has_variable(ANL, "BASE2") |
| 103 | ||
| 104 | 7x |
ANL <- ANL %>% dplyr::filter(!!sym(param_var) == param_var_value) # nolint |
| 105 | ||
| 106 | 7x |
visit_freq <- unique(ANL$AVISITCD) |
| 107 | ||
| 108 |
# get min max values |
|
| 109 | 7x |
if ((constraint_var == "BASE2" && any(grepl("SCR", visit_freq))) ||
|
| 110 | 7x |
(constraint_var == "BASE" && any(grepl("BL", visit_freq)))) { # nolint
|
| 111 | ! |
val <- stats::na.omit(switch(constraint_var, |
| 112 | ! |
"BASE" = ANL$BASE[ANL$AVISITCD == "BL"], |
| 113 | ! |
"BASE2" = ANL$BASE2[ANL$AVISITCD == "SCR"], |
| 114 | ! |
stop(paste(constraint_var, "not allowed")) |
| 115 |
)) |
|
| 116 | ||
| 117 | ! |
if (length(val) == 0 || all(is.na(val))) {
|
| 118 | ! |
shinyjs::show("all_na")
|
| 119 | ! |
shinyjs::hide("constraint_range")
|
| 120 | ! |
args <- list( |
| 121 | ! |
min = list(label = "Min", min = 0, max = 0, value = 0), |
| 122 | ! |
max = list(label = "Max", min = 0, max = 0, value = 0) |
| 123 |
) |
|
| 124 | ! |
update_min_max(session, args) |
| 125 |
} else {
|
|
| 126 | ! |
rng <- range(val, na.rm = TRUE) |
| 127 | ||
| 128 | ! |
minmax <- c(floor(rng[1] * 1000) / 1000, ceiling(rng[2] * 1000) / 1000) |
| 129 | ||
| 130 | ! |
label_min <- sprintf("Min (%s)", minmax[1])
|
| 131 | ! |
label_max <- sprintf("Max (%s)", minmax[2])
|
| 132 | ||
| 133 | ! |
args <- list( |
| 134 | ! |
min = list(label = label_min, min = minmax[1], max = minmax[2], value = minmax[1]), |
| 135 | ! |
max = list(label = label_max, min = minmax[1], max = minmax[2], value = minmax[2]) |
| 136 |
) |
|
| 137 | ||
| 138 | ! |
update_min_max(session, args) |
| 139 | ! |
shinyjs::show("constraint_range") # update before show
|
| 140 | ! |
shinyjs::hide("all_na")
|
| 141 |
} |
|
| 142 | 7x |
} else if (constraint_var == "NONE") {
|
| 143 | 7x |
shinyjs::hide("constraint_range") # hide before update
|
| 144 | 7x |
shinyjs::hide("all_na")
|
| 145 | ||
| 146 |
# force update (and thus refresh) on different constraint_var -> pass unique value for each constraint_var name |
|
| 147 | 7x |
args <- list( |
| 148 | 7x |
min = list(label = "Min", min = 0, max = 0, value = 0), |
| 149 | 7x |
max = list(label = "Max", min = 0, max = 0, value = 0) |
| 150 |
) |
|
| 151 | ||
| 152 | 7x |
update_min_max(session, args) |
| 153 |
} else {
|
|
| 154 | ! |
validate(need(FALSE, "This is an invalid data contraint for the filtered data")) |
| 155 |
} |
|
| 156 |
}) |
|
| 157 | ||
| 158 | 6x |
create_anl_constraint_reactive(anl_param, input, param_id = param_id, min_rows = min_rows) |
| 159 |
} |
|
| 160 | ||
| 161 |
# returns a reactive that applies the `x-axis data constraint` |
|
| 162 |
# More precisely, all patients are filtered out that do not have the range of |
|
| 163 |
# `param_id.constraint_var` in the specified range |
|
| 164 |
# constraint var means that `param_id.constraint_var` is constrained to the filtered range (or NA), |
|
| 165 |
# e.g. `ALT.BASE2` (i.e. `PARAMCD = ALT & range_filter_on(BASE2)`) |
|
| 166 |
create_anl_constraint_reactive <- function(anl_param, input, param_id, min_rows) {
|
|
| 167 | 6x |
iv_r <- reactive({
|
| 168 | 6x |
iv <- shinyvalidate::InputValidator$new() |
| 169 | 6x |
iv$condition(~ isTRUE(input$constraint_var != "NONE")) |
| 170 | 6x |
iv$add_rule("constraint_range_min", shinyvalidate::sv_required("A contraint minimum value is required"))
|
| 171 | 6x |
iv$add_rule("constraint_range_max", shinyvalidate::sv_required("A contraint maximum value is required"))
|
| 172 | 6x |
iv$add_rule( |
| 173 | 6x |
"constraint_range_min", |
| 174 | 6x |
~ if (!is.na(input$constraint_range_max) && (.) > input$constraint_range_max) {
|
| 175 | ! |
"constraint min needs to be less than max" |
| 176 |
} |
|
| 177 |
) |
|
| 178 | 6x |
iv$add_rule( |
| 179 | 6x |
"constraint_range_max", |
| 180 | 6x |
~ if (!is.na(input$constraint_range_min) && (.) < input$constraint_range_min) {
|
| 181 | ! |
"constraint min needs to be less than max" |
| 182 |
} |
|
| 183 |
) |
|
| 184 | 6x |
iv |
| 185 |
}) |
|
| 186 | ||
| 187 | ||
| 188 | 6x |
return_val <- reactive({
|
| 189 | 13x |
private_qenv <- anl_param()$qenv |
| 190 | ||
| 191 |
# it is assumed that constraint_var is triggering constraint_range which then trigger this clause |
|
| 192 | 7x |
constraint_var <- isolate(input[["constraint_var"]]) |
| 193 | 7x |
constraint_range_min <- input[["constraint_range_min"]] |
| 194 | 7x |
constraint_range_max <- input[["constraint_range_max"]] |
| 195 | 7x |
param <- input[[param_id]] |
| 196 | 7x |
req(param) |
| 197 | ||
| 198 |
# filter constraint |
|
| 199 | 7x |
if (constraint_var != "NONE") {
|
| 200 | ! |
private_qenv <- teal.code::eval_code( |
| 201 | ! |
object = private_qenv, |
| 202 | ! |
code = bquote({
|
| 203 |
# the below includes patients who have at least one non-NA BASE value |
|
| 204 |
# ideally, a patient should either have all NA values or none at all |
|
| 205 |
# this could be achieved through preprocessing; otherwise, this is easily overseen |
|
| 206 | ! |
filtered_usubjids <- ANL %>% # nolint |
| 207 | ! |
dplyr::filter( |
| 208 | ! |
PARAMCD == .(param), |
| 209 | ! |
(.(constraint_range_min) <= .data[[.(constraint_var)]]) & |
| 210 | ! |
(.data[[.(constraint_var)]] <= .(constraint_range_max)) |
| 211 |
) %>% |
|
| 212 | ! |
dplyr::pull(USUBJID) |
| 213 |
# include patients with all NA values for constraint_var |
|
| 214 | ! |
filtered_usubjids <- c( |
| 215 | ! |
filtered_usubjids, |
| 216 | ! |
ANL %>% |
| 217 | ! |
dplyr::filter(PARAMCD == .(param)) %>% |
| 218 | ! |
dplyr::group_by(USUBJID) %>% |
| 219 | ! |
dplyr::summarize(all_na = all(is.na(.data[[.(constraint_var)]]))) %>% |
| 220 | ! |
dplyr::filter(all_na) %>% |
| 221 | ! |
dplyr::pull(USUBJID) |
| 222 |
) |
|
| 223 | ! |
ANL <- ANL %>% dplyr::filter(USUBJID %in% filtered_usubjids) # nolint |
| 224 |
}) |
|
| 225 |
) |
|
| 226 | ! |
validate_has_data(private_qenv[["ANL"]], min_rows) |
| 227 |
} |
|
| 228 | 7x |
list(ANL = private_qenv[["ANL"]], qenv = private_qenv) |
| 229 |
}) |
|
| 230 | ||
| 231 | 6x |
reactive( |
| 232 | 6x |
list( |
| 233 | 6x |
value = return_val, |
| 234 | 6x |
iv_r = iv_r |
| 235 |
) |
|
| 236 |
) |
|
| 237 |
} |
|
| 238 | ||
| 239 |
# for outputting the constraint in the report |
|
| 240 |
formatted_data_constraint <- function(constraint_var, constraint_range_min, constraint_range_max) {
|
|
| 241 | ! |
constraint_var_label <- switch(constraint_var, |
| 242 | ! |
"BASE2" = "Screening", |
| 243 | ! |
"BASE" = "Baseline", |
| 244 | ! |
"None" |
| 245 |
) |
|
| 246 | ! |
msg <- paste("Data Constraint:", constraint_var_label)
|
| 247 | ! |
if (constraint_var_label != "None") {
|
| 248 | ! |
msg <- paste(msg, "from", constraint_range_min, "to", constraint_range_max) |
| 249 |
} |
|
| 250 | ! |
msg |
| 251 |
} |
|
| 252 | ||
| 253 |
update_min_max <- function(session, args) {
|
|
| 254 | 7x |
do.call("updateNumericInput", c(list(session = session, inputId = "constraint_range_min"), args$min))
|
| 255 | 7x |
do.call("updateNumericInput", c(list(session = session, inputId = "constraint_range_max"), args$max))
|
| 256 |
} |
| 1 |
#' UI with a toggleable dichotomous slider to change between slider and numeric input fields |
|
| 2 |
#' |
|
| 3 |
#' This is useful when a slider should be shown, but it is sometimes hard to configure sliders, |
|
| 4 |
#' so one can toggle to one or two numeric input fields to set slider instead. |
|
| 5 |
#' The toggle button will show two numeric input field for selecting the from and to range. |
|
| 6 |
#' |
|
| 7 |
#' @md |
|
| 8 |
#' @param id `character` module id |
|
| 9 |
#' @param label `label` label for input field, e.g. slider or numeric inputs |
|
| 10 |
#' @param ... additional parameters to pass to `sliderInput` |
|
| 11 |
#' |
|
| 12 |
#' @name toggle_slider |
|
| 13 |
#' @keywords internal |
|
| 14 |
#' @return `NULL`. |
|
| 15 |
NULL |
|
| 16 | ||
| 17 | ||
| 18 |
#' @rdname toggle_slider |
|
| 19 |
toggle_slider_ui <- function(id, label) {
|
|
| 20 | 12x |
ns <- NS(id) |
| 21 | 12x |
tags$div( |
| 22 | 12x |
tags$div( |
| 23 | 12x |
style = "display: flex; justify-content: space-between;", |
| 24 | 12x |
tags$span(tags$strong(label)), |
| 25 | 12x |
tags$div(actionButton(ns("toggle"), "Toggle", class = "btn-xs"))
|
| 26 |
), |
|
| 27 | 12x |
uiOutput(ns("inputs"))
|
| 28 |
) |
|
| 29 |
} |
|
| 30 | ||
| 31 |
#' @keywords internal |
|
| 32 |
#' @rdname toggle_slider |
|
| 33 |
toggle_slider_server <- function(id, data_state, ...) {
|
|
| 34 | 6x |
moduleServer(id, function(input, output, session) {
|
| 35 | 6x |
state <- reactiveValues( |
| 36 | 6x |
min = NULL, |
| 37 | 6x |
max = NULL, |
| 38 | 6x |
value = NULL |
| 39 |
) |
|
| 40 | 6x |
slider_shown <- reactive(input$toggle %% 2 == 0) |
| 41 | ||
| 42 | 6x |
observeEvent(data_state()$range, {
|
| 43 | 7x |
state$min <- data_state()$range[1] |
| 44 | 7x |
state$max <- data_state()$range[2] |
| 45 | 7x |
state$value <- data_state()$range |
| 46 |
}) |
|
| 47 | ||
| 48 | 6x |
output$inputs <- renderUI({
|
| 49 | 28x |
req(state$value) |
| 50 | 22x |
if (slider_shown()) {
|
| 51 | 11x |
tags$div( |
| 52 | 11x |
class = "teal-goshawk toggle-slider-container", |
| 53 | 11x |
sliderInput( |
| 54 | 11x |
inputId = session$ns("slider"),
|
| 55 | 11x |
label = NULL, |
| 56 | 11x |
min = min(data_state()$range[1], state$min), |
| 57 | 11x |
max = max(data_state()$range[2], state$max), |
| 58 | 11x |
value = state$value, |
| 59 | 11x |
step = data_state()$step, |
| 60 | 11x |
ticks = TRUE, |
| 61 |
... |
|
| 62 |
), |
|
| 63 | 11x |
tags$script(HTML(sprintf( |
| 64 |
' |
|
| 65 | 11x |
$(".teal-goshawk.toggle-slider-container #%s").ready(function () {
|
| 66 | 11x |
var tickLabel = document.querySelector( |
| 67 | 11x |
".teal-goshawk.toggle-slider-container .irs-grid-text.js-grid-text-9" |
| 68 |
); |
|
| 69 | 11x |
var tick = document.querySelector( |
| 70 | 11x |
".teal-goshawk.toggle-slider-container .irs-grid-pol:nth-last-child(6)" |
| 71 |
); |
|
| 72 | 11x |
if (tickLabel) {
|
| 73 | 11x |
if (parseFloat(tickLabel.style.left) > 95) {
|
| 74 | 11x |
tickLabel.style.opacity = "0"; |
| 75 | 11x |
tick.style.opacity = "0"; |
| 76 |
} |
|
| 77 |
} else {
|
|
| 78 | 11x |
console.log("Toggle slider element not found.");
|
| 79 |
} |
|
| 80 |
}); |
|
| 81 |
', |
|
| 82 | 11x |
session$ns("slider")
|
| 83 |
))) |
|
| 84 |
) |
|
| 85 |
} else {
|
|
| 86 | 11x |
tags$div( |
| 87 | 11x |
class = "teal-goshawk toggle-slider-container", |
| 88 | 11x |
numericInput( |
| 89 | 11x |
inputId = session$ns("value_low"),
|
| 90 | 11x |
label = "From:", |
| 91 | 11x |
value = state$value[1] |
| 92 |
), |
|
| 93 | 11x |
numericInput( |
| 94 | 11x |
inputId = session$ns("value_high"),
|
| 95 | 11x |
label = "to:", |
| 96 | 11x |
value = state$value[2] |
| 97 |
) |
|
| 98 |
) |
|
| 99 |
} |
|
| 100 |
}) |
|
| 101 | ||
| 102 | 6x |
d_slider <- debounce(reactive(input$slider), 500) |
| 103 | ||
| 104 | 6x |
observeEvent(d_slider(), {
|
| 105 | 11x |
if (!setequal(state$value, d_slider())) {
|
| 106 | 1x |
state$value <- d_slider() |
| 107 |
} |
|
| 108 |
}) |
|
| 109 | ||
| 110 | 6x |
d_value_low <- debounce(reactive(input$value_low), 500) |
| 111 | 6x |
d_value_high <- debounce(reactive(input$value_high), 500) |
| 112 | ||
| 113 | 6x |
observeEvent(c(d_value_low(), d_value_high()), ignoreInit = TRUE, {
|
| 114 | 16x |
values <- c(input$value_low, input$value_high) |
| 115 | 16x |
if (!setequal(state$value, values)) {
|
| 116 | 6x |
state$value <- values |
| 117 | 6x |
state$min <- values[1] |
| 118 | 6x |
state$max <- values[2] |
| 119 |
} |
|
| 120 |
}) |
|
| 121 | ||
| 122 | 6x |
state |
| 123 |
}) |
|
| 124 |
} |
|
| 125 | ||
| 126 |
#' @keywords internal |
|
| 127 |
#' @rdname toggle_slider |
|
| 128 |
get_data_range_states <- function(varname, paramname, ANL, trt_group = NULL, step = NULL) { # nolint object_name_linter
|
|
| 129 | 13x |
validate(need(varname, "Please select variable")) |
| 130 | 7x |
validate(need(paramname, "Please select variable")) |
| 131 | 7x |
req(length(paramname) == 1) |
| 132 | 7x |
step <- NULL |
| 133 | ||
| 134 | 7x |
ANL <- ANL %>% dplyr::filter(.data$PARAMCD == paramname) # nolint object_name_linter |
| 135 | 7x |
validate_has_variable(ANL, varname, paste("variable", varname, "does not exist"))
|
| 136 | ||
| 137 | 7x |
var <- stats::na.omit(ANL[[varname]]) |
| 138 | 7x |
minmax <- if (length(var)) c(floor(min(var)), ceiling(max(var))) else c(0, 0) |
| 139 | 7x |
if (!is.null(trt_group)) {
|
| 140 | ! |
ANL_split <- ANL %>% split(f = factor(paste0(ANL[["AVISITCD"]], ANL[[trt_group]]))) # nolint |
| 141 | ! |
density_maxes <- lapply(ANL_split, function(x) {
|
| 142 | ! |
max(stats::density(stats::na.omit(x[[varname]]))$y) |
| 143 |
}) |
|
| 144 | ! |
dmax <- max(unlist(density_maxes)) |
| 145 | ! |
minmax <- c(0, round(dmax * 1.2, 5)) |
| 146 | ! |
step <- round(dmax / 100, 5) |
| 147 |
} |
|
| 148 | 7x |
list( |
| 149 | 7x |
range = c(min = minmax[[1]], max = minmax[[2]]), |
| 150 | 7x |
step = step |
| 151 |
) |
|
| 152 |
} |
| 1 |
#' Box Plot |
|
| 2 |
#' |
|
| 3 |
#' This teal module renders the UI and calls the functions that create a box plot and accompanying |
|
| 4 |
#' summary table. |
|
| 5 |
#' |
|
| 6 |
#' @param label menu item label of the module in the teal app. |
|
| 7 |
#' @param dataname analysis data passed to the data argument of \code{\link[teal]{init}}. E.g. `ADaM` structured
|
|
| 8 |
#' laboratory data frame `ALB`. |
|
| 9 |
#' @param param_var name of variable containing biomarker codes e.g. `PARAMCD`. |
|
| 10 |
#' @param param list of biomarkers of interest. |
|
| 11 |
#' @param yaxis_var name of variable containing biomarker results displayed on y-axis e.g. `AVAL`. When not provided, |
|
| 12 |
#' it defaults to `choices_selected(c("AVAL", "CHG"), "AVAL")`.
|
|
| 13 |
#' @param xaxis_var variable to categorize the x-axis. When not provided, it defaults to |
|
| 14 |
#' `choices_selected("AVISITCD", "AVISITCD")`.
|
|
| 15 |
#' @param facet_var variable to facet the plots by. When not provided, it defaults to |
|
| 16 |
#' `choices_selected(c("ARM", "ACTARM"), "ARM")`.
|
|
| 17 |
#' @param trt_group \code{\link[teal.transform]{choices_selected}} object with available choices and pre-selected
|
|
| 18 |
#' option for variable names representing treatment group e.g. `ARM`. |
|
| 19 |
#' @param color_manual vector of colors applied to treatment values. |
|
| 20 |
#' @param shape_manual vector of symbols applied to `LOQ` values. |
|
| 21 |
#' @param facet_ncol numeric value indicating number of facets per row. |
|
| 22 |
#' @param loq_legend `loq` legend toggle. |
|
| 23 |
#' @param rotate_xlab 45 degree rotation of `x-axis` values. |
|
| 24 |
#' @param hline_arb numeric vector of at most 2 values identifying intercepts for arbitrary horizontal lines. |
|
| 25 |
#' @param hline_arb_color a character vector of at most length of \code{hline_arb}.
|
|
| 26 |
#' naming the color for the arbitrary horizontal lines. |
|
| 27 |
#' @param hline_arb_label a character vector of at most length of \code{hline_arb}.
|
|
| 28 |
#' naming the label for the arbitrary horizontal lines. |
|
| 29 |
#' @param hline_vars a character vector to name the columns that will define additional horizontal lines. |
|
| 30 |
#' @param hline_vars_colors a character vector naming the colors for the additional horizontal lines. |
|
| 31 |
#' @param hline_vars_labels a character vector naming the labels for the additional horizontal lines that will appear |
|
| 32 |
#' in the legend. |
|
| 33 |
#' @param plot_height controls plot height. |
|
| 34 |
#' @param plot_width optional, controls plot width. |
|
| 35 |
#' @param font_size font size control for title, `x-axis` label, `y-axis` label and legend. |
|
| 36 |
#' @param dot_size plot dot size. |
|
| 37 |
#' @param alpha numeric vector to define transparency of plotted points. |
|
| 38 |
#' |
|
| 39 |
#' @inheritParams teal.widgets::standard_layout |
|
| 40 |
#' @inheritParams teal::module |
|
| 41 |
#' |
|
| 42 |
#' @author Jeff Tomlinson (tomlinsj) jeffrey.tomlinson@roche.com |
|
| 43 |
#' @author Balazs Toth (tothb2) toth.balazs@gene.com |
|
| 44 |
#' |
|
| 45 |
#' @return an \code{\link[teal]{module}} object
|
|
| 46 |
#' |
|
| 47 |
#' @export |
|
| 48 |
#' |
|
| 49 |
#' @examplesIf require("nestcolor")
|
|
| 50 |
#' # Example using ADaM structure analysis dataset. |
|
| 51 |
#' data <- teal_data() |
|
| 52 |
#' data <- within(data, {
|
|
| 53 |
#' library(dplyr) |
|
| 54 |
#' library(nestcolor) |
|
| 55 |
#' library(stringr) |
|
| 56 |
#' |
|
| 57 |
#' # use non-exported function from goshawk |
|
| 58 |
#' .h_identify_loq_values <- getFromNamespace("h_identify_loq_values", "goshawk")
|
|
| 59 |
#' |
|
| 60 |
#' # original ARM value = dose value |
|
| 61 |
#' .arm_mapping <- list( |
|
| 62 |
#' "A: Drug X" = "150mg QD", |
|
| 63 |
#' "B: Placebo" = "Placebo", |
|
| 64 |
#' "C: Combination" = "Combination" |
|
| 65 |
#' ) |
|
| 66 |
#' set.seed(1) # @linksto ADSL ADLB |
|
| 67 |
#' ADSL <- rADSL |
|
| 68 |
#' ADLB <- rADLB |
|
| 69 |
#' .var_labels <- lapply(ADLB, function(x) attributes(x)$label) |
|
| 70 |
#' ADLB <- ADLB %>% |
|
| 71 |
#' mutate( |
|
| 72 |
#' AVISITCD = case_when( |
|
| 73 |
#' AVISIT == "SCREENING" ~ "SCR", |
|
| 74 |
#' AVISIT == "BASELINE" ~ "BL", |
|
| 75 |
#' grepl("WEEK", AVISIT) ~ paste("W", str_extract(AVISIT, "(?<=(WEEK ))[0-9]+")),
|
|
| 76 |
#' TRUE ~ as.character(NA) |
|
| 77 |
#' ), |
|
| 78 |
#' AVISITCDN = case_when( |
|
| 79 |
#' AVISITCD == "SCR" ~ -2, |
|
| 80 |
#' AVISITCD == "BL" ~ 0, |
|
| 81 |
#' grepl("W", AVISITCD) ~ as.numeric(gsub("[^0-9]*", "", AVISITCD)),
|
|
| 82 |
#' TRUE ~ as.numeric(NA) |
|
| 83 |
#' ), |
|
| 84 |
#' AVISITCD = factor(AVISITCD) %>% reorder(AVISITCDN), |
|
| 85 |
#' TRTORD = case_when( |
|
| 86 |
#' ARMCD == "ARM C" ~ 1, |
|
| 87 |
#' ARMCD == "ARM B" ~ 2, |
|
| 88 |
#' ARMCD == "ARM A" ~ 3 |
|
| 89 |
#' ), |
|
| 90 |
#' ARM = as.character(.arm_mapping[match(ARM, names(.arm_mapping))]), |
|
| 91 |
#' ARM = factor(ARM) %>% reorder(TRTORD), |
|
| 92 |
#' ACTARM = as.character(.arm_mapping[match(ACTARM, names(.arm_mapping))]), |
|
| 93 |
#' ACTARM = factor(ACTARM) %>% reorder(TRTORD), |
|
| 94 |
#' ANRLO = 50, |
|
| 95 |
#' ANRHI = 75 |
|
| 96 |
#' ) %>% |
|
| 97 |
#' rowwise() %>% |
|
| 98 |
#' group_by(PARAMCD) %>% |
|
| 99 |
#' mutate(LBSTRESC = ifelse( |
|
| 100 |
#' USUBJID %in% sample(USUBJID, 1, replace = TRUE), |
|
| 101 |
#' paste("<", round(runif(1, min = 25, max = 30))), LBSTRESC
|
|
| 102 |
#' )) %>% |
|
| 103 |
#' mutate(LBSTRESC = ifelse( |
|
| 104 |
#' USUBJID %in% sample(USUBJID, 1, replace = TRUE), |
|
| 105 |
#' paste(">", round(runif(1, min = 70, max = 75))), LBSTRESC
|
|
| 106 |
#' )) %>% |
|
| 107 |
#' ungroup() |
|
| 108 |
#' |
|
| 109 |
#' attr(ADLB[["ARM"]], "label") <- .var_labels[["ARM"]] |
|
| 110 |
#' attr(ADLB[["ACTARM"]], "label") <- .var_labels[["ACTARM"]] |
|
| 111 |
#' attr(ADLB[["ANRLO"]], "label") <- "Analysis Normal Range Lower Limit" |
|
| 112 |
#' attr(ADLB[["ANRHI"]], "label") <- "Analysis Normal Range Upper Limit" |
|
| 113 |
#' |
|
| 114 |
#' # add LLOQ and ULOQ variables |
|
| 115 |
#' ALB_LOQS <- .h_identify_loq_values(ADLB, "LOQFL") |
|
| 116 |
#' ADLB <- left_join(ADLB, ALB_LOQS, by = "PARAM") |
|
| 117 |
#' }) |
|
| 118 |
#' |
|
| 119 |
#' join_keys(data) <- default_cdisc_join_keys[names(data)] |
|
| 120 |
#' |
|
| 121 |
#' app <- init( |
|
| 122 |
#' data = data, |
|
| 123 |
#' modules = modules( |
|
| 124 |
#' tm_g_gh_boxplot( |
|
| 125 |
#' label = "Box Plot", |
|
| 126 |
#' dataname = "ADLB", |
|
| 127 |
#' param_var = "PARAMCD", |
|
| 128 |
#' param = choices_selected(c("ALT", "CRP", "IGA"), "ALT"),
|
|
| 129 |
#' yaxis_var = choices_selected(c("AVAL", "BASE", "CHG"), "AVAL"),
|
|
| 130 |
#' xaxis_var = choices_selected(c("ACTARM", "ARM", "AVISITCD", "STUDYID"), "ARM"),
|
|
| 131 |
#' facet_var = choices_selected(c("ACTARM", "ARM", "AVISITCD", "SEX"), "AVISITCD"),
|
|
| 132 |
#' trt_group = choices_selected(c("ARM", "ACTARM"), "ARM"),
|
|
| 133 |
#' loq_legend = TRUE, |
|
| 134 |
#' rotate_xlab = FALSE, |
|
| 135 |
#' hline_arb = c(60, 55), |
|
| 136 |
#' hline_arb_color = c("grey", "red"),
|
|
| 137 |
#' hline_arb_label = c("default_hori_A", "default_hori_B"),
|
|
| 138 |
#' hline_vars = c("ANRHI", "ANRLO", "ULOQN", "LLOQN"),
|
|
| 139 |
#' hline_vars_colors = c("pink", "brown", "purple", "black"),
|
|
| 140 |
#' ) |
|
| 141 |
#' ) |
|
| 142 |
#' ) |
|
| 143 |
#' if (interactive()) {
|
|
| 144 |
#' shinyApp(app$ui, app$server) |
|
| 145 |
#' } |
|
| 146 |
#' |
|
| 147 |
tm_g_gh_boxplot <- function(label, |
|
| 148 |
dataname, |
|
| 149 |
param_var, |
|
| 150 |
param, |
|
| 151 |
yaxis_var = teal.transform::choices_selected(c("AVAL", "CHG"), "AVAL"),
|
|
| 152 |
xaxis_var = teal.transform::choices_selected("AVISITCD", "AVISITCD"),
|
|
| 153 |
facet_var = teal.transform::choices_selected(c("ARM", "ACTARM"), "ARM"),
|
|
| 154 |
trt_group, |
|
| 155 |
color_manual = NULL, |
|
| 156 |
shape_manual = NULL, |
|
| 157 |
facet_ncol = NULL, |
|
| 158 |
loq_legend = TRUE, |
|
| 159 |
rotate_xlab = FALSE, |
|
| 160 |
hline_arb = numeric(0), |
|
| 161 |
hline_arb_color = "red", |
|
| 162 |
hline_arb_label = "Horizontal line", |
|
| 163 |
hline_vars = character(0), |
|
| 164 |
hline_vars_colors = "green", |
|
| 165 |
hline_vars_labels = hline_vars, |
|
| 166 |
plot_height = c(600, 200, 2000), |
|
| 167 |
plot_width = NULL, |
|
| 168 |
font_size = c(12, 8, 20), |
|
| 169 |
dot_size = c(2, 1, 12), |
|
| 170 |
alpha = c(0.8, 0.0, 1.0), |
|
| 171 |
pre_output = NULL, |
|
| 172 |
post_output = NULL, |
|
| 173 |
transformators = list()) {
|
|
| 174 | 6x |
message("Initializing tm_g_gh_boxplot")
|
| 175 | 6x |
checkmate::assert_string(label) |
| 176 | 6x |
checkmate::assert_string(dataname) |
| 177 | 6x |
checkmate::assert_string(param_var) |
| 178 | 6x |
checkmate::assert_class(param, "choices_selected") |
| 179 | 6x |
checkmate::assert_class(yaxis_var, "choices_selected") |
| 180 | 6x |
checkmate::assert_class(xaxis_var, "choices_selected") |
| 181 | 6x |
checkmate::assert_class(facet_var, "choices_selected") |
| 182 | 6x |
checkmate::assert_class(trt_group, "choices_selected") |
| 183 | 6x |
checkmate::assert_int(facet_ncol, null.ok = TRUE) |
| 184 | 6x |
checkmate::assert_flag(loq_legend) |
| 185 | 6x |
checkmate::assert_flag(rotate_xlab) |
| 186 | 6x |
checkmate::assert_numeric(font_size, len = 3) |
| 187 | 6x |
checkmate::assert_numeric(dot_size, len = 3) |
| 188 | 6x |
checkmate::assert_numeric(alpha, len = 3) |
| 189 | 6x |
validate_line_arb_arg(hline_arb, hline_arb_color, hline_arb_label) |
| 190 | 6x |
validate_line_vars_arg(hline_vars, hline_vars_colors, hline_vars_labels) |
| 191 | 6x |
checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) |
| 192 | 6x |
checkmate::assert_numeric(plot_height[1], lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height") |
| 193 | 6x |
checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) |
| 194 | 6x |
checkmate::assert_numeric( |
| 195 | 6x |
plot_width[1], |
| 196 | 6x |
lower = plot_width[2], upper = plot_width[3], null.ok = TRUE, .var.name = "plot_width" |
| 197 |
) |
|
| 198 | ||
| 199 | 6x |
args <- as.list(environment()) |
| 200 | ||
| 201 | 6x |
module( |
| 202 | 6x |
label = label, |
| 203 | 6x |
datanames = dataname, |
| 204 | 6x |
server = srv_g_boxplot, |
| 205 | 6x |
server_args = list( |
| 206 | 6x |
dataname = dataname, |
| 207 | 6x |
param_var = param_var, |
| 208 | 6x |
color_manual = color_manual, |
| 209 | 6x |
shape_manual = shape_manual, |
| 210 | 6x |
plot_height = plot_height, |
| 211 | 6x |
plot_width = plot_width, |
| 212 | 6x |
hline_vars_colors = hline_vars_colors, |
| 213 | 6x |
hline_vars_labels = hline_vars_labels, |
| 214 | 6x |
module_args = args |
| 215 |
), |
|
| 216 | 6x |
ui = ui_g_boxplot, |
| 217 | 6x |
ui_args = args, |
| 218 | 6x |
transformators = transformators |
| 219 |
) |
|
| 220 |
} |
|
| 221 | ||
| 222 |
ui_g_boxplot <- function(id, ...) {
|
|
| 223 | 12x |
ns <- NS(id) |
| 224 | 12x |
a <- list(...) |
| 225 | ||
| 226 | 12x |
teal.widgets::standard_layout( |
| 227 | 12x |
output = bslib::page_fluid( |
| 228 | 12x |
tags$div( |
| 229 | 12x |
teal.widgets::plot_with_settings_ui(id = ns("boxplot"))
|
| 230 |
), |
|
| 231 | 12x |
tags$div( |
| 232 | 12x |
tags$br(), tags$hr(), |
| 233 | 12x |
tags$h4("Selected Data Points"),
|
| 234 | 12x |
DT::dataTableOutput(ns("brush_data"))
|
| 235 |
), |
|
| 236 | 12x |
tags$div( |
| 237 | 12x |
tags$br(), tags$hr(), |
| 238 | 12x |
tags$h4("Descriptive Statistics"),
|
| 239 | 12x |
DT::dataTableOutput(ns("table_ui"))
|
| 240 |
) |
|
| 241 |
), |
|
| 242 | 12x |
encoding = tags$div( |
| 243 |
### Reporter |
|
| 244 | 12x |
teal.reporter::add_card_button_ui(ns("add_reporter"), label = "Add Report Card"),
|
| 245 | 12x |
tags$br(), tags$br(), |
| 246 |
### |
|
| 247 | 12x |
templ_ui_dataname(a$dataname), |
| 248 | 12x |
uiOutput(ns("axis_selections")),
|
| 249 | 12x |
templ_ui_constraint(ns, label = "Data Constraint"), # required by constr_anl_q |
| 250 | 12x |
if (length(a$hline_vars) > 0) {
|
| 251 | 12x |
teal.widgets::optionalSelectInput( |
| 252 | 12x |
ns("hline_vars"),
|
| 253 | 12x |
label = "Add Horizontal Range Line(s):", |
| 254 | 12x |
choices = a$hline_vars, |
| 255 | 12x |
selected = NULL, |
| 256 | 12x |
multiple = TRUE |
| 257 |
) |
|
| 258 |
}, |
|
| 259 | 12x |
ui_arbitrary_lines(id = ns("hline_arb"), a$hline_arb, a$hline_arb_label, a$hline_arb_color),
|
| 260 | 12x |
bslib::accordion( |
| 261 | 12x |
bslib::accordion_panel( |
| 262 | 12x |
title = "Plot Aesthetic Settings", |
| 263 | 12x |
toggle_slider_ui( |
| 264 | 12x |
ns("yrange_scale"),
|
| 265 | 12x |
label = "Y-Axis Range Zoom" |
| 266 |
), |
|
| 267 | 12x |
numericInput(ns("facet_ncol"), "Number of Plots Per Row:", a$facet_ncol, min = 1),
|
| 268 | 12x |
checkboxInput(ns("loq_legend"), "Display LoQ Legend", a$loq_legend),
|
| 269 | 12x |
checkboxInput(ns("rotate_xlab"), "Rotate X-axis Label", a$rotate_xlab)
|
| 270 |
), |
|
| 271 | 12x |
bslib::accordion_panel( |
| 272 | 12x |
title = "Plot settings", |
| 273 | 12x |
teal.widgets::optionalSliderInputValMinMax(ns("font_size"), "Font Size", a$font_size, ticks = FALSE),
|
| 274 | 12x |
teal.widgets::optionalSliderInputValMinMax(ns("dot_size"), "Dot Size", a$dot_size, ticks = FALSE),
|
| 275 | 12x |
teal.widgets::optionalSliderInputValMinMax(ns("alpha"), "Dot Alpha", a$alpha, ticks = FALSE)
|
| 276 |
) |
|
| 277 |
) |
|
| 278 |
), |
|
| 279 | 12x |
forms = tagList( |
| 280 | 12x |
teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code")
|
| 281 |
), |
|
| 282 | 12x |
pre_output = a$pre_output, |
| 283 | 12x |
post_output = a$post_output |
| 284 |
) |
|
| 285 |
} |
|
| 286 | ||
| 287 | ||
| 288 |
srv_g_boxplot <- function(id, |
|
| 289 |
data, |
|
| 290 |
reporter, |
|
| 291 |
filter_panel_api, |
|
| 292 |
dataname, |
|
| 293 |
param_var, |
|
| 294 |
trt_group, |
|
| 295 |
color_manual, |
|
| 296 |
shape_manual, |
|
| 297 |
plot_height, |
|
| 298 |
plot_width, |
|
| 299 |
hline_vars_colors, |
|
| 300 |
hline_vars_labels, |
|
| 301 |
module_args) {
|
|
| 302 | 6x |
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter") |
| 303 | 6x |
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI") |
| 304 | 6x |
checkmate::assert_class(data, "reactive") |
| 305 | 6x |
checkmate::assert_class(shiny::isolate(data()), "teal_data") |
| 306 | ||
| 307 | 6x |
moduleServer(id, function(input, output, session) {
|
| 308 | 6x |
teal.logger::log_shiny_input_changes(input, namespace = "teal.goshawk") |
| 309 | 6x |
output$axis_selections <- renderUI({
|
| 310 | 6x |
env <- shiny::isolate(as.list(data()[[".raw_data"]])) |
| 311 | 6x |
resolved_x <- teal.transform::resolve_delayed(module_args$xaxis_var, env) |
| 312 | 6x |
resolved_y <- teal.transform::resolve_delayed(module_args$yaxis_var, env) |
| 313 | 6x |
resolved_param <- teal.transform::resolve_delayed(module_args$param, env) |
| 314 | 6x |
resolved_facet_var <- teal.transform::resolve_delayed(module_args$facet_var, env) |
| 315 | 6x |
resolved_trt <- teal.transform::resolve_delayed(module_args$trt_group, env) |
| 316 | ||
| 317 | 6x |
templ_ui_params_vars( |
| 318 | 6x |
session$ns, |
| 319 | 6x |
xparam_choices = resolved_param$choices, |
| 320 | 6x |
xparam_selected = resolved_param$selected, |
| 321 | 6x |
xparam_label = module_args$"Select a Biomarker", |
| 322 | 6x |
xchoices = resolved_x$choices, |
| 323 | 6x |
xselected = resolved_x$selected, |
| 324 | 6x |
ychoices = resolved_y$choices, |
| 325 | 6x |
yselected = resolved_y$selected, |
| 326 | 6x |
facet_choices = resolved_facet_var$choices, |
| 327 | 6x |
facet_selected = resolved_facet_var$selected, |
| 328 | 6x |
trt_choices = resolved_trt$choices, |
| 329 | 6x |
trt_selected = resolved_trt$selected |
| 330 |
) |
|
| 331 |
}) |
|
| 332 |
# reused in all modules |
|
| 333 | 6x |
anl_q_output <- constr_anl_q( |
| 334 | 6x |
session, input, data, dataname, |
| 335 | 6x |
param_id = "xaxis_param", param_var = param_var, trt_group = input$trt_group, min_rows = 2 |
| 336 |
) |
|
| 337 | ||
| 338 | 6x |
anl_q <- anl_q_output()$value |
| 339 | ||
| 340 |
# update sliders for axes taking constraints into account |
|
| 341 | 6x |
data_state <- reactive({
|
| 342 | 13x |
get_data_range_states( |
| 343 | 13x |
varname = input$yaxis_var, |
| 344 | 13x |
paramname = input$xaxis_param, |
| 345 | 13x |
ANL = anl_q()$ANL |
| 346 |
) |
|
| 347 |
}) |
|
| 348 | 6x |
yrange_slider_state <- toggle_slider_server("yrange_scale", data_state)
|
| 349 | 6x |
keep_data_const_opts_updated(session, input, anl_q, "xaxis_param") |
| 350 | ||
| 351 | 6x |
horizontal_line <- srv_arbitrary_lines("hline_arb")
|
| 352 | ||
| 353 | 6x |
trt_group <- reactive({
|
| 354 | 6x |
input$trt_group |
| 355 |
}) |
|
| 356 | ||
| 357 | 6x |
iv_r <- reactive({
|
| 358 | 6x |
iv <- shinyvalidate::InputValidator$new() |
| 359 | ||
| 360 | 6x |
iv$add_rule("xaxis_param", shinyvalidate::sv_required("Please select a biomarker"))
|
| 361 | 6x |
iv$add_rule("trt_group", shinyvalidate::sv_required("Please select a treatment variable"))
|
| 362 | 6x |
iv$add_rule("xaxis_var", shinyvalidate::sv_required("Please select an X-Axis variable"))
|
| 363 | 6x |
iv$add_rule("xaxis_var", ~ if ((.) %in% c("ACTARM", "ARM") && isTRUE((.) != trt_group())) {
|
| 364 | ! |
sprintf("You can not choose %s as x-axis variable for treatment variable %s.", (.), trt_group())
|
| 365 |
}) |
|
| 366 | 6x |
iv$add_rule("yaxis_var", shinyvalidate::sv_required("Please select a Y-Axis variable"))
|
| 367 | ||
| 368 | 6x |
iv$add_rule("facet_var", shinyvalidate::sv_optional())
|
| 369 | 6x |
iv$add_rule("facet_var", ~ if ((.) %in% c("ACTARM", "ARM") && isTRUE((.) != trt_group())) {
|
| 370 | ! |
sprintf("You can not choose %s as faceting variable for treatment variable %s.", (.), trt_group())
|
| 371 |
}) |
|
| 372 | ||
| 373 | 6x |
iv_facet <- shinyvalidate::InputValidator$new() |
| 374 | 6x |
iv_facet$condition(~ !is.null(input$facet_var)) |
| 375 | 6x |
iv_facet$add_rule("facet_ncol", plots_per_row_validate_rules(required = FALSE))
|
| 376 | 6x |
iv$add_validator(iv_facet) |
| 377 | ||
| 378 | 6x |
iv$add_validator(horizontal_line()$iv_r()) |
| 379 | 6x |
iv$add_validator(anl_q_output()$iv_r()) |
| 380 | 6x |
iv$enable() |
| 381 | 6x |
iv |
| 382 |
}) |
|
| 383 | ||
| 384 | 6x |
create_plot <- debounce(reactive({
|
| 385 | 27x |
teal::validate_inputs(iv_r()) |
| 386 | ||
| 387 | 21x |
req(anl_q()) |
| 388 |
# nolint start |
|
| 389 | 21x |
param <- input$xaxis_param |
| 390 | 21x |
yaxis <- input$yaxis_var |
| 391 | 21x |
xaxis <- input$xaxis_var |
| 392 | 21x |
facet_var <- `if`(is.null(input$facet_var), "None", input$facet_var) |
| 393 | 21x |
ylim <- yrange_slider_state$value |
| 394 | 21x |
facet_ncol <- input$facet_ncol |
| 395 | ||
| 396 | 21x |
alpha <- input$alpha |
| 397 | 21x |
font_size <- input$font_size |
| 398 | 21x |
dot_size <- input$dot_size |
| 399 | 21x |
loq_legend <- input$loq_legend |
| 400 | 21x |
rotate_xlab <- input$rotate_xlab |
| 401 | ||
| 402 | 21x |
hline_arb <- horizontal_line()$line_arb |
| 403 | 21x |
hline_arb_label <- horizontal_line()$line_arb_label |
| 404 | 21x |
hline_arb_color <- horizontal_line()$line_arb_color |
| 405 | ||
| 406 | 21x |
hline_vars <- input$hline_vars |
| 407 | 21x |
trt_group <- input$trt_group |
| 408 |
# nolint end |
|
| 409 | ||
| 410 | 21x |
validate_has_variable( |
| 411 | 21x |
anl_q()$ANL, |
| 412 | 21x |
yaxis, |
| 413 | 21x |
sprintf("Variable %s is not available in data %s", yaxis, dataname)
|
| 414 |
) |
|
| 415 | 21x |
validate_has_variable( |
| 416 | 21x |
anl_q()$ANL, |
| 417 | 21x |
xaxis, |
| 418 | 21x |
sprintf("Variable %s is not available in data %s", xaxis, dataname)
|
| 419 |
) |
|
| 420 | ||
| 421 | 21x |
if (!facet_var == "None") {
|
| 422 | 21x |
validate_has_variable( |
| 423 | 21x |
anl_q()$ANL, |
| 424 | 21x |
facet_var, |
| 425 | 21x |
sprintf("Variable %s is not available in data %s", facet_var, dataname)
|
| 426 |
) |
|
| 427 |
} |
|
| 428 | ||
| 429 | 21x |
anl_q()$qenv %>% teal.code::eval_code( |
| 430 | 21x |
code = bquote({
|
| 431 | 21x |
p <- goshawk::g_boxplot( |
| 432 | 21x |
data = ANL, |
| 433 | 21x |
biomarker = .(param), |
| 434 | 21x |
xaxis_var = .(xaxis), |
| 435 | 21x |
yaxis_var = .(yaxis), |
| 436 | 21x |
hline_arb = .(hline_arb), |
| 437 | 21x |
hline_arb_label = .(hline_arb_label), |
| 438 | 21x |
hline_arb_color = .(hline_arb_color), |
| 439 | 21x |
hline_vars = .(hline_vars), |
| 440 | 21x |
hline_vars_colors = .(hline_vars_colors[seq_along(hline_vars)]), |
| 441 | 21x |
hline_vars_labels = .(hline_vars_labels[seq_along(hline_vars)]), |
| 442 | 21x |
facet_ncol = .(facet_ncol), |
| 443 | 21x |
loq_legend = .(loq_legend), |
| 444 | 21x |
rotate_xlab = .(rotate_xlab), |
| 445 | 21x |
trt_group = .(trt_group), |
| 446 | 21x |
ylim = .(ylim), |
| 447 | 21x |
color_manual = .(color_manual), |
| 448 | 21x |
shape_manual = .(shape_manual), |
| 449 | 21x |
facet_var = .(facet_var), |
| 450 | 21x |
alpha = .(alpha), |
| 451 | 21x |
dot_size = .(dot_size), |
| 452 | 21x |
font_size = .(font_size), |
| 453 | 21x |
unit = .("AVALU")
|
| 454 |
) |
|
| 455 | 15x |
p |
| 456 |
}) |
|
| 457 |
) |
|
| 458 | 6x |
}), 800) |
| 459 | ||
| 460 | 6x |
create_table <- debounce(reactive({
|
| 461 | 13x |
req(iv_r()$is_valid()) |
| 462 | 7x |
req(anl_q()) |
| 463 | 7x |
param <- input$xaxis_param |
| 464 | 7x |
xaxis_var <- input$yaxis_var # nolint |
| 465 | 7x |
font_size <- input$font_size |
| 466 | 7x |
trt_group <- input$trt_group |
| 467 | 7x |
facet_var <- input$facet_var |
| 468 | ||
| 469 | 7x |
anl_q()$qenv %>% teal.code::eval_code( |
| 470 | 7x |
code = bquote({
|
| 471 | 7x |
tbl <- goshawk::t_summarytable( |
| 472 | 7x |
data = ANL, |
| 473 | 7x |
trt_group = .(trt_group), |
| 474 | 7x |
param_var = .(param_var), |
| 475 | 7x |
param = .(param), |
| 476 | 7x |
xaxis_var = .(xaxis_var), |
| 477 | 7x |
facet_var = .(facet_var) |
| 478 |
) |
|
| 479 | 7x |
tbl |
| 480 |
}) |
|
| 481 |
) |
|
| 482 | 6x |
}), 800) |
| 483 | ||
| 484 | 6x |
plot_r <- reactive({
|
| 485 | 20x |
create_plot()[["p"]] |
| 486 |
}) |
|
| 487 | ||
| 488 | 6x |
boxplot_data <- teal.widgets::plot_with_settings_srv( |
| 489 | 6x |
id = "boxplot", |
| 490 | 6x |
plot_r = plot_r, |
| 491 | 6x |
height = plot_height, |
| 492 | 6x |
width = plot_width, |
| 493 | 6x |
brushing = TRUE |
| 494 |
) |
|
| 495 | ||
| 496 | 6x |
output$table_ui <- DT::renderDataTable({
|
| 497 | 13x |
req(create_table()) |
| 498 | ! |
tbl <- create_table()[["tbl"]] |
| 499 | ||
| 500 | ! |
numeric_cols <- setdiff(names(dplyr::select_if(tbl, is.numeric)), "n") |
| 501 | ||
| 502 | ! |
DT::datatable(tbl, |
| 503 | ! |
rownames = FALSE, options = list(scrollX = TRUE) |
| 504 |
) %>% |
|
| 505 | ! |
DT::formatRound(numeric_cols, 4) |
| 506 |
}) |
|
| 507 | ||
| 508 | 6x |
joined_qenvs <- reactive({
|
| 509 | ! |
req(create_plot(), create_table()) |
| 510 | ! |
c(create_plot(), create_table()) |
| 511 |
}) |
|
| 512 | ||
| 513 | 6x |
code <- reactive(teal.code::get_code(joined_qenvs())) |
| 514 | ||
| 515 |
### REPORTER |
|
| 516 | 6x |
if (with_reporter) {
|
| 517 | 6x |
card_fun <- function(comment, label) {
|
| 518 | ! |
constraint_description <- paste( |
| 519 | ! |
"\nFacet By:", |
| 520 | ! |
if (length(input$facet_var) != 0) input$facet_var else "None", |
| 521 | ! |
"\nSelect an X-axis Variable:", |
| 522 | ! |
input$xaxis_var |
| 523 |
) |
|
| 524 | ! |
card <- report_card_template_goshawk( |
| 525 | ! |
title = "Box Plot", |
| 526 | ! |
label = label, |
| 527 | ! |
with_filter = with_filter, |
| 528 | ! |
filter_panel_api = filter_panel_api, |
| 529 | ! |
constraint_list = list( |
| 530 | ! |
constraint_var = input$constraint_var, |
| 531 | ! |
constraint_range_min = input$constraint_range_min, |
| 532 | ! |
constraint_range_max = input$constraint_range_max |
| 533 |
), |
|
| 534 | ! |
constraint_description = constraint_description, |
| 535 | ! |
style = "verbatim" |
| 536 |
) |
|
| 537 | ! |
card$append_text("Plot", "header3")
|
| 538 | ! |
card$append_plot(plot_r(), dim = boxplot_data$dim()) |
| 539 | ! |
if (!comment == "") {
|
| 540 | ! |
card$append_text("Comment", "header3")
|
| 541 | ! |
card$append_text(comment) |
| 542 |
} |
|
| 543 | ! |
card$append_src(code()) |
| 544 | ! |
card |
| 545 |
} |
|
| 546 | 6x |
teal.reporter::add_card_button_srv("add_reporter", reporter = reporter, card_fun = card_fun)
|
| 547 |
} |
|
| 548 |
### |
|
| 549 | ||
| 550 |
# highlight plot area |
|
| 551 | 6x |
reactive_df <- debounce(reactive({
|
| 552 | 6x |
boxplot_brush <- boxplot_data$brush() |
| 553 | ||
| 554 | 6x |
ANL <- isolate(anl_q()$ANL) %>% droplevels() # nolint |
| 555 | ! |
validate_has_data(ANL, 2) |
| 556 | ||
| 557 | ! |
xvar <- isolate(input$xaxis_var) |
| 558 | ! |
yvar <- isolate(input$yaxis_var) |
| 559 | ! |
facetv <- isolate(input$facet_var) |
| 560 | ! |
trt_group <- isolate(input$trt_group) |
| 561 | ||
| 562 | ! |
req(all(c(xvar, yvar, facetv, trt_group) %in% names(ANL))) |
| 563 | ||
| 564 | ! |
teal.widgets::clean_brushedPoints( |
| 565 | ! |
dplyr::select( |
| 566 | ! |
ANL, "USUBJID", dplyr::all_of(c(trt_group, facetv)), |
| 567 | ! |
"AVISITCD", "PARAMCD", dplyr::all_of(c(xvar, yvar)), "LOQFL" |
| 568 |
), |
|
| 569 | ! |
boxplot_brush |
| 570 |
) |
|
| 571 | 6x |
}), 800) |
| 572 | ||
| 573 | 6x |
output$brush_data <- DT::renderDataTable({
|
| 574 | 6x |
numeric_cols <- names(dplyr::select_if(reactive_df(), is.numeric)) |
| 575 | ||
| 576 | ! |
DT::datatable(reactive_df(), |
| 577 | ! |
rownames = FALSE, options = list(scrollX = TRUE) |
| 578 |
) %>% |
|
| 579 | ! |
DT::formatRound(numeric_cols, 4) |
| 580 |
}) |
|
| 581 | ||
| 582 | 6x |
teal.widgets::verbatim_popup_srv( |
| 583 | 6x |
id = "rcode", |
| 584 | 6x |
verbatim_content = reactive(code()), |
| 585 | 6x |
title = "Show R Code for Boxplot" |
| 586 |
) |
|
| 587 |
}) |
|
| 588 |
} |
| 1 |
#' Spaghetti Plot |
|
| 2 |
#' |
|
| 3 |
#' This teal module renders the UI and calls the function |
|
| 4 |
#' that creates a spaghetti plot. |
|
| 5 |
#' |
|
| 6 |
#' @param label menu item label of the module in the teal app. |
|
| 7 |
#' @param dataname analysis data passed to the data argument of \code{\link[teal]{init}}.
|
|
| 8 |
#' E.g. `ADaM` structured laboratory data frame `ADLB`. |
|
| 9 |
#' @param param_var name of variable containing biomarker codes e.g. `PARAMCD`. |
|
| 10 |
#' @param param biomarker selected. |
|
| 11 |
#' @param param_var_label single name of variable in analysis data |
|
| 12 |
#' that includes parameter labels. |
|
| 13 |
#' @param idvar name of unique subject id variable. |
|
| 14 |
#' @param xaxis_var single name of variable in analysis data |
|
| 15 |
#' that is used as x-axis in the plot for the respective goshawk function. |
|
| 16 |
#' @param xaxis_var_level vector that can be used to define the factor level of `xaxis_var`. |
|
| 17 |
#' Only use it when `xaxis_var` is character or factor. |
|
| 18 |
#' @param filter_var data constraint variable. |
|
| 19 |
#' @param yaxis_var single name of variable in analysis data that is used as |
|
| 20 |
#' summary variable in the respective `goshawk` function. |
|
| 21 |
#' @param trt_group \code{\link[teal.transform]{choices_selected}} object with available choices and pre-selected option
|
|
| 22 |
#' for variable names representing treatment group e.g. `ARM`. |
|
| 23 |
#' @param trt_group_level vector that can be used to define factor |
|
| 24 |
#' level of `trt_group`. |
|
| 25 |
#' @param man_color string vector representing customized colors |
|
| 26 |
#' @param color_comb name or hex value for combined treatment color. |
|
| 27 |
#' @param xtick numeric vector to define the tick values of `x-axis` |
|
| 28 |
#' when x variable is numeric. Default value is `waive()`. |
|
| 29 |
#' @param xlabel vector with same length of `xtick` to define the |
|
| 30 |
#' label of `x-axis` tick values. Default value is `waive()`. |
|
| 31 |
#' @param rotate_xlab `logical(1)` value indicating whether to rotate `x-axis` labels |
|
| 32 |
#' @param facet_ncol numeric value indicating number of facets per row. |
|
| 33 |
#' @param free_x `logical(1)` should scales be `"fixed"` (`FALSE`) of `"free"` (`TRUE`) for `x-axis` in |
|
| 34 |
#' \code{\link[ggplot2]{facet_wrap}} \code{scales} parameter.
|
|
| 35 |
#' @param plot_height controls plot height. |
|
| 36 |
#' @param plot_width optional, controls plot width. |
|
| 37 |
#' @param font_size control font size for title, `x-axis`, `y-axis` and legend font. |
|
| 38 |
#' @param dot_size plot dot size. |
|
| 39 |
#' @param group_stats control group mean or median overlay. |
|
| 40 |
#' @param hline_arb numeric vector of at most 2 values identifying intercepts for arbitrary horizontal lines. |
|
| 41 |
#' @param hline_arb_color a character vector of at most length of \code{hline_arb}.
|
|
| 42 |
#' naming the color for the arbitrary horizontal lines. |
|
| 43 |
#' @param hline_arb_label a character vector of at most length of \code{hline_arb}.
|
|
| 44 |
#' naming the label for the arbitrary horizontal lines. |
|
| 45 |
#' @param hline_vars a character vector to name the columns that will define additional horizontal lines. |
|
| 46 |
#' @param hline_vars_colors a character vector naming the colors for the additional horizontal lines. |
|
| 47 |
#' @param hline_vars_labels a character vector naming the labels for the additional horizontal lines that will appear |
|
| 48 |
#' in the legend. |
|
| 49 |
#' @inheritParams teal::module |
|
| 50 |
#' @inheritParams teal.widgets::standard_layout |
|
| 51 |
#' |
|
| 52 |
#' @author Wenyi Liu (luiw2) wenyi.liu@roche.com |
|
| 53 |
#' @author Balazs Toth (tothb2) toth.balazs@gene.com |
|
| 54 |
#' |
|
| 55 |
#' @return \code{shiny} object
|
|
| 56 |
#' |
|
| 57 |
#' @export |
|
| 58 |
#' |
|
| 59 |
#' @examples |
|
| 60 |
#' # Example using ADaM structure analysis dataset. |
|
| 61 |
#' data <- teal_data() |
|
| 62 |
#' data <- within(data, {
|
|
| 63 |
#' library(dplyr) |
|
| 64 |
#' library(stringr) |
|
| 65 |
#' |
|
| 66 |
#' # use non-exported function from goshawk |
|
| 67 |
#' .h_identify_loq_values <- getFromNamespace("h_identify_loq_values", "goshawk")
|
|
| 68 |
#' |
|
| 69 |
#' # original ARM value = dose value |
|
| 70 |
#' .arm_mapping <- list( |
|
| 71 |
#' "A: Drug X" = "150mg QD", |
|
| 72 |
#' "B: Placebo" = "Placebo", |
|
| 73 |
#' "C: Combination" = "Combination" |
|
| 74 |
#' ) |
|
| 75 |
#' set.seed(1) # @linksto ADSL ADLB |
|
| 76 |
#' ADSL <- rADSL |
|
| 77 |
#' ADLB <- rADLB |
|
| 78 |
#' .var_labels <- lapply(ADLB, function(x) attributes(x)$label) |
|
| 79 |
#' ADLB <- ADLB %>% |
|
| 80 |
#' mutate( |
|
| 81 |
#' AVISITCD = case_when( |
|
| 82 |
#' AVISIT == "SCREENING" ~ "SCR", |
|
| 83 |
#' AVISIT == "BASELINE" ~ "BL", |
|
| 84 |
#' grepl("WEEK", AVISIT) ~ paste("W", str_extract(AVISIT, "(?<=(WEEK ))[0-9]+")),
|
|
| 85 |
#' TRUE ~ as.character(NA) |
|
| 86 |
#' ), |
|
| 87 |
#' AVISITCDN = case_when( |
|
| 88 |
#' AVISITCD == "SCR" ~ -2, |
|
| 89 |
#' AVISITCD == "BL" ~ 0, |
|
| 90 |
#' grepl("W", AVISITCD) ~ as.numeric(gsub("[^0-9]*", "", AVISITCD)),
|
|
| 91 |
#' TRUE ~ as.numeric(NA) |
|
| 92 |
#' ), |
|
| 93 |
#' AVISITCD = factor(AVISITCD) %>% reorder(AVISITCDN), |
|
| 94 |
#' TRTORD = case_when( |
|
| 95 |
#' ARMCD == "ARM C" ~ 1, |
|
| 96 |
#' ARMCD == "ARM B" ~ 2, |
|
| 97 |
#' ARMCD == "ARM A" ~ 3 |
|
| 98 |
#' ), |
|
| 99 |
#' ARM = as.character(.arm_mapping[match(ARM, names(.arm_mapping))]), |
|
| 100 |
#' ARM = factor(ARM) %>% reorder(TRTORD), |
|
| 101 |
#' ACTARM = as.character(.arm_mapping[match(ACTARM, names(.arm_mapping))]), |
|
| 102 |
#' ACTARM = factor(ACTARM) %>% reorder(TRTORD), |
|
| 103 |
#' ANRLO = 30, |
|
| 104 |
#' ANRHI = 75 |
|
| 105 |
#' ) %>% |
|
| 106 |
#' rowwise() %>% |
|
| 107 |
#' group_by(PARAMCD) %>% |
|
| 108 |
#' mutate(LBSTRESC = ifelse(USUBJID %in% sample(USUBJID, 1, replace = TRUE), |
|
| 109 |
#' paste("<", round(runif(1, min = 25, max = 30))), LBSTRESC
|
|
| 110 |
#' )) %>% |
|
| 111 |
#' mutate(LBSTRESC = ifelse(USUBJID %in% sample(USUBJID, 1, replace = TRUE), |
|
| 112 |
#' paste(">", round(runif(1, min = 70, max = 75))), LBSTRESC
|
|
| 113 |
#' )) %>% |
|
| 114 |
#' ungroup() |
|
| 115 |
#' attr(ADLB[["ARM"]], "label") <- .var_labels[["ARM"]] |
|
| 116 |
#' attr(ADLB[["ACTARM"]], "label") <- .var_labels[["ACTARM"]] |
|
| 117 |
#' attr(ADLB[["ANRLO"]], "label") <- "Analysis Normal Range Lower Limit" |
|
| 118 |
#' attr(ADLB[["ANRHI"]], "label") <- "Analysis Normal Range Upper Limit" |
|
| 119 |
#' |
|
| 120 |
#' # add LLOQ and ULOQ variables |
|
| 121 |
#' ALB_LOQS <- .h_identify_loq_values(ADLB, "LOQFL") |
|
| 122 |
#' ADLB <- left_join(ADLB, ALB_LOQS, by = "PARAM") |
|
| 123 |
#' }) |
|
| 124 |
#' |
|
| 125 |
#' join_keys(data) <- default_cdisc_join_keys[names(data)] |
|
| 126 |
#' |
|
| 127 |
#' app <- init( |
|
| 128 |
#' data = data, |
|
| 129 |
#' modules = modules( |
|
| 130 |
#' tm_g_gh_spaghettiplot( |
|
| 131 |
#' label = "Spaghetti Plot", |
|
| 132 |
#' dataname = "ADLB", |
|
| 133 |
#' param_var = "PARAMCD", |
|
| 134 |
#' param = choices_selected(c("ALT", "CRP", "IGA"), "ALT"),
|
|
| 135 |
#' idvar = "USUBJID", |
|
| 136 |
#' xaxis_var = choices_selected(c("Analysis Visit Code" = "AVISITCD"), "AVISITCD"),
|
|
| 137 |
#' yaxis_var = choices_selected(c("AVAL", "CHG", "PCHG"), "AVAL"),
|
|
| 138 |
#' filter_var = choices_selected( |
|
| 139 |
#' c("None" = "NONE", "Screening" = "BASE2", "Baseline" = "BASE"),
|
|
| 140 |
#' "NONE" |
|
| 141 |
#' ), |
|
| 142 |
#' trt_group = choices_selected(c("ARM", "ACTARM"), "ARM"),
|
|
| 143 |
#' color_comb = "#39ff14", |
|
| 144 |
#' man_color = c( |
|
| 145 |
#' "Combination" = "#000000", |
|
| 146 |
#' "Placebo" = "#fce300", |
|
| 147 |
#' "150mg QD" = "#5a2f5f" |
|
| 148 |
#' ), |
|
| 149 |
#' hline_arb = c(60, 50), |
|
| 150 |
#' hline_arb_color = c("grey", "red"),
|
|
| 151 |
#' hline_arb_label = c("default A", "default B"),
|
|
| 152 |
#' hline_vars = c("ANRHI", "ANRLO", "ULOQN", "LLOQN"),
|
|
| 153 |
#' hline_vars_colors = c("pink", "brown", "purple", "black"),
|
|
| 154 |
#' ) |
|
| 155 |
#' ) |
|
| 156 |
#' ) |
|
| 157 |
#' if (interactive()) {
|
|
| 158 |
#' shinyApp(app$ui, app$server) |
|
| 159 |
#' } |
|
| 160 |
#' |
|
| 161 |
tm_g_gh_spaghettiplot <- function(label, |
|
| 162 |
dataname, |
|
| 163 |
param_var, |
|
| 164 |
param, |
|
| 165 |
param_var_label = "PARAM", |
|
| 166 |
idvar, |
|
| 167 |
xaxis_var, |
|
| 168 |
yaxis_var, |
|
| 169 |
xaxis_var_level = NULL, |
|
| 170 |
filter_var = yaxis_var, |
|
| 171 |
trt_group, |
|
| 172 |
trt_group_level = NULL, |
|
| 173 |
group_stats = "NONE", |
|
| 174 |
man_color = NULL, |
|
| 175 |
color_comb = NULL, |
|
| 176 |
xtick = ggplot2::waiver(), |
|
| 177 |
xlabel = xtick, |
|
| 178 |
rotate_xlab = FALSE, |
|
| 179 |
facet_ncol = 2, |
|
| 180 |
free_x = FALSE, |
|
| 181 |
plot_height = c(600, 200, 2000), |
|
| 182 |
plot_width = NULL, |
|
| 183 |
font_size = c(12, 8, 20), |
|
| 184 |
dot_size = c(2, 1, 12), |
|
| 185 |
hline_arb = numeric(0), |
|
| 186 |
hline_arb_color = "red", |
|
| 187 |
hline_arb_label = "Horizontal line", |
|
| 188 |
hline_vars = character(0), |
|
| 189 |
hline_vars_colors = "green", |
|
| 190 |
hline_vars_labels = hline_vars, |
|
| 191 |
pre_output = NULL, |
|
| 192 |
post_output = NULL, |
|
| 193 |
transformators = list()) {
|
|
| 194 | ! |
message("Initializing tm_g_gh_spaghettiplot")
|
| 195 | ||
| 196 |
# Validate string inputs |
|
| 197 | ! |
checkmate::assert_string(label) |
| 198 | ! |
checkmate::assert_string(dataname) |
| 199 | ! |
checkmate::assert_string(param_var) |
| 200 | ! |
checkmate::assert_string(param_var_label) |
| 201 | ! |
checkmate::assert_string(idvar) |
| 202 | ! |
checkmate::assert_string(group_stats) |
| 203 | ||
| 204 |
# Validate choices_selected class inputs |
|
| 205 | ! |
checkmate::assert_class(param, "choices_selected") |
| 206 | ! |
checkmate::assert_class(xaxis_var, "choices_selected") |
| 207 | ! |
checkmate::assert_class(yaxis_var, "choices_selected") |
| 208 | ! |
checkmate::assert_class(trt_group, "choices_selected") |
| 209 | ||
| 210 |
# Validate flag inputs |
|
| 211 | ! |
checkmate::assert_flag(rotate_xlab) |
| 212 | ! |
checkmate::assert_flag(free_x) |
| 213 | ||
| 214 |
# Validate numeric vector inputs |
|
| 215 | ! |
checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) |
| 216 | ! |
checkmate::assert_numeric(plot_height[1], lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height") |
| 217 | ! |
checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) |
| 218 | ! |
checkmate::assert_numeric( |
| 219 | ! |
plot_width[1], |
| 220 | ! |
lower = plot_width[2], upper = plot_width[3], |
| 221 | ! |
null.ok = TRUE, .var.name = "plot_width" |
| 222 |
) |
|
| 223 | ! |
checkmate::assert_numeric(font_size, len = 3, any.missing = FALSE, finite = TRUE) |
| 224 | ! |
checkmate::assert_numeric(dot_size, len = 3, any.missing = FALSE, finite = TRUE) |
| 225 | ||
| 226 |
# Validate color manual if provided |
|
| 227 | ! |
checkmate::assert_character(man_color, null.ok = TRUE) |
| 228 | ! |
checkmate::assert_character(color_comb, null.ok = TRUE) |
| 229 | ! |
checkmate::assert_character(hline_arb_color) |
| 230 | ! |
checkmate::assert_character(hline_arb_label) |
| 231 | ||
| 232 |
# Validate facet columns |
|
| 233 | ! |
checkmate::assert_int(facet_ncol, lower = 1) |
| 234 | ||
| 235 |
# Validate line arguments |
|
| 236 | ! |
validate_line_arb_arg(hline_arb, hline_arb_color, hline_arb_label) |
| 237 | ! |
validate_line_vars_arg(hline_vars, hline_vars_colors, hline_vars_labels) |
| 238 | ||
| 239 | ! |
args <- as.list(environment()) |
| 240 | ||
| 241 | ! |
module( |
| 242 | ! |
label = label, |
| 243 | ! |
server = srv_g_spaghettiplot, |
| 244 | ! |
server_args = list( |
| 245 | ! |
dataname = dataname, |
| 246 | ! |
idvar = idvar, |
| 247 | ! |
param_var = param_var, |
| 248 | ! |
xaxis_var_level = xaxis_var_level, |
| 249 | ! |
trt_group_level = trt_group_level, |
| 250 | ! |
man_color = man_color, |
| 251 | ! |
color_comb = color_comb, |
| 252 | ! |
param_var_label = param_var_label, |
| 253 | ! |
xtick = xtick, |
| 254 | ! |
xlabel = xlabel, |
| 255 | ! |
plot_height = plot_height, |
| 256 | ! |
plot_width = plot_width, |
| 257 | ! |
hline_vars_colors = hline_vars_colors, |
| 258 | ! |
hline_vars_labels = hline_vars_labels, |
| 259 | ! |
module_args = args |
| 260 |
), |
|
| 261 | ! |
ui = g_ui_spaghettiplot, |
| 262 | ! |
ui_args = args, |
| 263 | ! |
transformators = transformators, |
| 264 | ! |
datanames = dataname |
| 265 |
) |
|
| 266 |
} |
|
| 267 | ||
| 268 |
g_ui_spaghettiplot <- function(id, ...) {
|
|
| 269 | ! |
ns <- NS(id) |
| 270 | ! |
a <- list(...) |
| 271 | ||
| 272 | ! |
tags$div( |
| 273 | ! |
teal.widgets::standard_layout( |
| 274 | ! |
output = templ_ui_output_datatable(ns), |
| 275 | ! |
encoding = tags$div( |
| 276 |
### Reporter |
|
| 277 | ! |
teal.reporter::add_card_button_ui(ns("add_reporter"), label = "Add Report Card"),
|
| 278 | ! |
tags$br(), tags$br(), |
| 279 |
### |
|
| 280 | ! |
templ_ui_dataname(a$dataname), |
| 281 | ! |
uiOutput(ns("axis_selections")),
|
| 282 | ! |
radioButtons( |
| 283 | ! |
ns("group_stats"),
|
| 284 | ! |
"Group Statistics", |
| 285 | ! |
c("None" = "NONE", "Mean" = "MEAN", "Median" = "MEDIAN"),
|
| 286 | ! |
inline = TRUE |
| 287 |
), |
|
| 288 | ! |
templ_ui_constraint(ns), # required by constr_anl_q |
| 289 | ! |
if (length(a$hline_vars) > 0) {
|
| 290 | ! |
teal.widgets::optionalSelectInput( |
| 291 | ! |
ns("hline_vars"),
|
| 292 | ! |
label = "Add Horizontal Range Line(s):", |
| 293 | ! |
choices = a$hline_vars, |
| 294 | ! |
selected = NULL, |
| 295 | ! |
multiple = TRUE |
| 296 |
) |
|
| 297 |
}, |
|
| 298 | ! |
ui_arbitrary_lines(id = ns("hline_arb"), a$hline_arb, a$hline_arb_label, a$hline_arb_color),
|
| 299 | ! |
bslib::accordion( |
| 300 | ! |
bslib::accordion_panel( |
| 301 | ! |
title = "Plot Aesthetic Settings", |
| 302 | ! |
tags$div( |
| 303 | ! |
toggle_slider_ui( |
| 304 | ! |
ns("yrange_scale"),
|
| 305 | ! |
label = "Y-Axis Range Zoom" |
| 306 |
), |
|
| 307 | ! |
tags$div( |
| 308 | ! |
class = "flex flex-wrap items-center", |
| 309 | ! |
tags$div( |
| 310 | ! |
class = "mr-1", |
| 311 | ! |
tags$span(tags$strong("Number of Plots Per Row:"))
|
| 312 |
), |
|
| 313 | ! |
tags$div( |
| 314 | ! |
class = "w-65px", |
| 315 | ! |
numericInput(ns("facet_ncol"), "", a$facet_ncol, min = 1)
|
| 316 |
) |
|
| 317 |
) |
|
| 318 |
), |
|
| 319 | ! |
checkboxInput(ns("free_x"), "Free X-Axis Scales", a$free_x),
|
| 320 | ! |
checkboxInput(ns("rotate_xlab"), "Rotate X-Axis Label", a$rotate_xlab),
|
| 321 | ! |
teal.widgets::optionalSliderInputValMinMax(ns("font_size"), "Font Size", a$font_size, ticks = FALSE),
|
| 322 | ! |
teal.widgets::optionalSliderInputValMinMax(ns("dot_size"), "Dot Size", a$dot_size, ticks = FALSE),
|
| 323 | ! |
teal.widgets::optionalSliderInputValMinMax( |
| 324 | ! |
ns("alpha"),
|
| 325 | ! |
"Line Alpha", |
| 326 | ! |
a$alpha, |
| 327 | ! |
value_min_max = c(0.8, 0.0, 1.0), step = 0.1, ticks = FALSE |
| 328 |
) |
|
| 329 |
) |
|
| 330 |
) |
|
| 331 |
), |
|
| 332 | ! |
forms = tagList( |
| 333 | ! |
teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code")
|
| 334 |
), |
|
| 335 | ! |
pre_output = a$pre_output, |
| 336 | ! |
post_output = a$post_output |
| 337 |
) |
|
| 338 |
) |
|
| 339 |
} |
|
| 340 | ||
| 341 | ||
| 342 | ||
| 343 |
srv_g_spaghettiplot <- function(id, |
|
| 344 |
data, |
|
| 345 |
reporter, |
|
| 346 |
filter_panel_api, |
|
| 347 |
dataname, |
|
| 348 |
idvar, |
|
| 349 |
param_var, |
|
| 350 |
trt_group, |
|
| 351 |
man_color, |
|
| 352 |
color_comb, |
|
| 353 |
xaxis_var_level, |
|
| 354 |
trt_group_level, |
|
| 355 |
param_var_label, |
|
| 356 |
xtick, |
|
| 357 |
xlabel, |
|
| 358 |
plot_height, |
|
| 359 |
plot_width, |
|
| 360 |
hline_vars_colors, |
|
| 361 |
hline_vars_labels, |
|
| 362 |
module_args) {
|
|
| 363 | ! |
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter") |
| 364 | ! |
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI") |
| 365 | ! |
checkmate::assert_class(data, "reactive") |
| 366 | ! |
checkmate::assert_class(shiny::isolate(data()), "teal_data") |
| 367 | ||
| 368 | ! |
moduleServer(id, function(input, output, session) {
|
| 369 | ! |
teal.logger::log_shiny_input_changes(input, namespace = "teal.goshawk") |
| 370 | ! |
output$axis_selections <- renderUI({
|
| 371 | ! |
env <- shiny::isolate(as.list(data()[[".raw_data"]])) |
| 372 | ! |
resolved_x <- teal.transform::resolve_delayed(module_args$xaxis_var, env) |
| 373 | ! |
resolved_y <- teal.transform::resolve_delayed(module_args$yaxis_var, env) |
| 374 | ! |
resolved_param <- teal.transform::resolve_delayed(module_args$param, env) |
| 375 | ! |
resolved_trt <- teal.transform::resolve_delayed(module_args$trt_group, env) |
| 376 | ! |
templ_ui_params_vars( |
| 377 | ! |
session$ns, |
| 378 |
# xparam and yparam are identical, so we only show the user one |
|
| 379 | ! |
xparam_choices = resolved_param$choices, |
| 380 | ! |
xparam_selected = resolved_param$selected, |
| 381 | ! |
xparam_label = "Select a Biomarker", |
| 382 | ! |
xchoices = resolved_x$choices, |
| 383 | ! |
xselected = resolved_x$selected, |
| 384 | ! |
ychoices = resolved_y$choices, |
| 385 | ! |
yselected = resolved_y$selected, |
| 386 | ! |
trt_choices = resolved_trt$choices, |
| 387 | ! |
trt_selected = resolved_trt$selected |
| 388 |
) |
|
| 389 |
}) |
|
| 390 | ||
| 391 |
# reused in all modules |
|
| 392 | ! |
anl_q_output <- constr_anl_q( |
| 393 | ! |
session, input, data, dataname, |
| 394 | ! |
param_id = "xaxis_param", param_var = param_var, trt_group = input$trt_group, min_rows = 1 |
| 395 |
) |
|
| 396 | ||
| 397 | ! |
anl_q <- anl_q_output()$value |
| 398 | ||
| 399 |
# update sliders for axes taking constraints into account |
|
| 400 | ! |
data_state <- reactive({
|
| 401 | ! |
get_data_range_states( |
| 402 | ! |
varname = input$yaxis_var, |
| 403 | ! |
paramname = input$xaxis_param, |
| 404 | ! |
ANL = anl_q()$ANL |
| 405 |
) |
|
| 406 |
}) |
|
| 407 | ! |
yrange_slider <- toggle_slider_server("yrange_scale", data_state)
|
| 408 | ! |
keep_data_const_opts_updated(session, input, anl_q, "xaxis_param") |
| 409 | ||
| 410 | ! |
horizontal_line <- srv_arbitrary_lines("hline_arb")
|
| 411 | ||
| 412 | ! |
iv_r <- reactive({
|
| 413 | ! |
iv <- shinyvalidate::InputValidator$new() |
| 414 | ||
| 415 | ! |
iv$add_rule("xaxis_param", shinyvalidate::sv_required("Please select a biomarker"))
|
| 416 | ! |
iv$add_rule("trt_group", shinyvalidate::sv_required("Please select a treatment variable"))
|
| 417 | ! |
iv$add_rule("xaxis_var", shinyvalidate::sv_required("Please select an X-Axis variable"))
|
| 418 | ! |
iv$add_rule("yaxis_var", shinyvalidate::sv_required("Please select a Y-Axis variable"))
|
| 419 | ! |
iv$add_rule("facet_ncol", plots_per_row_validate_rules())
|
| 420 | ||
| 421 | ! |
iv$add_validator(horizontal_line()$iv_r()) |
| 422 | ! |
iv$add_validator(anl_q_output()$iv_r()) |
| 423 | ! |
iv$enable() |
| 424 | ! |
iv |
| 425 |
}) |
|
| 426 | ||
| 427 | ||
| 428 | ! |
plot_q <- debounce(reactive({
|
| 429 | ! |
teal::validate_inputs(iv_r()) |
| 430 | ! |
req(anl_q()) |
| 431 |
# nolint start |
|
| 432 | ! |
ylim <- yrange_slider$value |
| 433 | ! |
facet_ncol <- input$facet_ncol |
| 434 | ! |
facet_scales <- ifelse(input$free_x, "free_x", "fixed") |
| 435 | ||
| 436 | ! |
rotate_xlab <- input$rotate_xlab |
| 437 | ! |
hline_arb <- horizontal_line()$line_arb |
| 438 | ! |
hline_arb_label <- horizontal_line()$line_arb_label |
| 439 | ! |
hline_arb_color <- horizontal_line()$line_arb_color |
| 440 | ! |
group_stats <- input$group_stats |
| 441 | ! |
font_size <- input$font_size |
| 442 | ! |
dot_size <- input$dot_size |
| 443 | ! |
alpha <- input$alpha |
| 444 | ! |
validate(need(input$trt_group, "Please select a treatment variable")) |
| 445 | ! |
trt_group <- input$trt_group |
| 446 | ||
| 447 |
# Below inputs should trigger plot via updates of other reactive objects (i.e. anl_q()) and some inputs |
|
| 448 | ! |
param <- input$xaxis_param |
| 449 | ! |
xaxis_var <- input$xaxis_var |
| 450 | ! |
yaxis_var <- input$yaxis_var |
| 451 | ! |
hline_vars <- input$hline_vars |
| 452 |
# nolint end |
|
| 453 | ||
| 454 | ! |
private_qenv <- anl_q()$qenv |
| 455 | ||
| 456 |
# this code is needed to make sure the waiver attribute |
|
| 457 |
# of ggplot2::waiver is correctly passed to goshawk's spaghettiplot |
|
| 458 | ! |
if (!methods::is(xtick, "waiver")) {
|
| 459 | ! |
private_qenv <- teal.code::eval_code( |
| 460 | ! |
object = private_qenv, |
| 461 | ! |
code = bquote(xtick <- .(xtick)) |
| 462 |
) |
|
| 463 |
} else {
|
|
| 464 | ! |
private_qenv <- teal.code::eval_code( |
| 465 | ! |
object = private_qenv, |
| 466 | ! |
code = quote(xtick <- ggplot2::waiver()) |
| 467 |
) |
|
| 468 |
} |
|
| 469 | ||
| 470 | ! |
if (!methods::is(xlabel, "waiver")) {
|
| 471 | ! |
private_qenv <- teal.code::eval_code( |
| 472 | ! |
object = private_qenv, |
| 473 | ! |
code = bquote(xlabel <- .(xlabel)) |
| 474 |
) |
|
| 475 |
} else {
|
|
| 476 | ! |
private_qenv <- teal.code::eval_code( |
| 477 | ! |
object = private_qenv, |
| 478 | ! |
code = quote(xlabel <- ggplot2::waiver()) |
| 479 |
) |
|
| 480 |
} |
|
| 481 | ||
| 482 | ! |
teal.code::eval_code( |
| 483 | ! |
object = private_qenv, |
| 484 | ! |
code = bquote({
|
| 485 | ! |
p <- goshawk::g_spaghettiplot( |
| 486 | ! |
data = ANL, |
| 487 | ! |
subj_id = .(idvar), |
| 488 | ! |
biomarker_var = .(param_var), |
| 489 | ! |
biomarker_var_label = .(param_var_label), |
| 490 | ! |
biomarker = .(param), |
| 491 | ! |
value_var = .(yaxis_var), |
| 492 | ! |
trt_group = .(trt_group), |
| 493 | ! |
trt_group_level = .(trt_group_level), |
| 494 | ! |
time = .(xaxis_var), |
| 495 | ! |
time_level = .(xaxis_var_level), |
| 496 | ! |
color_manual = .(man_color), |
| 497 | ! |
color_comb = .(color_comb), |
| 498 | ! |
ylim = .(ylim), |
| 499 | ! |
facet_ncol = .(facet_ncol), |
| 500 | ! |
facet_scales = .(facet_scales), |
| 501 | ! |
hline_arb = .(hline_arb), |
| 502 | ! |
hline_arb_label = .(hline_arb_label), |
| 503 | ! |
hline_arb_color = .(hline_arb_color), |
| 504 | ! |
xtick = xtick, |
| 505 | ! |
xlabel = xlabel, |
| 506 | ! |
rotate_xlab = .(rotate_xlab), |
| 507 | ! |
font_size = .(font_size), |
| 508 | ! |
dot_size = .(dot_size), |
| 509 | ! |
alpha = .(alpha), |
| 510 | ! |
group_stats = .(group_stats), |
| 511 | ! |
hline_vars = .(hline_vars), |
| 512 | ! |
hline_vars_colors = .(hline_vars_colors[seq_along(hline_vars)]), |
| 513 | ! |
hline_vars_labels = .(hline_vars_labels[seq_along(hline_vars)]) |
| 514 |
) |
|
| 515 | ! |
p |
| 516 |
}) |
|
| 517 |
) |
|
| 518 | ! |
}), 800) |
| 519 | ||
| 520 | ! |
plot_r <- reactive({
|
| 521 | ! |
plot_q()[["p"]] |
| 522 |
}) |
|
| 523 | ||
| 524 | ! |
plot_data <- teal.widgets::plot_with_settings_srv( |
| 525 | ! |
id = "plot", |
| 526 | ! |
plot_r = plot_r, |
| 527 | ! |
height = plot_height, |
| 528 | ! |
width = plot_width, |
| 529 | ! |
brushing = TRUE |
| 530 |
) |
|
| 531 | ||
| 532 | ! |
code <- reactive(teal.code::get_code(plot_q())) |
| 533 | ||
| 534 |
### REPORTER |
|
| 535 | ! |
if (with_reporter) {
|
| 536 | ! |
card_fun <- function(comment, label) {
|
| 537 | ! |
card <- report_card_template_goshawk( |
| 538 | ! |
title = "Spaghetti Plot", |
| 539 | ! |
label = label, |
| 540 | ! |
with_filter = with_filter, |
| 541 | ! |
filter_panel_api = filter_panel_api, |
| 542 | ! |
constraint_list = list( |
| 543 | ! |
constraint_var = input$constraint_var, |
| 544 | ! |
constraint_range_min = input$constraint_range_min, |
| 545 | ! |
constraint_range_max = input$constraint_range_max |
| 546 |
) |
|
| 547 |
) |
|
| 548 | ! |
card$append_text("Spaghetti Plot", "header3")
|
| 549 | ! |
card$append_plot(plot_r(), dim = plot_data$dim()) |
| 550 | ! |
if (!comment == "") {
|
| 551 | ! |
card$append_text("Comment", "header3")
|
| 552 | ! |
card$append_text(comment) |
| 553 |
} |
|
| 554 | ! |
card$append_src(code()) |
| 555 | ! |
card |
| 556 |
} |
|
| 557 | ! |
teal.reporter::add_card_button_srv("add_reporter", reporter = reporter, card_fun = card_fun)
|
| 558 |
} |
|
| 559 |
### |
|
| 560 | ||
| 561 | ! |
reactive_df <- debounce(reactive({
|
| 562 | ! |
plot_brush <- plot_data$brush() |
| 563 | ||
| 564 | ! |
ANL <- isolate(anl_q()$ANL) # nolint |
| 565 | ! |
validate_has_data(ANL, 1) |
| 566 | ||
| 567 | ! |
xvar <- isolate(input$xaxis_var) |
| 568 | ! |
yvar <- isolate(input$yaxis_var) |
| 569 | ! |
trt_group <- isolate(input$trt_group) |
| 570 | ||
| 571 | ! |
req(all(c(xvar, yvar) %in% names(ANL))) |
| 572 | ||
| 573 | ! |
df <- teal.widgets::clean_brushedPoints( |
| 574 | ! |
dplyr::select( |
| 575 | ! |
ANL, "USUBJID", dplyr::all_of(trt_group), "PARAMCD", |
| 576 | ! |
dplyr::all_of(c(xvar, yvar)), "LOQFL" |
| 577 |
), |
|
| 578 | ! |
plot_brush |
| 579 |
) |
|
| 580 | ! |
df[order(df$PARAMCD, df[[trt_group]], df$USUBJID, df[[xvar]]), ] |
| 581 | ! |
}), 800) |
| 582 | ||
| 583 | ! |
output$brush_data <- DT::renderDataTable({
|
| 584 | ! |
numeric_cols <- names(dplyr::select_if(reactive_df(), is.numeric)) |
| 585 | ||
| 586 | ! |
DT::datatable(reactive_df(), |
| 587 | ! |
rownames = FALSE, options = list(scrollX = TRUE) |
| 588 |
) %>% |
|
| 589 | ! |
DT::formatRound(numeric_cols, 4) |
| 590 |
}) |
|
| 591 | ||
| 592 | ! |
teal.widgets::verbatim_popup_srv( |
| 593 | ! |
id = "rcode", |
| 594 | ! |
verbatim_content = reactive(code()), |
| 595 | ! |
title = "Show R Code for Spaghetti Plot" |
| 596 |
) |
|
| 597 |
}) |
|
| 598 |
} |
| 1 |
#' UI module to arbitrary lines |
|
| 2 |
#' |
|
| 3 |
#' UI module to input either horizontal or vertical lines to a plot via comma separated values |
|
| 4 |
#' |
|
| 5 |
#' @param id (`character(1)`)\cr |
|
| 6 |
#' defining namespace of the `shiny` module. |
|
| 7 |
#' @param line_arb (`numeric`)\cr |
|
| 8 |
#' default values for the `textInput` defining values of arbitrary lines |
|
| 9 |
#' @param line_arb_color (`character`)\cr |
|
| 10 |
#' default values for the `textInput` defining colors of arbitrary lines |
|
| 11 |
#' @param line_arb_label (`character`)\cr |
|
| 12 |
#' default values for the `textInput` defining labels of arbitrary lines |
|
| 13 |
#' @param title (`character(1)`)\cr |
|
| 14 |
#' title of the arbitrary lines input. The default is "Arbitrary Horizontal Lines". |
|
| 15 |
#' @return (`shiny.tag`) an input to define values, colors and labels for arbitrary |
|
| 16 |
#' straight lines. |
|
| 17 |
#' @keywords internal |
|
| 18 |
ui_arbitrary_lines <- function(id, line_arb, line_arb_label, line_arb_color, title = "Arbitrary horizontal lines:") {
|
|
| 19 | 12x |
ns <- NS(id) |
| 20 | 12x |
tags$div( |
| 21 | 12x |
tags$b( |
| 22 | 12x |
title, |
| 23 | 12x |
bslib::tooltip( |
| 24 | 12x |
trigger = icon("circle-info"),
|
| 25 | 12x |
tags$span( |
| 26 | 12x |
"For multiple lines, supply a comma separated list of values." |
| 27 |
) |
|
| 28 |
) |
|
| 29 |
), |
|
| 30 | 12x |
textInput( |
| 31 | 12x |
ns("line_arb"),
|
| 32 | 12x |
"Value:", |
| 33 | 12x |
value = paste(line_arb, collapse = ", ") |
| 34 |
), |
|
| 35 | 12x |
textInput(ns("line_arb_label"), label = "Label:", value = paste(line_arb_label, collapse = ", ")),
|
| 36 | 12x |
textInput(ns("line_arb_color"), label = "Color:", value = paste(line_arb_color, collapse = ", "))
|
| 37 |
) |
|
| 38 |
} |
|
| 39 |
#' Server module to arbitrary lines |
|
| 40 |
#' |
|
| 41 |
#' Server to validate and transform the comma separated values into vectors of values |
|
| 42 |
#' to be passed into goshawk functions. |
|
| 43 |
#' @inheritParams shiny::moduleServer |
|
| 44 |
#' @return (`reactive`) returning a `list` containing `line_arb`, `line_arb_color`, |
|
| 45 |
#' `line_arb_label` which are validated and could be passed to `goshawk` plot functions. |
|
| 46 |
#' @keywords internal |
|
| 47 |
srv_arbitrary_lines <- function(id) {
|
|
| 48 | 6x |
moduleServer(id, function(input, output, session) {
|
| 49 | 6x |
comma_sep_to_values <- function(values, wrapper_fun = trimws) {
|
| 50 | 230x |
vals <- strsplit(values, "\\s{0,},\\s{0,}")[[1]]
|
| 51 | 230x |
suppressWarnings(wrapper_fun(vals)) |
| 52 |
} |
|
| 53 | ||
| 54 | 6x |
iv_r <- reactive({
|
| 55 | 6x |
iv <- shinyvalidate::InputValidator$new() |
| 56 | 6x |
iv$add_rule("line_arb", shinyvalidate::sv_optional())
|
| 57 | 6x |
iv$add_rule( |
| 58 | 6x |
"line_arb", |
| 59 | 6x |
~ if (any(is.na(comma_sep_to_values(., as.numeric)))) {
|
| 60 | ! |
"Arbitrary lines values should be a comma separated list of numbers" |
| 61 |
} |
|
| 62 |
) |
|
| 63 | ||
| 64 | 6x |
iv_color <- shinyvalidate::InputValidator$new() |
| 65 | 6x |
iv_color$condition(~ length(line_arb()) != 0) |
| 66 | ||
| 67 | 6x |
iv_color$add_rule("line_arb_color", shinyvalidate::sv_optional())
|
| 68 | 6x |
iv_color$add_rule( |
| 69 | 6x |
"line_arb_color", |
| 70 | 6x |
~ if (!length(comma_sep_to_values(.)) %in% c(1, length(line_arb()))) {
|
| 71 | ! |
sprintf( |
| 72 | ! |
"Line input error: number of colors should be equal to 1, the number of lines (%d) or left blank for 'red'", |
| 73 | ! |
length(line_arb()) |
| 74 |
) |
|
| 75 |
} |
|
| 76 |
) |
|
| 77 | 6x |
iv_color$add_rule("line_arb_color", ~ if (!check_color(comma_sep_to_values(.))) {
|
| 78 | ! |
"The line colors entered cannot be converted to colors in R, please check your spelling" |
| 79 |
}) |
|
| 80 | 6x |
iv$add_validator(iv_color) |
| 81 | ||
| 82 | ||
| 83 | 6x |
iv_label <- shinyvalidate::InputValidator$new() |
| 84 | 6x |
iv_label$condition(~ length(line_arb()) != 0) |
| 85 | ||
| 86 | 6x |
iv_label$add_rule("line_arb_label", shinyvalidate::sv_optional())
|
| 87 | 6x |
iv_label$add_rule( |
| 88 | 6x |
"line_arb_label", |
| 89 | 6x |
~ if (!length(comma_sep_to_values(.)) %in% c(1, length(line_arb()))) {
|
| 90 | ! |
sprintf( |
| 91 | ! |
"Line input error: number of labels should be equal to 1, the number of lines (%d) or left blank", |
| 92 | ! |
length(line_arb()) |
| 93 |
) |
|
| 94 |
} |
|
| 95 |
) |
|
| 96 | 6x |
iv$add_validator(iv_label) |
| 97 | 6x |
iv |
| 98 |
}) |
|
| 99 | ||
| 100 | 6x |
line_arb <- reactive({
|
| 101 | 6x |
req(!is.null(input$line_arb)) |
| 102 | 6x |
comma_sep_to_values(input$line_arb, as.numeric) |
| 103 |
}) |
|
| 104 | ||
| 105 | 6x |
line_arb_label <- reactive({
|
| 106 | 6x |
if (length(line_arb()) == 0) {
|
| 107 | ! |
return(character(0)) |
| 108 |
} |
|
| 109 | 6x |
val <- comma_sep_to_values(input$line_arb_label) |
| 110 | 6x |
if (length(val) == 0) {
|
| 111 | ! |
val <- "" |
| 112 |
} |
|
| 113 | 6x |
val |
| 114 |
}) |
|
| 115 | ||
| 116 | 6x |
line_arb_color <- reactive({
|
| 117 | 6x |
if (length(line_arb()) == 0) {
|
| 118 | ! |
return(character(0)) |
| 119 |
} |
|
| 120 | 6x |
val <- comma_sep_to_values(input$line_arb_color) |
| 121 | 6x |
if (length(val) == 0 || all(val == "")) {
|
| 122 | ! |
val <- "red" |
| 123 |
} |
|
| 124 | 6x |
val |
| 125 |
}) |
|
| 126 | ||
| 127 | 6x |
reactive( |
| 128 | 6x |
list( |
| 129 | 6x |
iv_r = iv_r, |
| 130 | 6x |
line_arb = line_arb(), |
| 131 | 6x |
line_arb_color = line_arb_color(), |
| 132 | 6x |
line_arb_label = line_arb_label() |
| 133 |
) |
|
| 134 |
) |
|
| 135 |
}) |
|
| 136 |
} |
|
| 137 | ||
| 138 |
check_color <- function(col) {
|
|
| 139 | 53x |
tryCatch( |
| 140 | 53x |
is.matrix(grDevices::col2rgb(col)), |
| 141 | 53x |
error = function(e) FALSE |
| 142 |
) |
|
| 143 |
} |
|
| 144 | ||
| 145 |
# to check the arbitrary line arguments |
|
| 146 |
validate_line_arb_arg <- function(line_arb, line_arb_color, line_arb_label) {
|
|
| 147 | 6x |
checkmate::assert_numeric(line_arb) |
| 148 | 6x |
if (length(line_arb) > 0) {
|
| 149 | 6x |
checkmate::assert( |
| 150 | 6x |
checkmate::check_string(line_arb_color), |
| 151 | 6x |
checkmate::check_character(line_arb_color, len = length(line_arb)) |
| 152 |
) |
|
| 153 | 6x |
checkmate::assert( |
| 154 | 6x |
checkmate::check_string(line_arb_label), |
| 155 | 6x |
checkmate::check_character(line_arb_label, len = length(line_arb)) |
| 156 |
) |
|
| 157 |
} |
|
| 158 |
} |
|
| 159 | ||
| 160 |
# to check the variable line arguments |
|
| 161 |
validate_line_vars_arg <- function(line_vars, line_vars_colors, line_vars_labels) {
|
|
| 162 | 6x |
checkmate::assert_character(line_vars) |
| 163 | 6x |
if (length(line_vars) > 0) {
|
| 164 | 6x |
checkmate::assert( |
| 165 | 6x |
checkmate::check_string(line_vars_colors), |
| 166 | 6x |
checkmate::check_character(line_vars_colors, len = length(line_vars)) |
| 167 |
) |
|
| 168 | 6x |
checkmate::assert( |
| 169 | 6x |
checkmate::check_string(line_vars_labels), |
| 170 | 6x |
checkmate::check_character(line_vars_labels, len = length(line_vars)) |
| 171 |
) |
|
| 172 |
} |
|
| 173 |
} |
| 1 |
#' Density Distribution Plot |
|
| 2 |
#' |
|
| 3 |
#' This teal module renders the UI and calls the functions that create a density distribution plot |
|
| 4 |
#' and an accompanying summary table. |
|
| 5 |
#' |
|
| 6 |
#' @param label menu item label of the module in the teal app. |
|
| 7 |
#' @param trt_group \code{\link[teal.transform]{choices_selected}} object with available choices and pre-selected option
|
|
| 8 |
#' for variable names representing treatment group e.g. `ARM`. |
|
| 9 |
#' @param color_manual vector of colors applied to treatment values. |
|
| 10 |
#' @param color_comb name or hex value for combined treatment color. |
|
| 11 |
#' @param plot_height controls plot height. |
|
| 12 |
#' @param plot_width optional, controls plot width. |
|
| 13 |
#' @param font_size font size control for title, `x-axis` label, `y-axis` label and legend. |
|
| 14 |
#' @param line_size plot line thickness. |
|
| 15 |
#' @param hline_arb numeric vector of at most 2 values identifying intercepts for arbitrary horizontal lines. |
|
| 16 |
#' @param hline_arb_color a character vector of at most length of \code{hline_arb}.
|
|
| 17 |
#' naming the color for the arbitrary horizontal lines. |
|
| 18 |
#' @param hline_arb_label a character vector of at most length of \code{hline_arb}.
|
|
| 19 |
#' naming the label for the arbitrary horizontal lines. |
|
| 20 |
#' @param facet_ncol numeric value indicating number of facets per row. |
|
| 21 |
#' @param comb_line display combined treatment line toggle. |
|
| 22 |
#' @param rotate_xlab 45 degree rotation of `x-axis` values. |
|
| 23 |
#' @param dataname analysis data passed to the data argument of \code{\link[teal]{init}}. E.g. `ADaM` structured
|
|
| 24 |
#' @param param_var name of variable containing biomarker codes e.g. \code{PARAMCD}.
|
|
| 25 |
#' @param param biomarker selected. |
|
| 26 |
#' @param xaxis_var name of variable containing biomarker results displayed on `x-axis` e.g. \code{BASE}.
|
|
| 27 |
#' |
|
| 28 |
#' @inheritParams teal.widgets::standard_layout |
|
| 29 |
#' @inheritParams teal::module |
|
| 30 |
#' |
|
| 31 |
#' |
|
| 32 |
#' @author Nick Paszty (npaszty) paszty.nicholas@gene.com |
|
| 33 |
#' @author Balazs Toth (tothb2) toth.balazs@gene.com |
|
| 34 |
#' |
|
| 35 |
#' @details None |
|
| 36 |
#' |
|
| 37 |
#' @export |
|
| 38 |
#' |
|
| 39 |
#' @examples |
|
| 40 |
#' # Example using ADaM structure analysis dataset. |
|
| 41 |
#' data <- teal_data() |
|
| 42 |
#' data <- within(data, {
|
|
| 43 |
#' library(dplyr) |
|
| 44 |
#' library(stringr) |
|
| 45 |
#' |
|
| 46 |
#' # original ARM value = dose value |
|
| 47 |
#' .arm_mapping <- list( |
|
| 48 |
#' "A: Drug X" = "150mg QD", |
|
| 49 |
#' "B: Placebo" = "Placebo", |
|
| 50 |
#' "C: Combination" = "Combination" |
|
| 51 |
#' ) |
|
| 52 |
#' set.seed(1) # @linksto ADSL ADLB |
|
| 53 |
#' ADSL <- rADSL |
|
| 54 |
#' ADLB <- rADLB |
|
| 55 |
#' .var_labels <- lapply(ADLB, function(x) attributes(x)$label) |
|
| 56 |
#' ADLB <- ADLB %>% |
|
| 57 |
#' mutate( |
|
| 58 |
#' AVISITCD = case_when( |
|
| 59 |
#' AVISIT == "SCREENING" ~ "SCR", |
|
| 60 |
#' AVISIT == "BASELINE" ~ "BL", |
|
| 61 |
#' grepl("WEEK", AVISIT) ~ paste("W", str_extract(AVISIT, "(?<=(WEEK ))[0-9]+")),
|
|
| 62 |
#' TRUE ~ as.character(NA) |
|
| 63 |
#' ), |
|
| 64 |
#' AVISITCDN = case_when( |
|
| 65 |
#' AVISITCD == "SCR" ~ -2, |
|
| 66 |
#' AVISITCD == "BL" ~ 0, |
|
| 67 |
#' grepl("W", AVISITCD) ~ as.numeric(gsub("[^0-9]*", "", AVISITCD)),
|
|
| 68 |
#' TRUE ~ as.numeric(NA) |
|
| 69 |
#' ), |
|
| 70 |
#' AVISITCD = factor(AVISITCD) %>% reorder(AVISITCDN), |
|
| 71 |
#' TRTORD = case_when( |
|
| 72 |
#' ARMCD == "ARM C" ~ 1, |
|
| 73 |
#' ARMCD == "ARM B" ~ 2, |
|
| 74 |
#' ARMCD == "ARM A" ~ 3 |
|
| 75 |
#' ), |
|
| 76 |
#' ARM = as.character(.arm_mapping[match(ARM, names(.arm_mapping))]), |
|
| 77 |
#' ARM = factor(ARM) %>% reorder(TRTORD), |
|
| 78 |
#' ACTARM = as.character(.arm_mapping[match(ACTARM, names(.arm_mapping))]), |
|
| 79 |
#' ACTARM = factor(ACTARM) %>% reorder(TRTORD) |
|
| 80 |
#' ) |
|
| 81 |
#' |
|
| 82 |
#' attr(ADLB[["ARM"]], "label") <- .var_labels[["ARM"]] |
|
| 83 |
#' attr(ADLB[["ACTARM"]], "label") <- .var_labels[["ACTARM"]] |
|
| 84 |
#' }) |
|
| 85 |
#' |
|
| 86 |
#' join_keys(data) <- default_cdisc_join_keys[names(data)] |
|
| 87 |
#' |
|
| 88 |
#' app <- init( |
|
| 89 |
#' data = data, |
|
| 90 |
#' modules = modules( |
|
| 91 |
#' tm_g_gh_density_distribution_plot( |
|
| 92 |
#' label = "Density Distribution Plot", |
|
| 93 |
#' dataname = "ADLB", |
|
| 94 |
#' param_var = "PARAMCD", |
|
| 95 |
#' param = choices_selected(c("ALT", "CRP", "IGA"), "ALT"),
|
|
| 96 |
#' xaxis_var = choices_selected(c("AVAL", "BASE", "CHG", "PCHG"), "AVAL"),
|
|
| 97 |
#' trt_group = choices_selected(c("ARM", "ACTARM"), "ARM"),
|
|
| 98 |
#' color_manual = c( |
|
| 99 |
#' "150mg QD" = "#000000", |
|
| 100 |
#' "Placebo" = "#3498DB", |
|
| 101 |
#' "Combination" = "#E74C3C" |
|
| 102 |
#' ), |
|
| 103 |
#' color_comb = "#39ff14", |
|
| 104 |
#' comb_line = TRUE, |
|
| 105 |
#' plot_height = c(500, 200, 2000), |
|
| 106 |
#' font_size = c(12, 8, 20), |
|
| 107 |
#' line_size = c(1, .25, 3), |
|
| 108 |
#' hline_arb = c(.02, .05), |
|
| 109 |
#' hline_arb_color = c("red", "black"),
|
|
| 110 |
#' hline_arb_label = c("Horizontal Line A", "Horizontal Line B")
|
|
| 111 |
#' ) |
|
| 112 |
#' ) |
|
| 113 |
#' ) |
|
| 114 |
#' if (interactive()) {
|
|
| 115 |
#' shinyApp(app$ui, app$server) |
|
| 116 |
#' } |
|
| 117 |
#' |
|
| 118 |
tm_g_gh_density_distribution_plot <- function(label, # nolint |
|
| 119 |
dataname, |
|
| 120 |
param_var, |
|
| 121 |
param, |
|
| 122 |
xaxis_var, |
|
| 123 |
trt_group, |
|
| 124 |
color_manual = NULL, |
|
| 125 |
color_comb = NULL, |
|
| 126 |
plot_height = c(500, 200, 2000), |
|
| 127 |
plot_width = NULL, |
|
| 128 |
font_size = c(12, 8, 20), |
|
| 129 |
line_size = c(1, .25, 3), |
|
| 130 |
hline_arb = numeric(0), |
|
| 131 |
hline_arb_color = "red", |
|
| 132 |
hline_arb_label = "Horizontal line", |
|
| 133 |
facet_ncol = 2L, |
|
| 134 |
comb_line = TRUE, |
|
| 135 |
rotate_xlab = FALSE, |
|
| 136 |
pre_output = NULL, |
|
| 137 |
post_output = NULL, |
|
| 138 |
transformators = list()) {
|
|
| 139 | ! |
message("Initializing tm_g_gh_density_distribution_plot")
|
| 140 | ! |
checkmate::assert_string(label) |
| 141 | ! |
checkmate::assert_string(dataname) |
| 142 | ! |
checkmate::assert_string(param_var) |
| 143 | ! |
checkmate::assert_class(param, "choices_selected") |
| 144 | ! |
checkmate::assert_class(xaxis_var, "choices_selected") |
| 145 | ! |
checkmate::assert_class(trt_group, "choices_selected") |
| 146 | ! |
checkmate::assert_numeric(font_size, len = 3) |
| 147 | ! |
checkmate::assert_numeric(line_size, len = 3) |
| 148 | ! |
checkmate::assert_int(facet_ncol) |
| 149 | ! |
checkmate::assert_flag(comb_line) |
| 150 | ! |
checkmate::assert_flag(rotate_xlab) |
| 151 | ! |
validate_line_arb_arg(hline_arb, hline_arb_color, hline_arb_label) |
| 152 | ! |
checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) |
| 153 | ! |
checkmate::assert_numeric(plot_height[1], lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height") |
| 154 | ! |
checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) |
| 155 | ! |
checkmate::assert_numeric( |
| 156 | ! |
plot_width[1], |
| 157 | ! |
lower = plot_width[2], upper = plot_width[3], null.ok = TRUE, .var.name = "plot_width" |
| 158 |
) |
|
| 159 | ||
| 160 | ! |
args <- as.list(environment()) |
| 161 | ||
| 162 | ! |
module( |
| 163 | ! |
label = label, |
| 164 | ! |
datanames = dataname, |
| 165 | ! |
server = srv_g_density_distribution_plot, |
| 166 | ! |
server_args = list( |
| 167 | ! |
dataname = dataname, |
| 168 | ! |
param_var = param_var, |
| 169 | ! |
param = param, |
| 170 | ! |
color_manual = color_manual, |
| 171 | ! |
color_comb = color_comb, |
| 172 | ! |
plot_height = plot_height, |
| 173 | ! |
plot_width = plot_width, |
| 174 | ! |
module_args = args |
| 175 |
), |
|
| 176 | ! |
ui = ui_g_density_distribution_plot, |
| 177 | ! |
ui_args = args, |
| 178 | ! |
transformators = transformators |
| 179 |
) |
|
| 180 |
} |
|
| 181 | ||
| 182 |
ui_g_density_distribution_plot <- function(id, ...) {
|
|
| 183 | ! |
ns <- NS(id) |
| 184 | ! |
a <- list(...) |
| 185 | ||
| 186 | ! |
teal.widgets::standard_layout( |
| 187 | ! |
output = bslib::page_fluid( |
| 188 | ! |
tags$div( |
| 189 | ! |
teal.widgets::plot_with_settings_ui(id = ns("plot"))
|
| 190 |
), |
|
| 191 | ! |
tags$div( |
| 192 | ! |
tags$br(), tags$hr(), |
| 193 | ! |
tags$h4("Descriptive Statistics"),
|
| 194 | ! |
DT::dataTableOutput(ns("table_ui"))
|
| 195 |
) |
|
| 196 |
), |
|
| 197 | ! |
encoding = tags$div( |
| 198 |
### Reporter |
|
| 199 | ! |
teal.reporter::add_card_button_ui(ns("add_reporter"), label = "Add Report Card"),
|
| 200 | ! |
tags$br(), tags$br(), |
| 201 |
### |
|
| 202 | ! |
templ_ui_dataname(a$dataname), |
| 203 | ! |
uiOutput(ns("axis_selections")),
|
| 204 | ! |
templ_ui_constraint(ns, label = "Data Constraint"), |
| 205 | ! |
ui_arbitrary_lines(id = ns("hline_arb"), a$hline_arb, a$hline_arb_label, a$hline_arb_color),
|
| 206 | ! |
bslib::accordion( |
| 207 | ! |
bslib::accordion_panel( |
| 208 | ! |
title = "Plot Aesthetic Settings", |
| 209 | ! |
toggle_slider_ui( |
| 210 | ! |
ns("xrange_scale"),
|
| 211 | ! |
label = "X-Axis Range Zoom" |
| 212 |
), |
|
| 213 | ! |
toggle_slider_ui( |
| 214 | ! |
ns("yrange_scale"),
|
| 215 | ! |
label = "Y-Axis Range Zoom" |
| 216 |
), |
|
| 217 | ! |
numericInput(ns("facet_ncol"), "Number of Plots Per Row:", a$facet_ncol, min = 1),
|
| 218 | ! |
checkboxInput(ns("comb_line"), "Display Combined line", a$comb_line),
|
| 219 | ! |
checkboxInput(ns("rug_plot"), "Include rug plot", value = FALSE),
|
| 220 | ! |
checkboxInput(ns("rotate_xlab"), "Rotate X-axis Label", a$rotate_xlab)
|
| 221 |
), |
|
| 222 | ! |
bslib::accordion_panel( |
| 223 | ! |
title = "Plot settings", |
| 224 | ! |
teal.widgets::optionalSliderInputValMinMax(ns("font_size"), "Font Size", a$font_size, ticks = FALSE),
|
| 225 | ! |
teal.widgets::optionalSliderInputValMinMax( |
| 226 | ! |
ns("line_size"),
|
| 227 | ! |
"Line Size", |
| 228 | ! |
value_min_max = a$line_size, |
| 229 | ! |
step = .25, |
| 230 | ! |
ticks = FALSE |
| 231 |
) |
|
| 232 |
) |
|
| 233 |
) |
|
| 234 |
), |
|
| 235 | ! |
forms = tagList( |
| 236 | ! |
teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code")
|
| 237 |
), |
|
| 238 | ! |
pre_output = a$pre_output, |
| 239 | ! |
post_output = a$post_output |
| 240 |
) |
|
| 241 |
} |
|
| 242 | ||
| 243 |
srv_g_density_distribution_plot <- function(id, # nolint |
|
| 244 |
data, |
|
| 245 |
reporter, |
|
| 246 |
filter_panel_api, |
|
| 247 |
dataname, |
|
| 248 |
param_var, |
|
| 249 |
param, |
|
| 250 |
trt_group, |
|
| 251 |
color_manual, |
|
| 252 |
color_comb, |
|
| 253 |
plot_height, |
|
| 254 |
plot_width, |
|
| 255 |
module_args) {
|
|
| 256 | ! |
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter") |
| 257 | ! |
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI") |
| 258 | ! |
checkmate::assert_class(data, "reactive") |
| 259 | ! |
checkmate::assert_class(shiny::isolate(data()), "teal_data") |
| 260 | ||
| 261 | ! |
moduleServer(id, function(input, output, session) {
|
| 262 | ! |
teal.logger::log_shiny_input_changes(input, namespace = "teal.goshawk") |
| 263 | ! |
output$axis_selections <- renderUI({
|
| 264 | ! |
env <- shiny::isolate(as.list(data()[[".raw_data"]])) |
| 265 | ! |
resolved_x <- teal.transform::resolve_delayed(module_args$xaxis_var, env) |
| 266 | ! |
resolved_param <- teal.transform::resolve_delayed(module_args$param, env) |
| 267 | ! |
resolved_trt <- teal.transform::resolve_delayed(module_args$trt_group, env) |
| 268 | ||
| 269 | ! |
templ_ui_params_vars( |
| 270 | ! |
session$ns, |
| 271 | ! |
xparam_choices = resolved_param$choices, |
| 272 | ! |
xparam_selected = resolved_param$selected, |
| 273 | ! |
xparam_label = "Select a Biomarker", |
| 274 | ! |
xchoices = resolved_x$choices, |
| 275 | ! |
xselected = resolved_x$selected, |
| 276 | ! |
trt_choices = resolved_trt$choices, |
| 277 | ! |
trt_selected = resolved_trt$selected |
| 278 |
) |
|
| 279 |
}) |
|
| 280 | ||
| 281 | ! |
anl_q_output <- constr_anl_q( |
| 282 | ! |
session, input, data, dataname, |
| 283 | ! |
param_id = "xaxis_param", param_var = param_var, trt_group = input$trt_group, min_rows = 2 |
| 284 |
) |
|
| 285 | ||
| 286 | ! |
anl_q <- anl_q_output()$value |
| 287 | ||
| 288 |
# update sliders for axes taking constraints into account |
|
| 289 | ! |
data_state_x <- reactive({
|
| 290 | ! |
get_data_range_states( |
| 291 | ! |
varname = input$xaxis_var, |
| 292 | ! |
paramname = input$xaxis_param, |
| 293 | ! |
ANL = anl_q()$ANL |
| 294 |
) |
|
| 295 |
}) |
|
| 296 | ! |
xrange_slider <- toggle_slider_server("xrange_scale", data_state_x)
|
| 297 | ! |
data_state_y <- reactive({
|
| 298 | ! |
get_data_range_states( |
| 299 | ! |
varname = input$xaxis_var, |
| 300 | ! |
paramname = input$xaxis_param, |
| 301 | ! |
ANL = anl_q()$ANL, |
| 302 | ! |
trt_group = "trt_group" |
| 303 |
) |
|
| 304 |
}) |
|
| 305 | ! |
yrange_slider <- toggle_slider_server("yrange_scale", data_state_y)
|
| 306 | ||
| 307 | ! |
keep_data_const_opts_updated(session, input, anl_q, "xaxis_param") |
| 308 | ||
| 309 | ! |
horizontal_line <- srv_arbitrary_lines("hline_arb")
|
| 310 | ||
| 311 | ! |
iv_r <- reactive({
|
| 312 | ! |
iv <- shinyvalidate::InputValidator$new() |
| 313 | ||
| 314 | ! |
iv$add_rule("xaxis_param", shinyvalidate::sv_required("Please select a biomarker"))
|
| 315 | ! |
iv$add_rule("trt_group", shinyvalidate::sv_required("Please select a treatment variable"))
|
| 316 | ! |
iv$add_rule("xaxis_var", shinyvalidate::sv_required("Please select an X-Axis variable"))
|
| 317 | ! |
iv$add_rule("facet_ncol", plots_per_row_validate_rules())
|
| 318 | ||
| 319 | ! |
iv$add_validator(horizontal_line()$iv_r()) |
| 320 | ! |
iv$add_validator(anl_q_output()$iv_r()) |
| 321 | ! |
iv$enable() |
| 322 | ! |
iv |
| 323 |
}) |
|
| 324 | ||
| 325 | ||
| 326 | ! |
create_plot <- debounce(reactive({
|
| 327 | ! |
teal::validate_inputs(iv_r()) |
| 328 | ! |
req(anl_q()) |
| 329 | ||
| 330 |
# nolint start |
|
| 331 | ! |
param <- input$xaxis_param |
| 332 | ! |
xaxis_var <- input$xaxis_var |
| 333 | ! |
xlim <- xrange_slider$value |
| 334 | ! |
ylim <- yrange_slider$value |
| 335 | ! |
font_size <- input$font_size |
| 336 | ! |
line_size <- input$line_size |
| 337 | ! |
hline_arb <- horizontal_line()$line_arb |
| 338 | ! |
hline_arb_label <- horizontal_line()$line_arb_label |
| 339 | ! |
hline_arb_color <- horizontal_line()$line_arb_color |
| 340 | ! |
facet_ncol <- input$facet_ncol |
| 341 | ||
| 342 | ! |
comb_line <- input$comb_line |
| 343 | ! |
rug_plot <- input$rug_plot |
| 344 | ! |
rotate_xlab <- input$rotate_xlab |
| 345 | ! |
trt_group <- input$trt_group |
| 346 |
# nolint end |
|
| 347 | ||
| 348 | ||
| 349 | ! |
teal.code::eval_code( |
| 350 | ! |
object = anl_q()$qenv, |
| 351 | ! |
code = bquote({
|
| 352 | ! |
p <- goshawk::g_density_distribution_plot( |
| 353 | ! |
data = ANL, |
| 354 | ! |
param_var = .(param_var), |
| 355 | ! |
param = .(param), |
| 356 | ! |
xaxis_var = .(xaxis_var), |
| 357 | ! |
trt_group = .(trt_group), |
| 358 | ! |
xlim = .(xlim), |
| 359 | ! |
ylim = .(ylim), |
| 360 | ! |
color_manual = .(color_manual), |
| 361 | ! |
color_comb = .(color_comb), |
| 362 | ! |
font_size = .(font_size), |
| 363 | ! |
line_size = .(line_size), |
| 364 | ! |
facet_ncol = .(facet_ncol), |
| 365 | ! |
comb_line = .(comb_line), |
| 366 | ! |
hline_arb = .(hline_arb), |
| 367 | ! |
hline_arb_label = .(hline_arb_label), |
| 368 | ! |
hline_arb_color = .(hline_arb_color), |
| 369 | ! |
rug_plot = .(rug_plot) |
| 370 |
) |
|
| 371 | ! |
p |
| 372 |
}) |
|
| 373 |
) |
|
| 374 | ! |
}), 800) |
| 375 | ||
| 376 | ! |
create_table <- debounce(reactive({
|
| 377 | ! |
req(iv_r()$is_valid()) |
| 378 | ! |
req(anl_q()) |
| 379 | ! |
param <- input$xaxis_param |
| 380 | ! |
xaxis_var <- input$xaxis_var |
| 381 | ! |
font_size <- input$font_size |
| 382 | ! |
trt_group <- input$trt_group |
| 383 | ||
| 384 | ! |
teal.code::eval_code( |
| 385 | ! |
object = anl_q()$qenv, |
| 386 | ! |
code = bquote({
|
| 387 | ! |
tbl <- goshawk::t_summarytable( |
| 388 | ! |
data = ANL, |
| 389 | ! |
trt_group = .(trt_group), |
| 390 | ! |
param_var = .(param_var), |
| 391 | ! |
param = .(param), |
| 392 | ! |
xaxis_var = .(xaxis_var), |
| 393 | ! |
font_size = .(font_size) |
| 394 |
) |
|
| 395 | ! |
tbl |
| 396 |
}) |
|
| 397 |
) |
|
| 398 | ! |
}), 800) |
| 399 | ||
| 400 | ! |
plot_r <- reactive({
|
| 401 | ! |
create_plot()[["p"]] |
| 402 |
}) |
|
| 403 | ||
| 404 | ! |
plot_data <- teal.widgets::plot_with_settings_srv( |
| 405 | ! |
id = "plot", |
| 406 | ! |
plot_r = plot_r, |
| 407 | ! |
height = plot_height, |
| 408 | ! |
width = plot_width, |
| 409 |
) |
|
| 410 | ||
| 411 | ! |
output$table_ui <- DT::renderDataTable({
|
| 412 | ! |
req(create_table()) |
| 413 | ! |
tbl <- create_table()[["tbl"]] |
| 414 | ! |
numeric_cols <- names(dplyr::select_if(tbl, is.numeric)) |
| 415 | ||
| 416 | ! |
DT::datatable(tbl, |
| 417 | ! |
rownames = FALSE, options = list(scrollX = TRUE) |
| 418 |
) %>% |
|
| 419 | ! |
DT::formatRound(numeric_cols, 2) |
| 420 |
}) |
|
| 421 | ||
| 422 | ! |
joined_qenvs <- reactive({
|
| 423 | ! |
req(create_plot(), create_table()) |
| 424 | ! |
c(create_plot(), create_table()) |
| 425 |
}) |
|
| 426 | ||
| 427 | ! |
code <- reactive(teal.code::get_code(joined_qenvs())) |
| 428 | ||
| 429 | ! |
teal.widgets::verbatim_popup_srv( |
| 430 | ! |
id = "rcode", |
| 431 | ! |
verbatim_content = reactive(code()), |
| 432 | ! |
title = "Show R Code for Density Distribution Plot" |
| 433 |
) |
|
| 434 | ||
| 435 |
### REPORTER |
|
| 436 | ! |
if (with_reporter) {
|
| 437 | ! |
card_fun <- function(comment, label) {
|
| 438 | ! |
card <- report_card_template_goshawk( |
| 439 | ! |
title = "Density Distribution Plot", |
| 440 | ! |
label = label, |
| 441 | ! |
with_filter = with_filter, |
| 442 | ! |
filter_panel_api = filter_panel_api, |
| 443 | ! |
constraint_list = list( |
| 444 | ! |
constraint_var = input$constraint_var, |
| 445 | ! |
constraint_range_min = input$constraint_range_min, |
| 446 | ! |
constraint_range_max = input$constraint_range_max |
| 447 |
) |
|
| 448 |
) |
|
| 449 | ! |
card$append_text("Plot", "header3")
|
| 450 | ! |
card$append_plot(plot_r(), dim = plot_data$dim()) |
| 451 | ! |
card$append_text("Descriptive Statistics", "header3")
|
| 452 | ! |
card$append_table( |
| 453 | ! |
create_table()[["tbl"]] %>% dplyr::mutate_if(is.numeric, round, 2) |
| 454 |
) |
|
| 455 | ! |
if (!comment == "") {
|
| 456 | ! |
card$append_text("Comment", "header3")
|
| 457 | ! |
card$append_text(comment) |
| 458 |
} |
|
| 459 | ! |
card$append_src(code()) |
| 460 | ! |
card |
| 461 |
} |
|
| 462 | ! |
teal.reporter::add_card_button_srv("add_reporter", reporter = reporter, card_fun = card_fun)
|
| 463 |
} |
|
| 464 |
### |
|
| 465 |
}) |
|
| 466 |
} |
| 1 |
#' Scatter Plot Teal Module For Biomarker Analysis |
|
| 2 |
#' |
|
| 3 |
#' @description Scatter Plot Teal Module For Biomarker Analysis |
|
| 4 |
#' |
|
| 5 |
#' @inheritParams teal.widgets::standard_layout |
|
| 6 |
#' @inheritParams teal::module |
|
| 7 |
#' @param label menu item label of the module in the teal app. |
|
| 8 |
#' @param dataname analysis data passed to the data argument of \code{\link[teal]{init}}. E.g. `ADaM` structured
|
|
| 9 |
#' laboratory data frame \code{ADLB}.
|
|
| 10 |
#' @param param_var name of variable containing biomarker codes e.g. \code{PARAMCD}.
|
|
| 11 |
#' @param xaxis_param biomarker selected for `x-axis`. |
|
| 12 |
#' @param yaxis_param biomarker selected for `y-axis`. |
|
| 13 |
#' @param xaxis_var name of variable containing biomarker results displayed on x-axis e.g. \code{BASE}.
|
|
| 14 |
#' @param yaxis_var name of variable containing biomarker results displayed on y-axis e.g. \code{AVAL}.
|
|
| 15 |
#' @param trt_group \code{\link[teal.transform]{choices_selected}} object with available choices and pre-selected option
|
|
| 16 |
#' for variable names representing treatment group e.g. `ARM`. |
|
| 17 |
#' @param color_manual vector of colors applied to treatment values. |
|
| 18 |
#' @param shape_manual vector of symbols applied to `LOQ` values. |
|
| 19 |
#' @param facet_ncol numeric value indicating number of facets per row. |
|
| 20 |
#' @param trt_facet facet by treatment group \code{trt_group}.
|
|
| 21 |
#' @param visit_facet visit facet toggle. |
|
| 22 |
#' @param reg_line include regression line and annotations for slope and coefficient in visualization. Use with facet |
|
| 23 |
#' TRUE. |
|
| 24 |
#' @param loq_legend `loq` legend toggle. |
|
| 25 |
#' @param rotate_xlab 45 degree rotation of `x-axis` values. |
|
| 26 |
#' @param hline_arb numeric vector of at most 2 values identifying intercepts for arbitrary horizontal lines. |
|
| 27 |
#' @param hline_arb_color a character vector of at most length of \code{hline_arb}.
|
|
| 28 |
#' naming the color for the arbitrary horizontal lines. |
|
| 29 |
#' @param hline_arb_label a character vector of at most length of \code{hline_arb}.
|
|
| 30 |
#' naming the label for the arbitrary horizontal lines. |
|
| 31 |
#' @param hline_vars a character vector to name the columns that will define additional horizontal lines. |
|
| 32 |
#' @param hline_vars_colors a character vector naming the colors for the additional horizontal lines. |
|
| 33 |
#' @param hline_vars_labels a character vector naming the labels for the additional horizontal lines that will appear |
|
| 34 |
#' @param vline_arb numeric vector of at most 2 values identifying intercepts for arbitrary horizontal lines. |
|
| 35 |
#' @param vline_arb_color a character vector of at most length of \code{vline_arb}.
|
|
| 36 |
#' naming the color for the arbitrary horizontal lines. |
|
| 37 |
#' @param vline_arb_label a character vector of at most length of \code{vline_arb}.
|
|
| 38 |
#' naming the label for the arbitrary horizontal lines. |
|
| 39 |
#' @param vline_vars a character vector to name the columns that will define additional vertical lines. |
|
| 40 |
#' @param vline_vars_colors a character vector naming the colors for the additional vertical lines. |
|
| 41 |
#' @param vline_vars_labels a character vector naming the labels for the additional vertical lines that will appear |
|
| 42 |
#' @param plot_height controls plot height. |
|
| 43 |
#' @param plot_width optional, controls plot width. |
|
| 44 |
#' @param font_size font size control for title, `x-axis` label, `y-axis` label and legend. |
|
| 45 |
#' @param dot_size plot dot size. |
|
| 46 |
#' @param reg_text_size font size control for regression line annotations. |
|
| 47 |
#' |
|
| 48 |
#' @export |
|
| 49 |
#' |
|
| 50 |
#' @author Nick Paszty (npaszty) paszty.nicholas@gene.com |
|
| 51 |
#' @author Balazs Toth (tothb2) toth.balazs@gene.com |
|
| 52 |
#' |
|
| 53 |
#' @examples |
|
| 54 |
#' # Example using ADaM structure analysis dataset. |
|
| 55 |
#' data <- teal_data() |
|
| 56 |
#' data <- within(data, {
|
|
| 57 |
#' library(dplyr) |
|
| 58 |
#' library(stringr) |
|
| 59 |
#' |
|
| 60 |
#' # use non-exported function from goshawk |
|
| 61 |
#' .h_identify_loq_values <- getFromNamespace("h_identify_loq_values", "goshawk")
|
|
| 62 |
#' |
|
| 63 |
#' # original ARM value = dose value |
|
| 64 |
#' .arm_mapping <- list( |
|
| 65 |
#' "A: Drug X" = "150mg QD", |
|
| 66 |
#' "B: Placebo" = "Placebo", |
|
| 67 |
#' "C: Combination" = "Combination" |
|
| 68 |
#' ) |
|
| 69 |
#' .color_manual <- c("150mg QD" = "#000000", "Placebo" = "#3498DB", "Combination" = "#E74C3C")
|
|
| 70 |
#' # assign LOQ flag symbols: circles for "N" and triangles for "Y", squares for "NA" |
|
| 71 |
#' .shape_manual <- c("N" = 1, "Y" = 2, "NA" = 0)
|
|
| 72 |
#' |
|
| 73 |
#' set.seed(1) # @linksto ADSL ADLB |
|
| 74 |
#' ADSL <- rADSL |
|
| 75 |
#' ADLB <- rADLB |
|
| 76 |
#' .var_labels <- lapply(ADLB, function(x) attributes(x)$label) |
|
| 77 |
#' ADLB <- ADLB %>% |
|
| 78 |
#' mutate(AVISITCD = case_when( |
|
| 79 |
#' AVISIT == "SCREENING" ~ "SCR", |
|
| 80 |
#' AVISIT == "BASELINE" ~ "BL", |
|
| 81 |
#' grepl("WEEK", AVISIT) ~
|
|
| 82 |
#' paste( |
|
| 83 |
#' "W", |
|
| 84 |
#' trimws( |
|
| 85 |
#' substr( |
|
| 86 |
#' AVISIT, |
|
| 87 |
#' start = 6, |
|
| 88 |
#' stop = str_locate(AVISIT, "DAY") - 1 |
|
| 89 |
#' ) |
|
| 90 |
#' ) |
|
| 91 |
#' ), |
|
| 92 |
#' TRUE ~ NA_character_ |
|
| 93 |
#' )) %>% |
|
| 94 |
#' mutate(AVISITCDN = case_when( |
|
| 95 |
#' AVISITCD == "SCR" ~ -2, |
|
| 96 |
#' AVISITCD == "BL" ~ 0, |
|
| 97 |
#' grepl("W", AVISITCD) ~ as.numeric(gsub("[^0-9]*", "", AVISITCD)),
|
|
| 98 |
#' TRUE ~ NA_real_ |
|
| 99 |
#' )) %>% |
|
| 100 |
#' # use ARMCD values to order treatment in visualization legend |
|
| 101 |
#' mutate(TRTORD = ifelse(grepl("C", ARMCD), 1,
|
|
| 102 |
#' ifelse(grepl("B", ARMCD), 2,
|
|
| 103 |
#' ifelse(grepl("A", ARMCD), 3, NA)
|
|
| 104 |
#' ) |
|
| 105 |
#' )) %>% |
|
| 106 |
#' mutate(ARM = as.character(.arm_mapping[match(ARM, names(.arm_mapping))])) %>% |
|
| 107 |
#' mutate(ARM = factor(ARM) %>% |
|
| 108 |
#' reorder(TRTORD)) %>% |
|
| 109 |
#' mutate( |
|
| 110 |
#' ANRHI = case_when( |
|
| 111 |
#' PARAMCD == "ALT" ~ 60, |
|
| 112 |
#' PARAMCD == "CRP" ~ 70, |
|
| 113 |
#' PARAMCD == "IGA" ~ 80, |
|
| 114 |
#' TRUE ~ NA_real_ |
|
| 115 |
#' ), |
|
| 116 |
#' ANRLO = case_when( |
|
| 117 |
#' PARAMCD == "ALT" ~ 20, |
|
| 118 |
#' PARAMCD == "CRP" ~ 30, |
|
| 119 |
#' PARAMCD == "IGA" ~ 40, |
|
| 120 |
#' TRUE ~ NA_real_ |
|
| 121 |
#' ) |
|
| 122 |
#' ) %>% |
|
| 123 |
#' rowwise() %>% |
|
| 124 |
#' group_by(PARAMCD) %>% |
|
| 125 |
#' mutate(LBSTRESC = ifelse( |
|
| 126 |
#' USUBJID %in% sample(USUBJID, 1, replace = TRUE), |
|
| 127 |
#' paste("<", round(runif(1, min = 25, max = 30))), LBSTRESC
|
|
| 128 |
#' )) %>% |
|
| 129 |
#' mutate(LBSTRESC = ifelse( |
|
| 130 |
#' USUBJID %in% sample(USUBJID, 1, replace = TRUE), |
|
| 131 |
#' paste(">", round(runif(1, min = 70, max = 75))), LBSTRESC
|
|
| 132 |
#' )) %>% |
|
| 133 |
#' ungroup() |
|
| 134 |
#' attr(ADLB[["ARM"]], "label") <- .var_labels[["ARM"]] |
|
| 135 |
#' attr(ADLB[["ANRHI"]], "label") <- "Analysis Normal Range Upper Limit" |
|
| 136 |
#' attr(ADLB[["ANRLO"]], "label") <- "Analysis Normal Range Lower Limit" |
|
| 137 |
#' |
|
| 138 |
#' # add LLOQ and ULOQ variables |
|
| 139 |
#' ADLB_LOQS <- .h_identify_loq_values(ADLB, "LOQFL") |
|
| 140 |
#' ADLB <- left_join(ADLB, ADLB_LOQS, by = "PARAM") |
|
| 141 |
#' }) |
|
| 142 |
#' |
|
| 143 |
#' join_keys(data) <- default_cdisc_join_keys[names(data)] |
|
| 144 |
#' |
|
| 145 |
#' app <- init( |
|
| 146 |
#' data = data, |
|
| 147 |
#' modules = modules( |
|
| 148 |
#' tm_g_gh_correlationplot( |
|
| 149 |
#' label = "Correlation Plot", |
|
| 150 |
#' dataname = "ADLB", |
|
| 151 |
#' param_var = "PARAMCD", |
|
| 152 |
#' xaxis_param = choices_selected(c("ALT", "CRP", "IGA"), "ALT"),
|
|
| 153 |
#' yaxis_param = choices_selected(c("ALT", "CRP", "IGA"), "CRP"),
|
|
| 154 |
#' xaxis_var = choices_selected(c("AVAL", "BASE", "CHG", "PCHG"), "BASE"),
|
|
| 155 |
#' yaxis_var = choices_selected(c("AVAL", "BASE", "CHG", "PCHG"), "AVAL"),
|
|
| 156 |
#' trt_group = choices_selected(c("ARM", "ACTARM"), "ARM"),
|
|
| 157 |
#' color_manual = c( |
|
| 158 |
#' "Drug X 100mg" = "#000000", |
|
| 159 |
#' "Placebo" = "#3498DB", |
|
| 160 |
#' "Combination 100mg" = "#E74C3C" |
|
| 161 |
#' ), |
|
| 162 |
#' shape_manual = c("N" = 1, "Y" = 2, "NA" = 0),
|
|
| 163 |
#' plot_height = c(500, 200, 2000), |
|
| 164 |
#' facet_ncol = 2, |
|
| 165 |
#' visit_facet = TRUE, |
|
| 166 |
#' reg_line = FALSE, |
|
| 167 |
#' loq_legend = TRUE, |
|
| 168 |
#' font_size = c(12, 8, 20), |
|
| 169 |
#' dot_size = c(1, 1, 12), |
|
| 170 |
#' reg_text_size = c(3, 3, 10), |
|
| 171 |
#' hline_arb = c(40, 50), |
|
| 172 |
#' hline_arb_label = "arb hori label", |
|
| 173 |
#' hline_arb_color = c("red", "blue"),
|
|
| 174 |
#' hline_vars = c("ANRHI", "ANRLO", "ULOQN", "LLOQN"),
|
|
| 175 |
#' hline_vars_colors = c("green", "blue", "purple", "cyan"),
|
|
| 176 |
#' hline_vars_labels = c("ANRHI Label", "ANRLO Label", "ULOQN Label", "LLOQN Label"),
|
|
| 177 |
#' vline_vars = c("ANRHI", "ANRLO", "ULOQN", "LLOQN"),
|
|
| 178 |
#' vline_vars_colors = c("yellow", "orange", "brown", "gold"),
|
|
| 179 |
#' vline_vars_labels = c("ANRHI Label", "ANRLO Label", "ULOQN Label", "LLOQN Label"),
|
|
| 180 |
#' vline_arb = c(50, 70), |
|
| 181 |
#' vline_arb_label = "arb vert A", |
|
| 182 |
#' vline_arb_color = c("green", "orange")
|
|
| 183 |
#' ) |
|
| 184 |
#' ) |
|
| 185 |
#' ) |
|
| 186 |
#' if (interactive()) {
|
|
| 187 |
#' shinyApp(app$ui, app$server) |
|
| 188 |
#' } |
|
| 189 |
#' |
|
| 190 |
tm_g_gh_correlationplot <- function(label, |
|
| 191 |
dataname, |
|
| 192 |
param_var = "PARAMCD", |
|
| 193 |
xaxis_param = "ALT", |
|
| 194 |
xaxis_var = "BASE", |
|
| 195 |
yaxis_param = "CRP", |
|
| 196 |
yaxis_var = "AVAL", |
|
| 197 |
trt_group, |
|
| 198 |
color_manual = NULL, |
|
| 199 |
shape_manual = NULL, |
|
| 200 |
facet_ncol = 2, |
|
| 201 |
visit_facet = TRUE, |
|
| 202 |
trt_facet = FALSE, |
|
| 203 |
reg_line = FALSE, |
|
| 204 |
loq_legend = TRUE, |
|
| 205 |
rotate_xlab = FALSE, |
|
| 206 |
hline_arb = numeric(0), |
|
| 207 |
hline_arb_color = "red", |
|
| 208 |
hline_arb_label = "Horizontal line", |
|
| 209 |
hline_vars = character(0), |
|
| 210 |
hline_vars_colors = "green", |
|
| 211 |
hline_vars_labels = hline_vars, |
|
| 212 |
vline_arb = numeric(0), |
|
| 213 |
vline_arb_color = "red", |
|
| 214 |
vline_arb_label = "Vertical line", |
|
| 215 |
vline_vars = character(0), |
|
| 216 |
vline_vars_colors = "green", |
|
| 217 |
vline_vars_labels = vline_vars, |
|
| 218 |
plot_height = c(500, 200, 2000), |
|
| 219 |
plot_width = NULL, |
|
| 220 |
font_size = c(12, 8, 20), |
|
| 221 |
dot_size = c(1, 1, 12), |
|
| 222 |
reg_text_size = c(3, 3, 10), |
|
| 223 |
pre_output = NULL, |
|
| 224 |
post_output = NULL, |
|
| 225 |
transformators = list()) {
|
|
| 226 | ! |
message("Initializing tm_g_gh_correlationplot")
|
| 227 | ! |
checkmate::assert_class(xaxis_param, "choices_selected") |
| 228 | ! |
checkmate::assert_class(yaxis_param, "choices_selected") |
| 229 | ! |
checkmate::assert_class(xaxis_var, "choices_selected") |
| 230 | ! |
checkmate::assert_class(yaxis_var, "choices_selected") |
| 231 | ! |
checkmate::assert_class(trt_group, "choices_selected") |
| 232 | ! |
checkmate::assert_flag(trt_facet) |
| 233 | ! |
validate_line_arb_arg(hline_arb, hline_arb_color, hline_arb_label) |
| 234 | ! |
validate_line_arb_arg(vline_arb, vline_arb_color, vline_arb_label) |
| 235 | ! |
validate_line_vars_arg(hline_vars, hline_vars_colors, hline_vars_labels) |
| 236 | ! |
validate_line_vars_arg(vline_vars, vline_vars_colors, vline_vars_labels) |
| 237 | ! |
checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) |
| 238 | ! |
checkmate::assert_numeric(plot_height[1], lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height") |
| 239 | ! |
checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) |
| 240 | ! |
checkmate::assert_numeric( |
| 241 | ! |
plot_width[1], |
| 242 | ! |
lower = plot_width[2], upper = plot_width[3], null.ok = TRUE, .var.name = "plot_width" |
| 243 |
) |
|
| 244 | ! |
checkmate::assert_numeric(font_size, len = 3) |
| 245 | ! |
checkmate::assert_numeric(dot_size, len = 3) |
| 246 | ! |
checkmate::assert_numeric(reg_text_size, len = 3) |
| 247 | ||
| 248 | ! |
args <- as.list(environment()) |
| 249 | ||
| 250 | ! |
module( |
| 251 | ! |
label = label, |
| 252 | ! |
datanames = dataname, |
| 253 | ! |
server = srv_g_correlationplot, |
| 254 | ! |
server_args = list( |
| 255 | ! |
dataname = dataname, |
| 256 | ! |
param_var = param_var, |
| 257 | ! |
trt_facet = trt_facet, |
| 258 | ! |
color_manual = color_manual, |
| 259 | ! |
shape_manual = shape_manual, |
| 260 | ! |
plot_height = plot_height, |
| 261 | ! |
plot_width = plot_width, |
| 262 | ! |
hline_vars_colors = hline_vars_colors, |
| 263 | ! |
hline_vars_labels = hline_vars_labels, |
| 264 | ! |
vline_vars_colors = vline_vars_colors, |
| 265 | ! |
vline_vars_labels = vline_vars_labels, |
| 266 | ! |
module_args = args |
| 267 |
), |
|
| 268 | ! |
ui = ui_g_correlationplot, |
| 269 | ! |
ui_args = args, |
| 270 | ! |
transformators = transformators |
| 271 |
) |
|
| 272 |
} |
|
| 273 | ||
| 274 |
ui_g_correlationplot <- function(id, ...) {
|
|
| 275 | ! |
ns <- NS(id) |
| 276 | ! |
a <- list(...) |
| 277 | ||
| 278 | ! |
teal.widgets::standard_layout( |
| 279 | ! |
output = templ_ui_output_datatable(ns), |
| 280 | ! |
encoding = tags$div( |
| 281 |
### Reporter |
|
| 282 | ! |
teal.reporter::add_card_button_ui(ns("add_reporter"), label = "Add Report Card"),
|
| 283 | ! |
tags$br(), tags$br(), |
| 284 |
### |
|
| 285 | ! |
templ_ui_dataname(a$dataname), |
| 286 | ! |
uiOutput(ns("axis_selections")),
|
| 287 | ! |
templ_ui_constraint(ns, "X-Axis Data Constraint"), # required by constr_anl_q |
| 288 | ! |
if (length(a$hline_vars) > 0) {
|
| 289 | ! |
teal.widgets::optionalSelectInput( |
| 290 | ! |
ns("hline_vars"),
|
| 291 | ! |
label = "Add Horizontal Range Line(s):", |
| 292 | ! |
choices = a$hline_vars, |
| 293 | ! |
selected = NULL, |
| 294 | ! |
multiple = TRUE |
| 295 |
) |
|
| 296 |
}, |
|
| 297 | ! |
ui_arbitrary_lines(id = ns("hline_arb"), a$hline_arb, a$hline_arb_label, a$hline_arb_color),
|
| 298 | ! |
if (length(a$vline_vars) > 0) {
|
| 299 | ! |
teal.widgets::optionalSelectInput( |
| 300 | ! |
ns("vline_vars"),
|
| 301 | ! |
label = "Add Vertical Range Line(s):", |
| 302 | ! |
choices = a$vline_vars, |
| 303 | ! |
selected = NULL, |
| 304 | ! |
multiple = TRUE |
| 305 |
) |
|
| 306 |
}, |
|
| 307 | ! |
ui_arbitrary_lines( |
| 308 | ! |
id = ns("vline_arb"),
|
| 309 | ! |
a$vline_arb, |
| 310 | ! |
a$vline_arb_label, |
| 311 | ! |
a$vline_arb_color, |
| 312 | ! |
title = "Arbitrary Vertical Lines:" |
| 313 |
), |
|
| 314 | ! |
bslib::accordion( |
| 315 | ! |
bslib::accordion_panel( |
| 316 | ! |
title = "Plot Aesthetic Settings", |
| 317 | ! |
toggle_slider_ui( |
| 318 | ! |
ns("xrange_scale"),
|
| 319 | ! |
label = "X-Axis Range Zoom" |
| 320 |
), |
|
| 321 | ! |
toggle_slider_ui( |
| 322 | ! |
ns("yrange_scale"),
|
| 323 | ! |
label = "Y-Axis Range Zoom" |
| 324 |
), |
|
| 325 | ! |
numericInput(ns("facet_ncol"), "Number of Plots Per Row:", a$facet_ncol, min = 1),
|
| 326 | ! |
checkboxInput(ns("trt_facet"), "Treatment Variable Faceting", a$trt_facet),
|
| 327 | ! |
checkboxInput(ns("visit_facet"), "Visit Faceting", a$visit_facet),
|
| 328 | ! |
checkboxInput(ns("reg_line"), "Regression Line", a$reg_line),
|
| 329 | ! |
checkboxInput(ns("loq_legend"), "Display LoQ Legend", a$loq_legend),
|
| 330 | ! |
checkboxInput(ns("rotate_xlab"), "Rotate X-axis Label", a$rotate_xlab)
|
| 331 |
), |
|
| 332 | ! |
bslib::accordion_panel( |
| 333 | ! |
title = "Plot settings", |
| 334 | ! |
teal.widgets::optionalSliderInputValMinMax(ns("font_size"), "Font Size", a$font_size, ticks = FALSE),
|
| 335 | ! |
teal.widgets::optionalSliderInputValMinMax(ns("dot_size"), "Dot Size", a$dot_size, ticks = FALSE),
|
| 336 | ! |
teal.widgets::optionalSliderInputValMinMax( |
| 337 | ! |
ns("reg_text_size"),
|
| 338 | ! |
"Regression Annotations Size", |
| 339 | ! |
a$reg_text_size, |
| 340 | ! |
ticks = FALSE |
| 341 |
) |
|
| 342 |
) |
|
| 343 |
) |
|
| 344 |
), |
|
| 345 | ! |
forms = tagList( |
| 346 | ! |
teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code")
|
| 347 |
), |
|
| 348 | ! |
pre_output = a$pre_output, |
| 349 | ! |
post_output = a$post_output |
| 350 |
) |
|
| 351 |
} |
|
| 352 | ||
| 353 |
srv_g_correlationplot <- function(id, |
|
| 354 |
data, |
|
| 355 |
reporter, |
|
| 356 |
filter_panel_api, |
|
| 357 |
dataname, |
|
| 358 |
param_var, |
|
| 359 |
trt_group, |
|
| 360 |
trt_facet, |
|
| 361 |
color_manual, |
|
| 362 |
shape_manual, |
|
| 363 |
plot_height, |
|
| 364 |
plot_width, |
|
| 365 |
hline_vars_colors, |
|
| 366 |
hline_vars_labels, |
|
| 367 |
vline_vars_colors, |
|
| 368 |
vline_vars_labels, |
|
| 369 |
module_args) {
|
|
| 370 | ! |
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter") |
| 371 | ! |
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI") |
| 372 | ! |
checkmate::assert_class(data, "reactive") |
| 373 | ! |
checkmate::assert_class(shiny::isolate(data()), "teal_data") |
| 374 | ||
| 375 | ! |
moduleServer(id, function(input, output, session) {
|
| 376 | ! |
teal.logger::log_shiny_input_changes(input, namespace = "teal.goshawk") |
| 377 | ! |
output$axis_selections <- renderUI({
|
| 378 | ! |
env <- shiny::isolate(as.list(data()[[".raw_data"]])) |
| 379 | ! |
resolved_x_param <- teal.transform::resolve_delayed(module_args$xaxis_param, env) |
| 380 | ! |
resolved_x_var <- teal.transform::resolve_delayed(module_args$xaxis_var, env) |
| 381 | ! |
resolved_y_param <- teal.transform::resolve_delayed(module_args$yaxis_param, env) |
| 382 | ! |
resolved_y_var <- teal.transform::resolve_delayed(module_args$yaxis_var, env) |
| 383 | ! |
resolved_trt <- teal.transform::resolve_delayed(module_args$trt_group, env) |
| 384 | ! |
templ_ui_params_vars( |
| 385 | ! |
session$ns, |
| 386 | ! |
xparam_choices = resolved_x_param$choices, |
| 387 | ! |
xparam_selected = resolved_x_param$selected, |
| 388 | ! |
xchoices = resolved_x_var$choices, |
| 389 | ! |
xselected = resolved_x_var$selected, |
| 390 | ! |
yparam_choices = resolved_y_param$choices, |
| 391 | ! |
yparam_selected = resolved_y_param$selected, |
| 392 | ! |
ychoices = resolved_y_var$choices, |
| 393 | ! |
yselected = resolved_y_var$selected, |
| 394 | ! |
trt_choices = resolved_trt$choices, |
| 395 | ! |
trt_selected = resolved_trt$selected |
| 396 |
) |
|
| 397 |
}) |
|
| 398 | ||
| 399 | ! |
iv_r <- reactive({
|
| 400 | ! |
iv <- shinyvalidate::InputValidator$new() |
| 401 | ||
| 402 | ! |
iv$add_rule("xaxis_param", shinyvalidate::sv_required("Please select an X-Axis biomarker"))
|
| 403 | ! |
iv$add_rule("yaxis_param", shinyvalidate::sv_required("Please select a Y-Axis biomarker"))
|
| 404 | ! |
iv$add_rule("trt_group", shinyvalidate::sv_required("Please select a treatment variable"))
|
| 405 | ! |
iv$add_rule("xaxis_var", shinyvalidate::sv_required("Please select an X-Axis variable"))
|
| 406 | ! |
iv$add_rule("yaxis_var", shinyvalidate::sv_required("Please select a Y-Axis variable"))
|
| 407 | ! |
iv$add_rule("facet_ncol", plots_per_row_validate_rules())
|
| 408 | ||
| 409 | ! |
iv$add_validator(anl_constraint_output()$iv_r()) |
| 410 | ! |
iv$add_validator(horizontal_line()$iv_r()) |
| 411 | ! |
iv$add_validator(vertical_line()$iv_r()) |
| 412 | ! |
iv$enable() |
| 413 | ! |
iv |
| 414 |
}) |
|
| 415 | ||
| 416 | ||
| 417 | ||
| 418 |
# filter selected biomarkers |
|
| 419 | ! |
anl_param <- reactive({
|
| 420 | ! |
dataset_var <- dataname |
| 421 | ! |
ANL <- data()[[dataname]] # nolint |
| 422 | ! |
validate_has_data(ANL, 1) |
| 423 | ||
| 424 | ! |
if (length(input$hline_vars) > 0) {
|
| 425 | ! |
validate( |
| 426 | ! |
need( |
| 427 | ! |
all(input$hline_vars %in% names(ANL)), |
| 428 | ! |
"One or more selected horizontal line variable(s) is/are not names to any column in the data" |
| 429 |
), |
|
| 430 | ! |
need( |
| 431 | ! |
all(input$vline_vars %in% names(ANL)), |
| 432 | ! |
"One or more selected vertical line variable(s) is/are not names to any column in the data" |
| 433 |
) |
|
| 434 |
) |
|
| 435 |
} |
|
| 436 | ||
| 437 | ! |
validate_has_variable(ANL, param_var) |
| 438 | ||
| 439 | ! |
validate_in( |
| 440 | ! |
input$xaxis_param, unique(ANL[[param_var]]), |
| 441 | ! |
sprintf("X-Axis Biomarker %s is not available in data %s", input$xaxis_param, dataname)
|
| 442 |
) |
|
| 443 | ||
| 444 | ! |
validate_in( |
| 445 | ! |
input$yaxis_param, unique(ANL[[param_var]]), |
| 446 | ! |
sprintf("Y-Axis Biomarker %s is not available in data %s", input$yaxis_param, dataname)
|
| 447 |
) |
|
| 448 | ||
| 449 | ! |
validate_has_variable( |
| 450 | ! |
ANL, |
| 451 | ! |
"AVISITCD", |
| 452 | ! |
sprintf("Variable AVISITCD is not available in data %s", dataname)
|
| 453 |
) |
|
| 454 | ||
| 455 | ! |
validate_has_variable( |
| 456 | ! |
ANL, |
| 457 | ! |
"BASE", |
| 458 | ! |
sprintf("Variable BASE is not available in data %s", dataname)
|
| 459 |
) |
|
| 460 | ||
| 461 | ! |
validate_has_variable( |
| 462 | ! |
ANL, |
| 463 | ! |
"BASE2", |
| 464 | ! |
sprintf("Variable BASE2 is not available in data %s", dataname)
|
| 465 |
) |
|
| 466 | ||
| 467 | ! |
validate_has_variable( |
| 468 | ! |
ANL, |
| 469 | ! |
"LOQFL", |
| 470 | ! |
sprintf("Variable LOQFL is not available in data %s", dataname)
|
| 471 |
) |
|
| 472 | ||
| 473 | ! |
validate_has_variable( |
| 474 | ! |
ANL, |
| 475 | ! |
"PARAM", |
| 476 | ! |
sprintf("Variable PARAM is not available in data %s", dataname)
|
| 477 |
) |
|
| 478 | ||
| 479 | ! |
validate_has_variable( |
| 480 | ! |
ANL, |
| 481 | ! |
"LBSTRESC", |
| 482 | ! |
sprintf("Variable LBSTRESC is not available in data %s", dataname)
|
| 483 |
) |
|
| 484 | ||
| 485 | ! |
validate_has_variable( |
| 486 | ! |
ANL, |
| 487 | ! |
input$trt_group, |
| 488 | ! |
sprintf("Variable %s is not available in data %s", input$trt_group, dataname)
|
| 489 |
) |
|
| 490 | ||
| 491 | ! |
validate_has_variable( |
| 492 | ! |
ANL, |
| 493 | ! |
"USUBJID", |
| 494 | ! |
sprintf("Variable USUBJID is not available in data %s", dataname)
|
| 495 |
) |
|
| 496 | ||
| 497 | ! |
validate_has_variable( |
| 498 | ! |
ANL, |
| 499 | ! |
input$xaxis_var, |
| 500 | ! |
sprintf("Variable %s is not available in data %s", input$xaxis_var, dataname)
|
| 501 |
) |
|
| 502 | ||
| 503 | ! |
validate_has_variable( |
| 504 | ! |
ANL, |
| 505 | ! |
input$yaxis_var, |
| 506 | ! |
sprintf("Variable %s is not available in data %s", input$yaxis_var, dataname)
|
| 507 |
) |
|
| 508 | ||
| 509 |
# analysis |
|
| 510 | ! |
private_qenv <- data() %>% |
| 511 | ! |
teal.code::eval_code( |
| 512 | ! |
code = bquote({
|
| 513 | ! |
ANL <- .(as.name(dataset_var)) %>% # nolint |
| 514 | ! |
dplyr::filter(.data[[.(param_var)]] %in% union(.(input$xaxis_param), .(input$yaxis_param))) %>% |
| 515 | ! |
dplyr::select( |
| 516 | ! |
.(c( |
| 517 | ! |
"USUBJID", input$trt_group, "AVISITCD", param_var, "PARAM", input$xaxis_var, input$yaxis_var, "AVALU", |
| 518 | ! |
"LOQFL", "LBSTRESC", unique(c(input$hline_vars, input$vline_vars)) |
| 519 |
)) |
|
| 520 |
) |
|
| 521 |
}) |
|
| 522 |
) |
|
| 523 | ! |
validate_has_data(private_qenv[["ANL"]], 1) |
| 524 | ! |
return(list(ANL = ANL, qenv = private_qenv)) |
| 525 |
}) |
|
| 526 | ||
| 527 |
# constraints |
|
| 528 | ! |
observe({
|
| 529 | ! |
req(input$xaxis_param) |
| 530 | ||
| 531 | ! |
constraint_var <- input$constraint_var |
| 532 | ! |
req(constraint_var) |
| 533 | ||
| 534 |
# note that filtered is false thus we cannot use anl_param()$ANL |
|
| 535 | ! |
ANL <- data()[[dataname]] # nolint |
| 536 | ! |
validate_has_data(ANL, 1) |
| 537 | ||
| 538 | ! |
validate_has_variable(ANL, param_var) |
| 539 | ! |
validate_has_variable(ANL, "AVISITCD") |
| 540 | ! |
validate_has_variable(ANL, "BASE") |
| 541 | ! |
validate_has_variable(ANL, "BASE2") |
| 542 | ||
| 543 | ! |
ANL <- ANL %>% dplyr::filter(.data[[param_var]] == input$xaxis_param) # nolint |
| 544 | ||
| 545 | ! |
visit_freq <- unique(ANL$AVISITCD) |
| 546 | ||
| 547 |
# get min max values |
|
| 548 | ! |
if ((constraint_var == "BASE2" && any(grepl("SCR", visit_freq))) ||
|
| 549 | ! |
(constraint_var == "BASE" && any(grepl("BL", visit_freq)))) { # nolint
|
| 550 | ! |
val <- stats::na.omit(switch(constraint_var, |
| 551 | ! |
"BASE" = ANL$BASE[ANL$AVISITCD == "BL"], |
| 552 | ! |
"BASE2" = ANL$BASE2[ANL$AVISITCD == "SCR"], |
| 553 | ! |
stop(paste(constraint_var, "not allowed")) |
| 554 |
)) |
|
| 555 | ||
| 556 | ! |
if (length(val) == 0 || all(is.na(val))) {
|
| 557 | ! |
shinyjs::show("all_na")
|
| 558 | ! |
shinyjs::hide("constraint_range")
|
| 559 | ! |
args <- list( |
| 560 | ! |
min = list(label = "Min", min = 0, max = 0, value = 0), |
| 561 | ! |
max = list(label = "Max", min = 0, max = 0, value = 0) |
| 562 |
) |
|
| 563 | ! |
update_min_max(session, args) |
| 564 |
} else {
|
|
| 565 | ! |
rng <- range(val, na.rm = TRUE) |
| 566 | ||
| 567 | ! |
minmax <- c(floor(rng[1] * 1000) / 1000, ceiling(rng[2] * 1000) / 1000) |
| 568 | ||
| 569 | ! |
label_min <- sprintf("Min (%s)", minmax[1])
|
| 570 | ! |
label_max <- sprintf("Max (%s)", minmax[2])
|
| 571 | ||
| 572 | ! |
args <- list( |
| 573 | ! |
min = list(label = label_min, min = minmax[1], max = minmax[2], value = minmax[1]), |
| 574 | ! |
max = list(label = label_max, min = minmax[1], max = minmax[2], value = minmax[2]) |
| 575 |
) |
|
| 576 | ||
| 577 | ! |
update_min_max(session, args) |
| 578 | ! |
shinyjs::show("constraint_range") # update before show
|
| 579 | ! |
shinyjs::hide("all_na")
|
| 580 |
} |
|
| 581 | ! |
} else if (constraint_var == "NONE") {
|
| 582 | ! |
shinyjs::hide("constraint_range") # hide before update
|
| 583 | ! |
shinyjs::hide("all_na")
|
| 584 | ||
| 585 |
# force update (and thus refresh) on different constraint_var -> pass unique value for each constraint_var name |
|
| 586 | ! |
args <- list( |
| 587 | ! |
min = list(label = "Min", min = 0, max = 0, value = 0), |
| 588 | ! |
max = list(label = "Max", min = 0, max = 0, value = 0) |
| 589 |
) |
|
| 590 | ||
| 591 | ! |
update_min_max(session, args) |
| 592 |
} else {
|
|
| 593 | ! |
stop("invalid contraint_var", constraint_var)
|
| 594 |
} |
|
| 595 |
}) |
|
| 596 | ||
| 597 | ! |
anl_constraint_output <- create_anl_constraint_reactive(anl_param, input, param_id = "xaxis_param", min_rows = 1) |
| 598 | ! |
anl_constraint <- anl_constraint_output()$value |
| 599 | ||
| 600 |
# update sliders for axes taking constraints into account |
|
| 601 | ! |
data_state_x <- reactive({
|
| 602 | ! |
get_data_range_states( |
| 603 | ! |
varname = input$xaxis_var, |
| 604 | ! |
paramname = input$xaxis_param, |
| 605 | ! |
ANL = anl_constraint()$ANL |
| 606 |
) |
|
| 607 |
}) |
|
| 608 | ! |
xrange_slider <- toggle_slider_server("xrange_scale", data_state_x)
|
| 609 | ! |
data_state_y <- reactive({
|
| 610 | ! |
get_data_range_states( |
| 611 | ! |
varname = input$yaxis_var, |
| 612 | ! |
paramname = input$yaxis_param, |
| 613 | ! |
ANL = anl_constraint()$ANL |
| 614 |
) |
|
| 615 |
}) |
|
| 616 | ! |
yrange_slider <- toggle_slider_server("yrange_scale", data_state_y)
|
| 617 | ||
| 618 | ! |
keep_data_const_opts_updated(session, input, anl_constraint, "xaxis_param") |
| 619 | ||
| 620 |
# selector names after transposition |
|
| 621 | ! |
xvar <- reactive(paste0(input$xaxis_var, "_", input$xaxis_param)) |
| 622 | ! |
yvar <- reactive(paste0(input$yaxis_var, "_", input$yaxis_param)) |
| 623 | ! |
xloqfl <- reactive(paste0("LOQFL_", input$xaxis_param))
|
| 624 | ! |
yloqfl <- reactive(paste0("LOQFL_", input$yaxis_param))
|
| 625 | ||
| 626 |
# transpose data to plot |
|
| 627 | ! |
plot_data_transpose <- reactive({
|
| 628 | ! |
teal::validate_inputs(iv_r()) |
| 629 | ||
| 630 | ! |
req(anl_constraint()) |
| 631 | ! |
ANL <- anl_constraint()$ANL # nolint |
| 632 | ! |
trt_group <- input$trt_group |
| 633 | ||
| 634 | ! |
qenv <- anl_constraint()$qenv %>% teal.code::eval_code( |
| 635 | ! |
code = bquote({
|
| 636 | ! |
ANL_x <- ANL %>% # nolint |
| 637 | ! |
dplyr::filter(.data[[.(param_var)]] == .(input$xaxis_param) & !is.na(.data[[.(input$xaxis_var)]])) |
| 638 |
}) |
|
| 639 |
) |
|
| 640 | ||
| 641 | ! |
if (input$xaxis_var == "BASE") {
|
| 642 | ! |
qenv <- qenv %>% within({
|
| 643 | ! |
ANL_x <- ANL_x %>% # nolint |
| 644 | ! |
dplyr::group_by(.data[["USUBJID"]]) %>% |
| 645 | ! |
dplyr::mutate(LOQFL = .data[["LOQFL"]][.data[["AVISITCD"]] == "BL"]) %>% |
| 646 | ! |
dplyr::ungroup() |
| 647 |
}) |
|
| 648 | ! |
} else if (input$xaxis_var != "AVAL") {
|
| 649 | ! |
qenv <- qenv %>% within({
|
| 650 | ! |
ANL_x <- ANL_x %>% # nolint |
| 651 | ! |
dplyr::mutate(LOQFL = "N") |
| 652 |
}) |
|
| 653 |
} |
|
| 654 | ||
| 655 | ! |
qenv <- qenv %>% teal.code::eval_code( |
| 656 | ! |
code = bquote({
|
| 657 | ! |
ANL_y <- ANL %>% # nolint |
| 658 | ! |
dplyr::filter(.data[[.(param_var)]] == .(input$yaxis_param) & !is.na(.data[[.(input$yaxis_var)]])) |
| 659 |
}) |
|
| 660 |
) |
|
| 661 | ||
| 662 | ! |
if (input$yaxis_var == "BASE") {
|
| 663 | ! |
qenv <- qenv %>% within({
|
| 664 | ! |
ANL_y <- ANL_y %>% # nolint |
| 665 | ! |
dplyr::group_by(.data[["USUBJID"]]) %>% |
| 666 | ! |
dplyr::mutate(LOQFL = .data[["LOQFL"]][.data[["AVISITCD"]] == "BL"]) %>% |
| 667 | ! |
dplyr::ungroup() |
| 668 |
}) |
|
| 669 | ! |
} else if (input$yaxis_var != "AVAL") {
|
| 670 | ! |
qenv <- qenv %>% within({
|
| 671 | ! |
ANL_y <- ANL_y %>% # nolint |
| 672 | ! |
dplyr::mutate(LOQFL = "N") |
| 673 |
}) |
|
| 674 |
} |
|
| 675 | ||
| 676 | ! |
qenv <- qenv %>% teal.code::eval_code( |
| 677 | ! |
code = bquote({
|
| 678 | ! |
ANL_TRANSPOSED <- dplyr::inner_join( # nolint |
| 679 | ! |
ANL_x, ANL_y, |
| 680 | ! |
by = c("USUBJID", "AVISITCD", .(trt_group)),
|
| 681 | ! |
suffix = .(sprintf("_%s", c(input$xaxis_param, input$yaxis_param)))
|
| 682 |
) |
|
| 683 | ! |
ANL_TRANSPOSED <- ANL_TRANSPOSED %>% # nolint |
| 684 | ! |
dplyr::mutate( |
| 685 | ! |
LOQFL_COMB = case_when( |
| 686 | ! |
.data[[.(xloqfl())]] == "Y" | .data[[.(yloqfl())]] == "Y" ~ "Y", |
| 687 | ! |
.data[[.(xloqfl())]] == "N" | .data[[.(yloqfl())]] == "N" ~ "N", |
| 688 | ! |
TRUE ~ "NA" |
| 689 |
) |
|
| 690 |
) |
|
| 691 |
}) |
|
| 692 |
) |
|
| 693 | ||
| 694 | ! |
validate(need(nrow(qenv[["ANL_TRANSPOSED"]]) > 0, "Plot Data No Observations Left")) |
| 695 | ! |
validate_has_variable(data = qenv[["ANL_TRANSPOSED"]], varname = c(xvar(), yvar(), xloqfl(), yloqfl())) |
| 696 | ||
| 697 | ! |
qenv <- teal.code::eval_code( |
| 698 | ! |
object = qenv, |
| 699 | ! |
code = |
| 700 | ! |
bquote(attr(ANL_TRANSPOSED[[.(trt_group)]], "label") <- attr(ANL[[.(trt_group)]], "label")) # nolint |
| 701 |
) |
|
| 702 | ! |
return(list(ANL_TRANSPOSED = qenv[["ANL_TRANSPOSED"]], qenv = qenv)) |
| 703 |
}) |
|
| 704 | ||
| 705 | ! |
plot_labels <- reactive({
|
| 706 | ! |
req(anl_constraint()) |
| 707 | ! |
ANL <- anl_constraint()$qenv[["ANL"]] # nolint |
| 708 | ||
| 709 | ! |
xparam <- ANL$PARAM[ANL[[param_var]] == input$xaxis_param][1] |
| 710 | ! |
yparam <- ANL$PARAM[ANL[[param_var]] == input$yaxis_param][1] |
| 711 | ||
| 712 |
# setup the x-axis label. Combine the biomarker and the units (if available) |
|
| 713 | ! |
if (is.null(ANL$AVALU) || all(ANL[["AVALU"]] == "")) {
|
| 714 | ! |
title_text <- paste(xparam, "and", yparam, "@ Visits") |
| 715 | ! |
xaxis_lab <- paste(xparam, input$xaxis_var, "Values") |
| 716 | ! |
yaxis_lab <- paste(yparam, input$yaxis_var, "Values") |
| 717 |
} else {
|
|
| 718 | ! |
xunit <- ANL$AVALU[ANL[[param_var]] == input$xaxis_param][1] |
| 719 | ! |
yunit <- ANL$AVALU[ANL[[param_var]] == input$yaxis_param][1] |
| 720 | ||
| 721 | ! |
title_text <- paste0(xparam, " (", xunit, ") and ", yparam, " (", yunit, ") @ Visits")
|
| 722 | ! |
xaxis_lab <- paste0(xparam, " (", xunit, ") ", input$xaxis_var, " Values")
|
| 723 | ! |
yaxis_lab <- paste0(yparam, " (", yunit, ") ", input$yaxis_var, " Values")
|
| 724 |
} |
|
| 725 | ||
| 726 | ! |
list(title_text = title_text, xaxis_lab = xaxis_lab, yaxis_lab = yaxis_lab) |
| 727 |
}) |
|
| 728 | ||
| 729 | ! |
horizontal_line <- srv_arbitrary_lines("hline_arb")
|
| 730 | ! |
vertical_line <- srv_arbitrary_lines("vline_arb")
|
| 731 | ||
| 732 |
# plot |
|
| 733 | ! |
plot_q <- debounce(reactive({
|
| 734 | ! |
req(plot_data_transpose()) |
| 735 |
# nolint start |
|
| 736 | ! |
xaxis_param <- input$xaxis_param |
| 737 | ! |
xaxis_var <- input$xaxis_var |
| 738 | ! |
yaxis_param <- input$yaxis_param |
| 739 | ! |
yaxis_var <- input$yaxis_var |
| 740 | ! |
xlim <- xrange_slider$value |
| 741 | ! |
ylim <- yrange_slider$value |
| 742 | ! |
font_size <- input$font_size |
| 743 | ! |
dot_size <- input$dot_size |
| 744 | ! |
reg_text_size <- input$reg_text_size |
| 745 | ! |
hline_arb <- horizontal_line()$line_arb |
| 746 | ! |
hline_arb_label <- horizontal_line()$line_arb_label |
| 747 | ! |
hline_arb_color <- horizontal_line()$line_arb_color |
| 748 | ! |
hline_vars <- if (length(input$hline_vars) == 0) {
|
| 749 | ! |
NULL |
| 750 |
} else {
|
|
| 751 | ! |
paste0(input$hline_vars, "_", yaxis_param) |
| 752 |
} |
|
| 753 | ! |
vline_arb <- vertical_line()$line_arb |
| 754 | ! |
vline_arb_label <- vertical_line()$line_arb_label |
| 755 | ! |
vline_arb_color <- vertical_line()$line_arb_color |
| 756 | ! |
vline_vars <- if (length(input$vline_vars) == 0) {
|
| 757 | ! |
NULL |
| 758 |
} else {
|
|
| 759 | ! |
paste0(input$vline_vars, "_", xaxis_param) |
| 760 |
} |
|
| 761 | ! |
facet_ncol <- input$facet_ncol |
| 762 | ! |
validate(need( |
| 763 | ! |
is.na(facet_ncol) || (as.numeric(facet_ncol) > 0 && as.numeric(facet_ncol) %% 1 == 0), |
| 764 | ! |
"Number of plots per row must be a positive integer" |
| 765 |
)) |
|
| 766 | ! |
visit_facet <- input$visit_facet |
| 767 | ! |
facet <- input$trt_facet |
| 768 | ! |
reg_line <- input$reg_line |
| 769 | ! |
loq_legend <- input$loq_legend |
| 770 | ! |
rotate_xlab <- input$rotate_xlab |
| 771 |
# nolint end |
|
| 772 | ! |
title_text <- plot_labels()$title_text |
| 773 | ! |
xaxis_lab <- plot_labels()$xaxis_lab |
| 774 | ! |
yaxis_lab <- plot_labels()$yaxis_lab |
| 775 | ! |
validate(need(input$trt_group, "Please select a treatment variable")) |
| 776 | ! |
trt_group <- input$trt_group |
| 777 | ||
| 778 | ! |
teal.code::eval_code( |
| 779 | ! |
object = plot_data_transpose()$qenv, |
| 780 | ! |
code = bquote({
|
| 781 |
# re-establish treatment variable label |
|
| 782 | ! |
p <- goshawk::g_correlationplot( |
| 783 | ! |
data = ANL_TRANSPOSED, |
| 784 | ! |
param_var = .(param_var), |
| 785 | ! |
xaxis_param = .(xaxis_param), |
| 786 | ! |
xaxis_var = .(xaxis_var), |
| 787 | ! |
xvar = .(xvar()), |
| 788 | ! |
yaxis_param = .(yaxis_param), |
| 789 | ! |
yaxis_var = .(yaxis_var), |
| 790 | ! |
yvar = .(yvar()), |
| 791 | ! |
trt_group = .(trt_group), |
| 792 | ! |
xlim = .(xlim), |
| 793 | ! |
ylim = .(ylim), |
| 794 | ! |
title_text = .(title_text), |
| 795 | ! |
xaxis_lab = .(xaxis_lab), |
| 796 | ! |
yaxis_lab = .(yaxis_lab), |
| 797 | ! |
color_manual = .(color_manual), |
| 798 | ! |
shape_manual = .(shape_manual), |
| 799 | ! |
facet_ncol = .(facet_ncol), |
| 800 | ! |
visit_facet = .(visit_facet), |
| 801 | ! |
facet = .(facet), |
| 802 | ! |
facet_var = .(trt_group), |
| 803 | ! |
reg_line = .(reg_line), |
| 804 | ! |
font_size = .(font_size), |
| 805 | ! |
dot_size = .(dot_size), |
| 806 | ! |
reg_text_size = .(reg_text_size), |
| 807 | ! |
loq_legend = .(loq_legend), |
| 808 | ! |
rotate_xlab = .(rotate_xlab), |
| 809 | ! |
hline_arb = .(hline_arb), |
| 810 | ! |
hline_arb_label = .(hline_arb_label), |
| 811 | ! |
hline_arb_color = .(hline_arb_color), |
| 812 | ! |
hline_vars = .(hline_vars), |
| 813 | ! |
hline_vars_colors = .(hline_vars_colors[seq_along(hline_vars)]), |
| 814 | ! |
hline_vars_labels = .(paste(hline_vars_labels[seq_along(hline_vars)], "-", yaxis_param)), |
| 815 | ! |
vline_arb = .(vline_arb), |
| 816 | ! |
vline_arb_label = .(vline_arb_label), |
| 817 | ! |
vline_arb_color = .(vline_arb_color), |
| 818 | ! |
vline_vars = .(vline_vars), |
| 819 | ! |
vline_vars_colors = .(vline_vars_colors[seq_along(vline_vars)]), |
| 820 | ! |
vline_vars_labels = .(paste(vline_vars_labels[seq_along(vline_vars)], "-", xaxis_param)) |
| 821 |
) |
|
| 822 | ! |
p |
| 823 |
}) |
|
| 824 |
) |
|
| 825 | ! |
}), 800) |
| 826 | ||
| 827 | ! |
plot_r <- reactive(plot_q()[["p"]]) |
| 828 | ||
| 829 | ! |
plot_data <- teal.widgets::plot_with_settings_srv( |
| 830 | ! |
id = "plot", |
| 831 | ! |
plot_r = plot_r, |
| 832 | ! |
height = plot_height, |
| 833 | ! |
width = plot_width, |
| 834 | ! |
brushing = TRUE |
| 835 |
) |
|
| 836 | ||
| 837 | ! |
code <- reactive(teal.code::get_code(plot_q())) |
| 838 | ||
| 839 |
### REPORTER |
|
| 840 | ! |
if (with_reporter) {
|
| 841 | ! |
card_fun <- function(comment, label) {
|
| 842 | ! |
constraint_description <- paste( |
| 843 | ! |
"\nTreatment Variable Faceting:", |
| 844 | ! |
input$trt_facet, |
| 845 | ! |
"\nRegression Line:", |
| 846 | ! |
input$reg_line |
| 847 |
) |
|
| 848 | ! |
card <- report_card_template_goshawk( |
| 849 | ! |
title = "Correlation Plot", |
| 850 | ! |
label = label, |
| 851 | ! |
with_filter = with_filter, |
| 852 | ! |
filter_panel_api = filter_panel_api, |
| 853 | ! |
constraint_list = list( |
| 854 | ! |
constraint_var = input$constraint_var, |
| 855 | ! |
constraint_range_min = input$constraint_range_min, |
| 856 | ! |
constraint_range_max = input$constraint_range_max |
| 857 |
), |
|
| 858 | ! |
constraint_description = constraint_description, |
| 859 | ! |
style = "verbatim" |
| 860 |
) |
|
| 861 | ! |
card$append_text("Plot", "header3")
|
| 862 | ! |
card$append_plot(plot_r(), dim = plot_data$dim()) |
| 863 | ! |
if (!comment == "") {
|
| 864 | ! |
card$append_text("Comment", "header3")
|
| 865 | ! |
card$append_text(comment) |
| 866 |
} |
|
| 867 | ! |
card$append_src(code()) |
| 868 | ! |
card |
| 869 |
} |
|
| 870 | ! |
teal.reporter::add_card_button_srv("add_reporter", reporter = reporter, card_fun = card_fun)
|
| 871 |
} |
|
| 872 |
### |
|
| 873 | ||
| 874 | ! |
reactive_df <- debounce(reactive({
|
| 875 | ! |
req(iv_r()$is_valid()) |
| 876 | ! |
plot_brush <- plot_data$brush() |
| 877 | ||
| 878 | ! |
ANL_TRANSPOSED <- isolate(plot_data_transpose()$ANL_TRANSPOSED) # nolint |
| 879 | ||
| 880 | ! |
df <- teal.widgets::clean_brushedPoints( |
| 881 | ! |
dplyr::select( |
| 882 | ! |
ANL_TRANSPOSED, "USUBJID", dplyr::all_of(input$trt_group), "AVISITCD", |
| 883 | ! |
dplyr::all_of(c(xvar(), yvar())), "LOQFL_COMB" |
| 884 |
), |
|
| 885 | ! |
plot_brush |
| 886 |
) |
|
| 887 | ! |
}), 800) |
| 888 | ||
| 889 |
# highlight plot area |
|
| 890 | ! |
output$brush_data <- DT::renderDataTable({
|
| 891 | ! |
numeric_cols <- names(dplyr::select_if(reactive_df(), is.numeric)) |
| 892 | ||
| 893 | ! |
DT::datatable(reactive_df(), |
| 894 | ! |
rownames = FALSE, options = list(scrollX = TRUE) |
| 895 |
) %>% |
|
| 896 | ! |
DT::formatRound(numeric_cols, 4) |
| 897 |
}) |
|
| 898 | ||
| 899 | ! |
teal.widgets::verbatim_popup_srv( |
| 900 | ! |
id = "rcode", |
| 901 | ! |
verbatim_content = reactive(code()), |
| 902 | ! |
title = "Show R Code for Correlation Plot" |
| 903 |
) |
|
| 904 |
}) |
|
| 905 |
} |
| 1 |
#' Line plot |
|
| 2 |
#' |
|
| 3 |
#' This teal module renders the UI and calls the function that creates a line plot. |
|
| 4 |
#' |
|
| 5 |
#' @inheritParams teal.widgets::standard_layout |
|
| 6 |
#' @inheritParams teal::module |
|
| 7 |
#' @param label menu item label of the module in the teal app. |
|
| 8 |
#' @param dataname analysis data passed to the data argument of \code{\link[teal]{init}}. E.g. `ADaM` structured
|
|
| 9 |
#' laboratory data frame `ADLB`. |
|
| 10 |
#' @param param_var name of variable containing biomarker codes e.g. `PARAMCD`. |
|
| 11 |
#' @param param biomarker selected. |
|
| 12 |
#' @param param_var_label single name of variable in analysis data that includes parameter labels. |
|
| 13 |
#' @param xaxis_var single name of variable in analysis data that is used as x-axis in the plot for the |
|
| 14 |
#' respective `goshawk` function. |
|
| 15 |
#' @param xvar_level vector that can be used to define the factor level of `xvar`. Only use it when |
|
| 16 |
#' `xvar` is character or factor. |
|
| 17 |
#' @param filter_var data constraint variable. |
|
| 18 |
#' @param filter_var_choices data constraint variable choices. |
|
| 19 |
#' @param yaxis_var single name of variable in analysis data that is used as summary variable in the |
|
| 20 |
#' respective `goshawk` function. |
|
| 21 |
#' @param trt_group \code{\link[teal.transform]{choices_selected}} object with available choices and pre-selected option
|
|
| 22 |
#' for variable names representing treatment group e.g. `ARM`. |
|
| 23 |
#' @param trt_group_level vector that can be used to define factor level of `trt_group`. |
|
| 24 |
#' @param shape_choices Vector or \code{choices_selected} object with names of `ADSL` variables which
|
|
| 25 |
#' can be used to change shape |
|
| 26 |
#' @param color_manual string vector representing customized colors |
|
| 27 |
#' @param stat string of statistics |
|
| 28 |
#' @param hline_arb numeric vector of at most 2 values identifying intercepts for arbitrary horizontal lines. |
|
| 29 |
#' @param hline_arb_color a character vector of at most length of \code{hline_arb}.
|
|
| 30 |
#' naming the color for the arbitrary horizontal lines. |
|
| 31 |
#' @param hline_arb_label a character vector of at most length of \code{hline_arb}.
|
|
| 32 |
#' naming the label for the arbitrary horizontal lines. |
|
| 33 |
#' @param xtick numeric vector to define the tick values of x-axis when x variable is numeric. |
|
| 34 |
#' Default value is waive(). |
|
| 35 |
#' @param xlabel vector with same length of `xtick` to define the label of x-axis tick values. |
|
| 36 |
#' Default value is waive(). |
|
| 37 |
#' @param rotate_xlab `logical(1)` value indicating whether to rotate `x-axis` labels. |
|
| 38 |
#' @param plot_height controls plot height. |
|
| 39 |
#' @param plot_width optional, controls plot width. |
|
| 40 |
#' @param plot_font_size control font size for title, `x-axis`, `y-axis` and legend font. |
|
| 41 |
#' @param dodge controls the position dodge of error bar |
|
| 42 |
#' @param count_threshold minimum count of observations (as listed in the output table) to plot |
|
| 43 |
#' nodes on the graph |
|
| 44 |
#' @param table_font_size controls the font size of values in the table |
|
| 45 |
#' @param dot_size plot dot size. |
|
| 46 |
#' @param plot_relative_height_value numeric value between 500 and 5000 for controlling the starting value |
|
| 47 |
#' of the relative plot height slider |
|
| 48 |
#' @author Wenyi Liu (luiw2) wenyi.liu@roche.com |
|
| 49 |
#' @author Balazs Toth (tothb2) toth.balazs@gene.com |
|
| 50 |
#' |
|
| 51 |
#' @return \code{shiny} object
|
|
| 52 |
#' |
|
| 53 |
#' @export |
|
| 54 |
#' |
|
| 55 |
#' @examplesIf require("nestcolor")
|
|
| 56 |
#' # Example using ADaM structure analysis dataset. |
|
| 57 |
#' data <- teal_data() |
|
| 58 |
#' data <- within(data, {
|
|
| 59 |
#' library(dplyr) |
|
| 60 |
#' library(stringr) |
|
| 61 |
#' library(nestcolor) |
|
| 62 |
#' |
|
| 63 |
#' # original ARM value = dose value |
|
| 64 |
#' .arm_mapping <- list( |
|
| 65 |
#' "A: Drug X" = "150mg QD", |
|
| 66 |
#' "B: Placebo" = "Placebo", |
|
| 67 |
#' "C: Combination" = "Combination" |
|
| 68 |
#' ) |
|
| 69 |
#' set.seed(1) # @linksto ADSL ADLB |
|
| 70 |
#' ADSL <- rADSL |
|
| 71 |
#' ADLB <- rADLB |
|
| 72 |
#' .var_labels <- lapply(ADLB, function(x) attributes(x)$label) |
|
| 73 |
#' ADLB <- ADLB %>% |
|
| 74 |
#' mutate( |
|
| 75 |
#' AVISITCD = case_when( |
|
| 76 |
#' AVISIT == "SCREENING" ~ "SCR", |
|
| 77 |
#' AVISIT == "BASELINE" ~ "BL", |
|
| 78 |
#' grepl("WEEK", AVISIT) ~ paste("W", str_extract(AVISIT, "(?<=(WEEK ))[0-9]+")),
|
|
| 79 |
#' TRUE ~ as.character(NA) |
|
| 80 |
#' ), |
|
| 81 |
#' AVISITCDN = case_when( |
|
| 82 |
#' AVISITCD == "SCR" ~ -2, |
|
| 83 |
#' AVISITCD == "BL" ~ 0, |
|
| 84 |
#' grepl("W", AVISITCD) ~ as.numeric(gsub("[^0-9]*", "", AVISITCD)),
|
|
| 85 |
#' TRUE ~ as.numeric(NA) |
|
| 86 |
#' ), |
|
| 87 |
#' AVISITCD = factor(AVISITCD) %>% reorder(AVISITCDN), |
|
| 88 |
#' TRTORD = case_when( |
|
| 89 |
#' ARMCD == "ARM C" ~ 1, |
|
| 90 |
#' ARMCD == "ARM B" ~ 2, |
|
| 91 |
#' ARMCD == "ARM A" ~ 3 |
|
| 92 |
#' ), |
|
| 93 |
#' ARM = as.character(.arm_mapping[match(ARM, names(.arm_mapping))]), |
|
| 94 |
#' ARM = factor(ARM) %>% reorder(TRTORD), |
|
| 95 |
#' ACTARM = as.character(.arm_mapping[match(ACTARM, names(.arm_mapping))]), |
|
| 96 |
#' ACTARM = factor(ACTARM) %>% reorder(TRTORD) |
|
| 97 |
#' ) |
|
| 98 |
#' attr(ADLB[["ARM"]], "label") <- .var_labels[["ARM"]] |
|
| 99 |
#' attr(ADLB[["ACTARM"]], "label") <- .var_labels[["ACTARM"]] |
|
| 100 |
#' }) |
|
| 101 |
#' |
|
| 102 |
#' join_keys(data) <- default_cdisc_join_keys[names(data)] |
|
| 103 |
#' |
|
| 104 |
#' app <- init( |
|
| 105 |
#' data = data, |
|
| 106 |
#' modules = modules( |
|
| 107 |
#' tm_g_gh_lineplot( |
|
| 108 |
#' label = "Line Plot", |
|
| 109 |
#' dataname = "ADLB", |
|
| 110 |
#' param_var = "PARAMCD", |
|
| 111 |
#' param = choices_selected(c("ALT", "CRP", "IGA"), "ALT"),
|
|
| 112 |
#' shape_choices = c("SEX", "RACE"),
|
|
| 113 |
#' xaxis_var = choices_selected("AVISITCD", "AVISITCD"),
|
|
| 114 |
#' yaxis_var = choices_selected(c("AVAL", "BASE", "CHG", "PCHG"), "AVAL"),
|
|
| 115 |
#' trt_group = choices_selected(c("ARM", "ACTARM"), "ARM"),
|
|
| 116 |
#' hline_arb = c(20.5, 19.5), |
|
| 117 |
#' hline_arb_color = c("red", "green"),
|
|
| 118 |
#' hline_arb_label = c("A", "B")
|
|
| 119 |
#' ) |
|
| 120 |
#' ) |
|
| 121 |
#' ) |
|
| 122 |
#' if (interactive()) {
|
|
| 123 |
#' shinyApp(app$ui, app$server) |
|
| 124 |
#' } |
|
| 125 |
#' |
|
| 126 |
tm_g_gh_lineplot <- function(label, |
|
| 127 |
dataname, |
|
| 128 |
param_var, |
|
| 129 |
param, |
|
| 130 |
param_var_label = "PARAM", |
|
| 131 |
xaxis_var, |
|
| 132 |
yaxis_var, |
|
| 133 |
xvar_level = NULL, |
|
| 134 |
filter_var = yaxis_var, |
|
| 135 |
filter_var_choices = filter_var, |
|
| 136 |
trt_group, |
|
| 137 |
trt_group_level = NULL, |
|
| 138 |
shape_choices = NULL, |
|
| 139 |
stat = "mean", |
|
| 140 |
hline_arb = numeric(0), |
|
| 141 |
hline_arb_color = "red", |
|
| 142 |
hline_arb_label = "Horizontal line", |
|
| 143 |
color_manual = c( |
|
| 144 |
getOption("ggplot2.discrete.colour"),
|
|
| 145 |
c("#ff0000", "#008000", "#4ca3dd", "#8a2be2")
|
|
| 146 |
)[1:4], |
|
| 147 |
xtick = ggplot2::waiver(), |
|
| 148 |
xlabel = xtick, |
|
| 149 |
rotate_xlab = FALSE, |
|
| 150 |
plot_height = c(600, 200, 4000), |
|
| 151 |
plot_width = NULL, |
|
| 152 |
plot_font_size = c(12, 8, 20), |
|
| 153 |
dodge = c(0.4, 0, 1), |
|
| 154 |
pre_output = NULL, |
|
| 155 |
post_output = NULL, |
|
| 156 |
count_threshold = 0, |
|
| 157 |
table_font_size = c(12, 4, 20), |
|
| 158 |
dot_size = c(2, 1, 12), |
|
| 159 |
plot_relative_height_value = 1000, |
|
| 160 |
transformators = list()) {
|
|
| 161 | ! |
message("Initializing tm_g_gh_lineplot")
|
| 162 |
# Validate string inputs |
|
| 163 | ! |
checkmate::assert_string(label) |
| 164 | ! |
checkmate::assert_string(dataname) |
| 165 | ! |
checkmate::assert_string(param_var) |
| 166 | ! |
checkmate::assert_string(param_var_label) |
| 167 | ! |
checkmate::assert_string(stat) |
| 168 | ||
| 169 |
# Validate choices_selected class inputs |
|
| 170 | ! |
checkmate::assert_class(param, "choices_selected") |
| 171 | ! |
checkmate::assert_class(xaxis_var, "choices_selected") |
| 172 | ! |
checkmate::assert_class(yaxis_var, "choices_selected") |
| 173 | ! |
checkmate::assert_class(trt_group, "choices_selected") |
| 174 | ||
| 175 |
# Validate flag inputs |
|
| 176 | ! |
checkmate::assert_flag(rotate_xlab) |
| 177 | ||
| 178 |
# Validate numeric vector inputs |
|
| 179 | ! |
checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) |
| 180 | ! |
checkmate::assert_numeric(plot_height[1], lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height") |
| 181 | ! |
checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) |
| 182 | ! |
checkmate::assert_numeric( |
| 183 | ! |
plot_width[1], |
| 184 | ! |
lower = plot_width[2], upper = plot_width[3], null.ok = TRUE, .var.name = "plot_width" |
| 185 |
) |
|
| 186 | ! |
checkmate::assert_numeric(table_font_size, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) |
| 187 | ! |
checkmate::assert_numeric(dot_size, len = 3) |
| 188 | ! |
checkmate::assert_numeric( |
| 189 | ! |
table_font_size[1], |
| 190 | ! |
lower = table_font_size[2], upper = table_font_size[3], |
| 191 | ! |
null.ok = TRUE, .var.name = "table_font_size" |
| 192 |
) |
|
| 193 | ! |
checkmate::assert_number(plot_relative_height_value, lower = 500, upper = 5000) |
| 194 | ! |
checkmate::assert_number(count_threshold) |
| 195 | ||
| 196 |
# Validate color manual if provided |
|
| 197 | ! |
checkmate::assert_character(color_manual, null.ok = TRUE) |
| 198 | ! |
checkmate::assert_character(hline_arb_color) |
| 199 | ! |
checkmate::assert_character(hline_arb_label) |
| 200 | ||
| 201 |
# Validate line arguments |
|
| 202 | ! |
validate_line_arb_arg(hline_arb, hline_arb_color, hline_arb_label) |
| 203 | ||
| 204 | ! |
args <- as.list(environment()) |
| 205 | ||
| 206 | ! |
module( |
| 207 | ! |
label = label, |
| 208 | ! |
server = srv_lineplot, |
| 209 | ! |
server_args = list( |
| 210 | ! |
dataname = dataname, |
| 211 | ! |
param_var = param_var, |
| 212 | ! |
color_manual = color_manual, |
| 213 | ! |
xvar_level = xvar_level, |
| 214 | ! |
trt_group_level = trt_group_level, |
| 215 | ! |
shape_choices = shape_choices, |
| 216 | ! |
param_var_label = param_var_label, |
| 217 | ! |
xtick = xtick, |
| 218 | ! |
xlabel = xlabel, |
| 219 | ! |
plot_height = plot_height, |
| 220 | ! |
plot_width = plot_width, |
| 221 | ! |
module_args = args |
| 222 |
), |
|
| 223 | ! |
ui = ui_lineplot, |
| 224 | ! |
ui_args = args, |
| 225 | ! |
transformators = transformators, |
| 226 | ! |
datanames = dataname |
| 227 |
) |
|
| 228 |
} |
|
| 229 | ||
| 230 |
ui_lineplot <- function(id, ...) {
|
|
| 231 | ! |
ns <- NS(id) |
| 232 | ! |
a <- list(...) |
| 233 | ||
| 234 | ! |
shiny::tagList( |
| 235 | ! |
teal.widgets::standard_layout( |
| 236 | ! |
output = teal.widgets::plot_with_settings_ui(id = ns("plot")),
|
| 237 | ! |
encoding = tags$div( |
| 238 |
### Reporter |
|
| 239 | ! |
teal.reporter::add_card_button_ui(ns("add_reporter"), label = "Add Report Card"),
|
| 240 | ! |
tags$br(), tags$br(), |
| 241 |
### |
|
| 242 | ! |
templ_ui_dataname(a$dataname), |
| 243 | ! |
uiOutput(ns("axis_selections")),
|
| 244 | ! |
uiOutput(ns("shape_ui")),
|
| 245 | ! |
radioButtons(ns("stat"), "Select a Statistic:", c("mean", "median"), a$stat),
|
| 246 | ! |
checkboxInput(ns("include_stat"), "Include Statistic Table", value = TRUE),
|
| 247 | ! |
tags$div( |
| 248 | ! |
sliderInput( |
| 249 | ! |
ns("relative_height"),
|
| 250 | ! |
tags$div( |
| 251 | ! |
"Relative height of plot to table(s)", |
| 252 | ! |
bslib::tooltip( |
| 253 | ! |
trigger = icon("circle-info"),
|
| 254 | ! |
tags$span( |
| 255 | ! |
paste( |
| 256 | ! |
"The larger the value selected the greater the size of the plot relative\nto", |
| 257 | ! |
"the size of the tables. Note the units of this slider are arbitrary.\nTo", |
| 258 | ! |
"change the total size of the plot and table(s)\nuse", |
| 259 | ! |
"the plot resizing controls available at the top right of the plot." |
| 260 |
) |
|
| 261 |
) |
|
| 262 |
) |
|
| 263 |
), |
|
| 264 | ! |
min = 500, |
| 265 | ! |
max = 5000, |
| 266 | ! |
step = 50, |
| 267 | ! |
value = a$plot_relative_height_value, |
| 268 | ! |
ticks = FALSE |
| 269 |
), |
|
| 270 |
), |
|
| 271 | ! |
templ_ui_constraint(ns), # required by constr_anl_q |
| 272 | ! |
ui_arbitrary_lines(id = ns("hline_arb"), a$hline_arb, a$hline_arb_label, a$hline_arb_color),
|
| 273 | ! |
bslib::accordion( |
| 274 | ! |
bslib::accordion_panel( |
| 275 | ! |
title = "Plot Aesthetic Settings", |
| 276 | ! |
toggle_slider_ui( |
| 277 | ! |
ns("yrange_scale"),
|
| 278 | ! |
label = "Y-Axis Range Zoom" |
| 279 |
), |
|
| 280 | ! |
checkboxInput(ns("rotate_xlab"), "Rotate X-axis Label", a$rotate_xlab),
|
| 281 | ! |
numericInput(ns("count_threshold"), "Contributing Observations Threshold:", a$count_threshold)
|
| 282 |
), |
|
| 283 | ! |
bslib::accordion_panel( |
| 284 | ! |
title = "Plot settings", |
| 285 | ! |
teal.widgets::optionalSliderInputValMinMax(ns("dodge"), "Error Bar Position Dodge", a$dodge, ticks = FALSE),
|
| 286 | ! |
bslib::accordion( |
| 287 | ! |
bslib::accordion_panel( |
| 288 | ! |
title = "Line Settings", |
| 289 | ! |
uiOutput(ns("lines"))
|
| 290 |
), |
|
| 291 | ! |
bslib::accordion_panel( |
| 292 | ! |
title = "Symbol settings", |
| 293 | ! |
uiOutput(ns("symbols"))
|
| 294 |
) |
|
| 295 |
), |
|
| 296 | ! |
teal.widgets::optionalSliderInputValMinMax( |
| 297 | ! |
ns("plot_font_size"),
|
| 298 | ! |
"Font Size", |
| 299 | ! |
a$plot_font_size, |
| 300 | ! |
ticks = FALSE |
| 301 |
), |
|
| 302 | ! |
teal.widgets::optionalSliderInputValMinMax( |
| 303 | ! |
ns("dot_size"),
|
| 304 | ! |
"Dot Size", |
| 305 | ! |
a$dot_size, |
| 306 | ! |
ticks = FALSE |
| 307 |
) |
|
| 308 |
), |
|
| 309 | ! |
bslib::accordion_panel( |
| 310 | ! |
title = "Table settings", |
| 311 | ! |
teal.widgets::optionalSliderInputValMinMax( |
| 312 | ! |
ns("table_font_size"),
|
| 313 | ! |
"Table Font Size", |
| 314 | ! |
a$table_font_size, |
| 315 | ! |
ticks = FALSE |
| 316 |
) |
|
| 317 |
) |
|
| 318 |
) |
|
| 319 |
), |
|
| 320 | ! |
forms = tagList( |
| 321 | ! |
teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code")
|
| 322 |
), |
|
| 323 | ! |
pre_output = a$pre_output, |
| 324 | ! |
post_output = a$post_output |
| 325 |
) |
|
| 326 |
) |
|
| 327 |
} |
|
| 328 | ||
| 329 |
srv_lineplot <- function(id, |
|
| 330 |
data, |
|
| 331 |
reporter, |
|
| 332 |
filter_panel_api, |
|
| 333 |
dataname, |
|
| 334 |
param_var, |
|
| 335 |
trt_group, |
|
| 336 |
color_manual, |
|
| 337 |
xvar_level, |
|
| 338 |
trt_group_level, |
|
| 339 |
shape_choices, |
|
| 340 |
param_var_label, |
|
| 341 |
xtick, |
|
| 342 |
xlabel, |
|
| 343 |
plot_height, |
|
| 344 |
plot_width, |
|
| 345 |
module_args) {
|
|
| 346 | ! |
with_reporter <- !missing(reporter) && inherits(reporter, "Reporter") |
| 347 | ! |
with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelAPI") |
| 348 | ! |
checkmate::assert_class(data, "reactive") |
| 349 | ! |
checkmate::assert_class(shiny::isolate(data()), "teal_data") |
| 350 | ||
| 351 | ! |
moduleServer(id, function(input, output, session) {
|
| 352 | ! |
teal.logger::log_shiny_input_changes(input, namespace = "teal.goshawk") |
| 353 | ! |
ns <- session$ns |
| 354 | ||
| 355 | ! |
output$axis_selections <- renderUI({
|
| 356 | ! |
env <- shiny::isolate(as.list(data()[[".raw_data"]])) |
| 357 | ! |
resolved_x <- teal.transform::resolve_delayed(module_args$xaxis_var, env) |
| 358 | ! |
resolved_y <- teal.transform::resolve_delayed(module_args$yaxis_var, env) |
| 359 | ! |
resolved_param <- teal.transform::resolve_delayed(module_args$param, env) |
| 360 | ! |
resolved_trt <- teal.transform::resolve_delayed(module_args$trt_group, env) |
| 361 | ! |
templ_ui_params_vars( |
| 362 | ! |
ns, |
| 363 |
# xparam and yparam are identical, so we only show the user one |
|
| 364 | ! |
xparam_choices = resolved_param$choices, |
| 365 | ! |
xparam_selected = resolved_param$selected, |
| 366 | ! |
xparam_label = "Select a Biomarker", |
| 367 | ! |
xchoices = resolved_x$choices, |
| 368 | ! |
xselected = resolved_x$selected, |
| 369 | ! |
ychoices = resolved_y$choices, |
| 370 | ! |
yselected = resolved_y$selected, |
| 371 | ! |
trt_choices = resolved_trt$choices, |
| 372 | ! |
trt_selected = resolved_trt$selected |
| 373 |
) |
|
| 374 |
}) |
|
| 375 | ||
| 376 | ! |
output$shape_ui <- renderUI({
|
| 377 | ! |
if (!is.null(shape_choices)) {
|
| 378 | ! |
if (methods::is(shape_choices, "choices_selected")) {
|
| 379 | ! |
choices <- get_choices(shape_choices$choices) |
| 380 | ! |
selected <- shape_choices$selected |
| 381 |
} else {
|
|
| 382 | ! |
choices <- shape_choices |
| 383 | ! |
selected <- NULL |
| 384 |
} |
|
| 385 | ! |
teal.widgets::optionalSelectInput( |
| 386 | ! |
ns("shape"),
|
| 387 | ! |
"Select Line Splitting Variable", |
| 388 | ! |
choices = choices, selected = selected |
| 389 |
) |
|
| 390 |
} |
|
| 391 |
}) |
|
| 392 | ||
| 393 | ! |
anl_q_output <- constr_anl_q( |
| 394 | ! |
session = session, |
| 395 | ! |
input = input, |
| 396 | ! |
data = data, |
| 397 | ! |
dataname = dataname, |
| 398 | ! |
param_id = "xaxis_param", |
| 399 | ! |
param_var = param_var, |
| 400 | ! |
trt_group = input$trt_group, |
| 401 | ! |
min_rows = 2 |
| 402 |
) |
|
| 403 | ||
| 404 | ! |
anl_q <- anl_q_output()$value |
| 405 | ||
| 406 | ! |
keep_data_const_opts_updated(session, input, anl_q, "xaxis_param") |
| 407 | ||
| 408 | ! |
horizontal_line <- srv_arbitrary_lines("hline_arb")
|
| 409 | ||
| 410 | ! |
iv_r <- reactive({
|
| 411 | ! |
iv <- shinyvalidate::InputValidator$new() |
| 412 | ! |
iv$add_rule("xaxis_param", shinyvalidate::sv_required("Please select a biomarker"))
|
| 413 | ! |
iv$add_rule("trt_group", shinyvalidate::sv_required("Please select a treatment variable"))
|
| 414 | ! |
iv$add_rule("xaxis_var", shinyvalidate::sv_required("Please select an X-Axis variable"))
|
| 415 | ! |
iv$add_rule("yaxis_var", shinyvalidate::sv_required("Please select a Y-Axis variable"))
|
| 416 | ||
| 417 | ! |
iv$add_validator(horizontal_line()$iv_r()) |
| 418 | ! |
iv$add_validator(anl_q_output()$iv_r()) |
| 419 | ! |
iv$enable() |
| 420 | ! |
iv |
| 421 |
}) |
|
| 422 | ||
| 423 | ||
| 424 |
# update sliders for axes |
|
| 425 | ! |
data_state <- reactive({
|
| 426 | ! |
varname <- input[["yaxis_var"]] |
| 427 | ! |
validate(need(varname, "Please select variable")) |
| 428 | ||
| 429 | ! |
ANL <- anl_q()$ANL # nolint |
| 430 | ! |
validate_has_variable(ANL, varname, paste("variable", varname, "does not exist"))
|
| 431 | ||
| 432 | ! |
shape <- if (!(is.null(input$shape) || input$shape == "None")) {
|
| 433 | ! |
input$shape |
| 434 |
} else {
|
|
| 435 | ! |
NULL |
| 436 |
} |
|
| 437 | ||
| 438 |
# we don't need to additionally filter for paramvar here as in get_data_range_states because |
|
| 439 |
# xaxis_var and yaxis_var are always distinct |
|
| 440 | ! |
sum_data <- ANL %>% |
| 441 | ! |
dplyr::group_by_at(c(input$xaxis_var, input$trt_group, shape)) %>% |
| 442 | ! |
dplyr::summarise( |
| 443 | ! |
upper = if (input$stat == "mean") {
|
| 444 | ! |
mean(!!sym(varname), na.rm = TRUE) + |
| 445 | ! |
1.96 * stats::sd(!!sym(varname), na.rm = TRUE) / sqrt(dplyr::n()) |
| 446 |
} else {
|
|
| 447 | ! |
stats::quantile(!!sym(varname), 0.75, na.rm = TRUE) |
| 448 |
}, |
|
| 449 | ! |
lower = if (input$stat == "mean") {
|
| 450 | ! |
mean(!!sym(varname), na.rm = TRUE) - |
| 451 | ! |
1.96 * stats::sd(!!sym(varname), na.rm = TRUE) / sqrt(dplyr::n()) |
| 452 |
} else {
|
|
| 453 | ! |
stats::quantile(!!sym(varname), 0.25, na.rm = TRUE) |
| 454 |
} |
|
| 455 |
) |
|
| 456 | ||
| 457 | ! |
minmax <- grDevices::extendrange( |
| 458 | ! |
r = c( |
| 459 | ! |
floor(min(sum_data$lower, na.rm = TRUE) * 10) / 10, |
| 460 | ! |
ceiling(max(sum_data$upper, na.rm = TRUE) * 10) / 10 |
| 461 |
), |
|
| 462 | ! |
f = 0.05 |
| 463 |
) |
|
| 464 | ||
| 465 |
# we don't use get_data_range_states because this module computes the data ranges |
|
| 466 |
# not from the constrained ANL, but rather by first grouping and computing confidence |
|
| 467 |
# intervals |
|
| 468 | ! |
list( |
| 469 | ! |
range = c(min = minmax[[1]], max = minmax[[2]]) |
| 470 |
) |
|
| 471 |
}) |
|
| 472 | ! |
yrange_slider <- toggle_slider_server("yrange_scale", data_state)
|
| 473 | ||
| 474 | ! |
line_color_defaults <- color_manual |
| 475 | ! |
line_type_defaults <- c( |
| 476 | ! |
"blank", |
| 477 | ! |
"solid", |
| 478 | ! |
"dashed", |
| 479 | ! |
"dotted", |
| 480 | ! |
"dotdash", |
| 481 | ! |
"longdash", |
| 482 | ! |
"twodash", |
| 483 | ! |
"1F", |
| 484 | ! |
"F1", |
| 485 | ! |
"4C88C488", |
| 486 | ! |
"12345678" |
| 487 |
) |
|
| 488 | ||
| 489 | ! |
line_color_selected <- reactive({
|
| 490 | ! |
req(input$trt_group) |
| 491 | ! |
anl_arm <- as.factor(isolate(anl_q())$ANL[[input$trt_group]]) |
| 492 | ! |
anl_arm_nlevels <- nlevels(anl_arm) |
| 493 | ! |
anl_arm_levels <- levels(anl_arm) |
| 494 | ||
| 495 | ! |
stats::setNames( |
| 496 | ! |
vapply( |
| 497 | ! |
seq_len(anl_arm_nlevels), |
| 498 | ! |
function(idx) {
|
| 499 | ! |
x <- input[[paste0("line_color_", idx)]]
|
| 500 | ! |
anl_arm_level <- anl_arm_levels[[idx]] |
| 501 | ! |
if (length(x)) {
|
| 502 | ! |
x |
| 503 | ! |
} else if (anl_arm_level %in% names(line_color_defaults)) {
|
| 504 | ! |
line_color_defaults[[anl_arm_level]] |
| 505 | ! |
} else if (idx <= length(line_color_defaults)) {
|
| 506 | ! |
line_color_defaults[[idx]] |
| 507 |
} else {
|
|
| 508 | ! |
"#000000" |
| 509 |
} |
|
| 510 |
}, |
|
| 511 | ! |
character(1) |
| 512 |
), |
|
| 513 | ! |
anl_arm_levels |
| 514 |
) |
|
| 515 |
}) |
|
| 516 | ! |
line_type_selected <- reactive({
|
| 517 | ! |
req(input$trt_group) |
| 518 | ! |
anl_arm <- as.factor(isolate(anl_q())$ANL[[input$trt_group]]) |
| 519 | ! |
anl_arm_nlevels <- nlevels(anl_arm) |
| 520 | ! |
anl_arm_levels <- levels(anl_arm) |
| 521 | ||
| 522 | ! |
stats::setNames( |
| 523 | ! |
vapply( |
| 524 | ! |
seq_len(anl_arm_nlevels), |
| 525 | ! |
function(idx) {
|
| 526 | ! |
x <- input[[paste0("line_type_", idx)]]
|
| 527 | ! |
if (is.null(x)) "solid" else x |
| 528 |
}, |
|
| 529 | ! |
character(1) |
| 530 |
), |
|
| 531 | ! |
anl_arm_levels |
| 532 |
) |
|
| 533 |
}) |
|
| 534 | ||
| 535 | ! |
output$lines <- renderUI({
|
| 536 | ! |
req(input$trt_group) |
| 537 | ! |
anl_arm <- as.factor(anl_q()$ANL[[input$trt_group]]) |
| 538 | ! |
anl_arm_nlevels <- nlevels(anl_arm) |
| 539 | ! |
anl_arm_levels <- levels(anl_arm) |
| 540 | ||
| 541 | ! |
tagList( |
| 542 | ! |
lapply( |
| 543 | ! |
seq_len(anl_arm_nlevels), |
| 544 | ! |
function(idx) {
|
| 545 | ! |
x <- anl_arm_levels[[idx]] |
| 546 | ! |
color_input <- colourpicker::colourInput( |
| 547 | ! |
ns(paste0("line_color_", idx)),
|
| 548 | ! |
"Color:", |
| 549 | ! |
isolate(line_color_selected()[[idx]]) |
| 550 |
) |
|
| 551 | ! |
type_input <- selectInput( |
| 552 | ! |
ns(paste0("line_type_", idx)),
|
| 553 | ! |
"Type:", |
| 554 | ! |
choices = line_type_defaults, |
| 555 | ! |
selected = isolate(line_type_selected()[[idx]]) |
| 556 |
) |
|
| 557 | ! |
tags$div( |
| 558 | ! |
tags$label("Line configuration for:", tags$code(x)),
|
| 559 | ! |
tags$div( |
| 560 | ! |
color_input, |
| 561 | ! |
type_input |
| 562 |
) |
|
| 563 |
) |
|
| 564 |
} |
|
| 565 |
) |
|
| 566 |
) |
|
| 567 |
}) |
|
| 568 | ||
| 569 | ! |
symbol_type_start <- c( |
| 570 | ! |
"circle", |
| 571 | ! |
"square", |
| 572 | ! |
"diamond", |
| 573 | ! |
"triangle", |
| 574 | ! |
"circle open", |
| 575 | ! |
"square open", |
| 576 | ! |
"diamond open", |
| 577 | ! |
"triangle open", |
| 578 | ! |
"triangle down open", |
| 579 | ! |
"circle cross", |
| 580 | ! |
"square cross", |
| 581 | ! |
"circle plus", |
| 582 | ! |
"square plus", |
| 583 | ! |
"diamond plus", |
| 584 | ! |
"square triangle", |
| 585 | ! |
"plus", |
| 586 | ! |
"cross", |
| 587 | ! |
"asterisk" |
| 588 |
) |
|
| 589 | ! |
symbol_type_defaults <- reactiveVal(symbol_type_start) |
| 590 | ||
| 591 |
# reset shapes when different splitting variable is selected |
|
| 592 | ! |
observeEvent( |
| 593 | ! |
eventExpr = input$shape, |
| 594 | ! |
handlerExpr = symbol_type_defaults(symbol_type_start), |
| 595 | ! |
ignoreNULL = TRUE |
| 596 |
) |
|
| 597 | ||
| 598 | ! |
observe({
|
| 599 | ! |
req(input$shape) |
| 600 | ! |
req(anl_q()) |
| 601 | ! |
anl_shape <- anl_q()$ANL[[input$shape]] |
| 602 | ! |
anl_shape_nlevels <- nlevels(anl_shape) |
| 603 | ! |
symbol_type_to_set <- symbol_type_defaults()[pmin(length(symbol_type_defaults()), seq_len(anl_shape_nlevels))] |
| 604 | ! |
symbol_type_defaults(symbol_type_to_set) |
| 605 |
}) |
|
| 606 | ||
| 607 | ! |
symbol_type_selected <- reactive({
|
| 608 | ! |
req(anl_q()) |
| 609 | ! |
if (is.null(input$shape)) {
|
| 610 | ! |
return(NULL) |
| 611 |
} |
|
| 612 | ! |
anl_shape <- isolate(anl_q()$ANL[[input$shape]]) |
| 613 | ! |
anl_shape_nlevels <- nlevels(anl_shape) |
| 614 | ! |
anl_shape_levels <- levels(anl_shape) |
| 615 | ||
| 616 | ! |
stats::setNames( |
| 617 | ! |
vapply( |
| 618 | ! |
seq_len(anl_shape_nlevels), |
| 619 | ! |
function(idx) {
|
| 620 | ! |
x <- input[[paste0("symbol_type_", idx)]]
|
| 621 | ! |
if (is.null(x)) isolate(symbol_type_defaults())[[idx]] else x |
| 622 |
}, |
|
| 623 | ! |
character(1) |
| 624 |
), |
|
| 625 | ! |
anl_shape_levels |
| 626 |
) |
|
| 627 |
}) |
|
| 628 | ||
| 629 | ! |
output$symbols <- renderUI({
|
| 630 | ! |
req(symbol_type_defaults()) |
| 631 | ! |
validate(need(input$shape, "Please select line splitting variable first.")) |
| 632 | ||
| 633 | ! |
anl_shape <- isolate(anl_q()$ANL[[input$shape]]) |
| 634 | ! |
validate(need(is.factor(anl_shape), "Line splitting variable must be a factor.")) |
| 635 | ||
| 636 | ! |
anl_shape_nlevels <- nlevels(anl_shape) |
| 637 | ! |
anl_shape_levels <- levels(anl_shape) |
| 638 | ! |
symbol_def <- symbol_type_defaults() |
| 639 | ||
| 640 | ! |
tagList( |
| 641 | ! |
lapply( |
| 642 | ! |
seq_len(anl_shape_nlevels), |
| 643 | ! |
function(idx) {
|
| 644 | ! |
x <- anl_shape_levels[[idx]] |
| 645 | ! |
x_color <- symbol_def[[idx]] |
| 646 | ! |
selectInput( |
| 647 | ! |
ns(paste0("symbol_type_", idx)),
|
| 648 | ! |
HTML(paste0("Symbol for: ", tags$code(x))),
|
| 649 | ! |
choices = symbol_type_start, |
| 650 | ! |
selected = x_color |
| 651 |
) |
|
| 652 |
} |
|
| 653 |
) |
|
| 654 |
) |
|
| 655 |
}) |
|
| 656 | ||
| 657 | ! |
plot_q <- debounce(reactive({
|
| 658 | ! |
teal::validate_inputs(iv_r()) |
| 659 | ! |
req(anl_q(), line_color_selected(), line_type_selected()) |
| 660 |
# nolint start |
|
| 661 | ! |
ylim <- yrange_slider$value |
| 662 | ! |
plot_font_size <- input$plot_font_size |
| 663 | ! |
dot_size <- input$dot_size |
| 664 | ! |
dodge <- input$dodge |
| 665 | ! |
rotate_xlab <- input$rotate_xlab |
| 666 | ! |
count_threshold <- `if`(is.na(as.numeric(input$count_threshold)), 0, as.numeric(input$count_threshold)) |
| 667 | ! |
table_font_size <- input$table_font_size |
| 668 | ||
| 669 | ! |
median <- ifelse(input$stat == "median", TRUE, FALSE) |
| 670 | ! |
relative_height <- input$relative_height |
| 671 | ! |
trt_group <- input$trt_group |
| 672 | ! |
color_selected <- line_color_selected() |
| 673 | ! |
type_selected <- line_type_selected() |
| 674 | ! |
symbol_selected <- symbol_type_selected() |
| 675 | ! |
include_stat <- input$include_stat |
| 676 | ||
| 677 | ! |
param <- input$xaxis_param |
| 678 | ! |
xaxis <- input$xaxis_var |
| 679 | ! |
yaxis <- input$yaxis_var |
| 680 |
# nolint end |
|
| 681 | ||
| 682 | ! |
shape <- if (!(is.null(input$shape) || input$shape == "None")) {
|
| 683 | ! |
input$shape |
| 684 |
} else {
|
|
| 685 | ! |
NULL |
| 686 |
} |
|
| 687 | ||
| 688 | ! |
validate( |
| 689 | ! |
need( |
| 690 | ! |
nrow(anl_q()$ANL[stats::complete.cases(anl_q()$ANL[, c(yaxis, xaxis)]), ]) >= 2, |
| 691 | ! |
"Number of complete rows on x and y axis variables is less than 2" |
| 692 |
) |
|
| 693 |
) |
|
| 694 | ||
| 695 | ! |
private_qenv <- anl_q()$qenv |
| 696 | ||
| 697 | ! |
if (!methods::is(xtick, "waiver") && !is.null(xtick)) {
|
| 698 | ! |
private_qenv <- teal.code::eval_code( |
| 699 | ! |
object = private_qenv, |
| 700 | ! |
code = bquote({
|
| 701 | ! |
keep_index <- which(.(xtick) %in% ANL[[.(xaxis)]]) |
| 702 | ! |
xtick <- (.(xtick))[keep_index] # extra parentheses needed for edge case, e.g. 1:5[keep_index] |
| 703 | ! |
xlabel <- (.(xlabel))[keep_index] |
| 704 |
}) |
|
| 705 |
) |
|
| 706 | ! |
} else if (methods::is(xtick, "waiver")) {
|
| 707 | ! |
private_qenv <- teal.code::eval_code( |
| 708 | ! |
object = private_qenv, |
| 709 | ! |
code = " |
| 710 | ! |
xtick <- ggplot2::waiver() |
| 711 | ! |
xlabel <- ggplot2::waiver() |
| 712 |
" |
|
| 713 |
) |
|
| 714 |
} |
|
| 715 | ||
| 716 | ! |
hline_arb <- horizontal_line()$line_arb |
| 717 | ! |
hline_arb_label <- horizontal_line()$line_arb_label |
| 718 | ! |
hline_arb_color <- horizontal_line()$line_arb_color |
| 719 | ||
| 720 | ! |
teal.code::eval_code( |
| 721 | ! |
object = private_qenv, |
| 722 | ! |
code = bquote({
|
| 723 | ! |
p <- goshawk::g_lineplot( |
| 724 | ! |
data = ANL[stats::complete.cases(ANL[, c(.(yaxis), .(xaxis))]), ], |
| 725 | ! |
biomarker_var = .(param_var), |
| 726 | ! |
biomarker_var_label = .(param_var_label), |
| 727 | ! |
biomarker = .(param), |
| 728 | ! |
value_var = .(yaxis), |
| 729 | ! |
ylim = .(ylim), |
| 730 | ! |
trt_group = .(trt_group), |
| 731 | ! |
trt_group_level = .(trt_group_level), |
| 732 | ! |
shape = .(shape), |
| 733 | ! |
shape_type = .(symbol_selected), |
| 734 | ! |
time = .(xaxis), |
| 735 | ! |
time_level = .(xvar_level), |
| 736 | ! |
color_manual = .(color_selected), |
| 737 | ! |
line_type = .(type_selected), |
| 738 | ! |
median = .(median), |
| 739 | ! |
hline_arb = .(hline_arb), |
| 740 | ! |
hline_arb_label = .(hline_arb_label), |
| 741 | ! |
hline_arb_color = .(hline_arb_color), |
| 742 | ! |
xtick = .(if (!is.null(xtick)) quote(xtick) else xtick), |
| 743 | ! |
xlabel = .(if (!is.null(xtick)) quote(xlabel) else xlabel), |
| 744 | ! |
rotate_xlab = .(rotate_xlab), |
| 745 | ! |
plot_height = .(relative_height), # in g_lineplot this is relative height of plot to table |
| 746 | ! |
plot_font_size = .(plot_font_size), |
| 747 | ! |
dot_size = .(dot_size), |
| 748 | ! |
dodge = .(dodge), |
| 749 | ! |
count_threshold = .(count_threshold), |
| 750 | ! |
table_font_size = .(table_font_size), |
| 751 | ! |
display_center_tbl = .(include_stat) |
| 752 |
) |
|
| 753 | ! |
p |
| 754 |
}) |
|
| 755 |
) |
|
| 756 | ! |
}), 800) |
| 757 | ||
| 758 | ! |
plot_r <- reactive(plot_q()[["p"]]) |
| 759 | ||
| 760 | ! |
plot_data <- teal.widgets::plot_with_settings_srv( |
| 761 | ! |
id = "plot", |
| 762 | ! |
plot_r = plot_r, |
| 763 | ! |
height = plot_height, |
| 764 | ! |
width = plot_width, |
| 765 |
) |
|
| 766 | ||
| 767 | ! |
code <- reactive(teal.code::get_code(plot_q())) |
| 768 | ||
| 769 |
### REPORTER |
|
| 770 | ! |
if (with_reporter) {
|
| 771 | ! |
card_fun <- function(comment, label) {
|
| 772 | ! |
constraint_description <- paste( |
| 773 | ! |
"\nSelect Line Splitting Variable:", |
| 774 | ! |
if (!is.null(input$shape)) input$shape else "None", |
| 775 | ! |
"\nContributing Observations Threshold:", |
| 776 | ! |
input$count_threshold |
| 777 |
) |
|
| 778 | ! |
card <- report_card_template_goshawk( |
| 779 | ! |
title = "Line Plot", |
| 780 | ! |
label = label, |
| 781 | ! |
with_filter = with_filter, |
| 782 | ! |
filter_panel_api = filter_panel_api, |
| 783 | ! |
constraint_list = list( |
| 784 | ! |
constraint_var = input$constraint_var, |
| 785 | ! |
constraint_range_min = input$constraint_range_min, |
| 786 | ! |
constraint_range_max = input$constraint_range_max |
| 787 |
), |
|
| 788 | ! |
constraint_description = constraint_description, |
| 789 | ! |
style = "verbatim" |
| 790 |
) |
|
| 791 | ! |
card$append_text("Plot", "header3")
|
| 792 | ! |
card$append_plot(plot_r(), dim = plot_data$dim()) |
| 793 | ! |
if (!comment == "") {
|
| 794 | ! |
card$append_text("Comment", "header3")
|
| 795 | ! |
card$append_text(comment) |
| 796 |
} |
|
| 797 | ! |
card$append_src(code()) |
| 798 | ! |
card |
| 799 |
} |
|
| 800 | ! |
teal.reporter::add_card_button_srv("add_reporter", reporter = reporter, card_fun = card_fun)
|
| 801 |
} |
|
| 802 |
### |
|
| 803 | ||
| 804 | ! |
teal.widgets::verbatim_popup_srv( |
| 805 | ! |
id = "rcode", |
| 806 | ! |
verbatim_content = reactive(code()), |
| 807 | ! |
title = "Show R Code for Line Plot" |
| 808 |
) |
|
| 809 |
}) |
|
| 810 |
} |
| 1 |
templ_ui_output_datatable <- function(ns) {
|
|
| 2 | ! |
bslib::page_fluid( |
| 3 | ! |
teal.widgets::plot_with_settings_ui(id = ns("plot")),
|
| 4 | ! |
tags$br(), tags$hr(), |
| 5 | ! |
tags$h4("Selected Data Points"),
|
| 6 | ! |
tags$div( |
| 7 | ! |
DT::dataTableOutput(ns("brush_data"))
|
| 8 |
) |
|
| 9 |
) |
|
| 10 |
} |
|
| 11 | ||
| 12 |
templ_ui_dataname <- function(dataname) {
|
|
| 13 | 12x |
tags$label(dataname, "Data Settings", class = "text-primary") |
| 14 |
} |
|
| 15 | ||
| 16 |
# UI to create params (biomarker, value of PARAMCD) and vars (column, e.g. AVAL column) select fields for x and y |
|
| 17 |
templ_ui_params_vars <- function(ns, |
|
| 18 |
# x |
|
| 19 |
xparam_choices = NULL, |
|
| 20 |
xparam_selected = NULL, |
|
| 21 |
xparam_label = NULL, # biomarker, e.g. ALT |
|
| 22 |
xchoices = NULL, |
|
| 23 |
xselected = NULL, |
|
| 24 |
xvar_label = NULL, # variable, e.g. AVAL |
|
| 25 |
# y |
|
| 26 |
yparam_choices = NULL, |
|
| 27 |
yparam_selected = NULL, |
|
| 28 |
yparam_label = NULL, # biomarker, e.g. ALT |
|
| 29 |
ychoices = NULL, |
|
| 30 |
yselected = NULL, |
|
| 31 |
yvar_label = NULL, # variable, e.g. AVAL |
|
| 32 |
# facet_var |
|
| 33 |
facet_choices = NULL, |
|
| 34 |
facet_selected = NULL, |
|
| 35 |
# trt_group |
|
| 36 |
trt_choices = NULL, |
|
| 37 |
trt_selected = NULL, |
|
| 38 |
multiple = FALSE) {
|
|
| 39 | 6x |
if (is.null(xparam_choices) && !is.null(xchoices) && !is.null(yparam_choices)) {
|
| 40 |
# otherwise, xchoices will appear first without any biomarker to select and this looks odd in the UI |
|
| 41 | ! |
stop( |
| 42 | ! |
"You have to specify xparam choices rather than yparamchoices |
| 43 | ! |
if both xvar and yvar should be values for the same biomarker." |
| 44 |
) |
|
| 45 |
} |
|
| 46 | 6x |
tagList( |
| 47 | 6x |
if (!is.null(trt_choices)) {
|
| 48 | 6x |
teal.widgets::optionalSelectInput( |
| 49 | 6x |
ns("trt_group"),
|
| 50 | 6x |
label = "Select Treatment Variable", |
| 51 | 6x |
choices = trt_choices, |
| 52 | 6x |
selected = trt_selected, |
| 53 | 6x |
multiple = FALSE |
| 54 |
) |
|
| 55 |
}, |
|
| 56 | 6x |
if (!is.null(xparam_choices)) {
|
| 57 | 6x |
teal.widgets::optionalSelectInput( |
| 58 | 6x |
ns("xaxis_param"),
|
| 59 | 6x |
`if`(is.null(xparam_label), "Select an X-Axis Biomarker", xparam_label), |
| 60 | 6x |
xparam_choices, |
| 61 | 6x |
`if`(is.null(xparam_selected), xparam_choices[1], xparam_selected), |
| 62 | 6x |
multiple = FALSE |
| 63 |
) |
|
| 64 |
}, |
|
| 65 | 6x |
if (!is.null(xchoices)) {
|
| 66 | 6x |
teal.widgets::optionalSelectInput( |
| 67 | 6x |
ns("xaxis_var"),
|
| 68 | 6x |
`if`(is.null(xvar_label), "Select an X-Axis Variable", xvar_label), |
| 69 | 6x |
xchoices, xselected, |
| 70 | 6x |
multiple = multiple |
| 71 |
) |
|
| 72 |
}, |
|
| 73 | 6x |
if (!is.null(yparam_choices)) {
|
| 74 | ! |
teal.widgets::optionalSelectInput( |
| 75 | ! |
ns("yaxis_param"),
|
| 76 | ! |
`if`(is.null(yparam_label), "Select an Y-Axis Biomarker", yparam_label), |
| 77 | ! |
yparam_choices, |
| 78 | ! |
`if`(is.null(yparam_selected), yparam_choices[1], yparam_selected), |
| 79 | ! |
multiple = FALSE |
| 80 |
) |
|
| 81 |
}, |
|
| 82 | 6x |
if (!is.null(ychoices)) {
|
| 83 | 6x |
teal.widgets::optionalSelectInput( |
| 84 | 6x |
ns("yaxis_var"),
|
| 85 | 6x |
`if`(is.null(yvar_label), "Select a Y-Axis Variable", yvar_label), |
| 86 | 6x |
ychoices, yselected, |
| 87 | 6x |
multiple = multiple |
| 88 |
) |
|
| 89 |
}, |
|
| 90 | 6x |
if (!is.null(facet_choices)) {
|
| 91 | 6x |
teal.widgets::optionalSelectInput( |
| 92 | 6x |
ns("facet_var"),
|
| 93 | 6x |
label = "Facet by", |
| 94 | 6x |
choices = facet_choices, |
| 95 | 6x |
selected = facet_selected, |
| 96 | 6x |
multiple = FALSE |
| 97 |
) |
|
| 98 |
} |
|
| 99 |
) |
|
| 100 |
} |
| 1 |
#' helper for writing arm mapping and ordering code. |
|
| 2 |
#' |
|
| 3 |
#' Provides lines of code for left hand side of arm mapping. user must provide right hand side |
|
| 4 |
#' |
|
| 5 |
#' @details SPA configure study specific pre-processing for deploying `goshawk`. writing the code for `ARM` mapping and |
|
| 6 |
#' ordering is tedious. this function helps to get that started by providing the left hand side of the |
|
| 7 |
#' mapping and ordering syntax. call the function and then copy and paste the resulting code from the console |
|
| 8 |
#' into the app.R file. |
|
| 9 |
#' |
|
| 10 |
#' @param df_armvar the dataframe and column name containing treatment code. e.g. `ADSL$ARMCD` |
|
| 11 |
#' @param code controls whether mapping or ordering code is written to console. Valid values: `"M"` and `"O"`. |
|
| 12 |
#' |
|
| 13 |
#' @export |
|
| 14 |
#' |
|
| 15 |
#' @examples |
|
| 16 |
#' ADSL <- rADSL |
|
| 17 |
#' |
|
| 18 |
#' # get treatment mapping code |
|
| 19 |
#' maptrt(df_armvar = ADSL$ARMCD, code = "M") |
|
| 20 |
#' |
|
| 21 |
#' # get treatment ordering code |
|
| 22 |
#' maptrt(df_armvar = ADSL$ARMCD, code = "O") |
|
| 23 |
maptrt <- function(df_armvar, code = c("M", "O")) {
|
|
| 24 | ! |
code <- match.arg(code) |
| 25 | ||
| 26 |
# get arm variable |
|
| 27 | ! |
trtvar <- strsplit(deparse(substitute(df_armvar)), "[$]")[[1]][2] |
| 28 | ||
| 29 | ! |
dftrt <- data.frame(unique(df_armvar)) %>% |
| 30 | ! |
dplyr::mutate(trt_mapping = paste0("\"", unique(df_armvar), "\"", " = \"\",")) %>%
|
| 31 | ! |
dplyr::mutate(trt_ordering = paste0(eval(trtvar), " == \"", unique(df_armvar), "\"", " ~ ,")) |
| 32 | ||
| 33 | ! |
if (toupper(code) == "M") {
|
| 34 | ! |
print(unname(dftrt["trt_mapping"]), row.names = FALSE) |
| 35 | ! |
} else if (toupper(code) == "O") {
|
| 36 | ! |
print(unname(dftrt["trt_ordering"]), row.names = FALSE) |
| 37 |
} |
|
| 38 |
} |
| 1 |
plots_per_row_validate_rules <- function(required = TRUE) {
|
|
| 2 | 6x |
msg <- "Number of plots per row must be a positive integer" |
| 3 | 6x |
shinyvalidate::compose_rules( |
| 4 | 6x |
if (required) {
|
| 5 | ! |
shinyvalidate::sv_required(msg) |
| 6 |
} else {
|
|
| 7 | 6x |
shinyvalidate::sv_optional() |
| 8 |
}, |
|
| 9 | 6x |
shinyvalidate::sv_integer(msg), |
| 10 | 6x |
shinyvalidate::sv_gt(0, message_fmt = msg) |
| 11 |
) |
|
| 12 |
} |
|
| 13 | ||
| 14 |
#' Template Function for `TealReportCard` Creation and Customization in `teal.goshawk` |
|
| 15 |
#' |
|
| 16 |
#' This function generates a report card with a title, |
|
| 17 |
#' an optional description, and the option to append the filter state list. |
|
| 18 |
#' Additionally, it display selected constraint options. |
|
| 19 |
#' |
|
| 20 |
#' @inheritParams teal::report_card_template |
|
| 21 |
#' @param constraint_list (`list`) a list containing constraint variables, including: |
|
| 22 |
#' - constraint_var (`character(1)`) the constraint variable name. |
|
| 23 |
#' - constraint_range_min (`numeric(1)`) the minimum constraint range value. |
|
| 24 |
#' - constraint_range_max (`numeric(1)`) the maximum constraint range value. |
|
| 25 |
#' @param constraint_description (`character(1)`) description of the constraints. |
|
| 26 |
#' @param style (`character(1)`) style of the constraint text block. |
|
| 27 |
#' options: `default`, `verbatim` (default is `default`). |
|
| 28 |
#' |
|
| 29 |
#' @return (`TealReportCard`) populated with a title, description, and filter state |
|
| 30 |
#' |
|
| 31 |
#' @keywords internal |
|
| 32 |
report_card_template_goshawk <- function(title, |
|
| 33 |
label, |
|
| 34 |
with_filter, |
|
| 35 |
filter_panel_api, |
|
| 36 |
constraint_list, |
|
| 37 |
constraint_description = NULL, |
|
| 38 |
style = "default") {
|
|
| 39 | ! |
checkmate::assert_subset(names(constraint_list), c("constraint_var", "constraint_range_min", "constraint_range_max"))
|
| 40 | ! |
checkmate::assert_string(constraint_description, null.ok = TRUE) |
| 41 | ! |
checkmate::assert_choice(style, c("default", "verbatim"))
|
| 42 | ||
| 43 | ! |
card <- teal::report_card_template( |
| 44 | ! |
title = title, |
| 45 | ! |
label = label, |
| 46 | ! |
with_filter = with_filter, |
| 47 | ! |
filter_panel_api = filter_panel_api |
| 48 |
) |
|
| 49 | ||
| 50 | ! |
card$append_text("Selected Options", "header3")
|
| 51 | ! |
card$append_text( |
| 52 | ! |
paste( |
| 53 | ! |
formatted_data_constraint( |
| 54 | ! |
constraint_list$constraint_var, |
| 55 | ! |
constraint_list$constraint_range_min, |
| 56 | ! |
constraint_list$constraint_range_max |
| 57 |
), |
|
| 58 | ! |
constraint_description |
| 59 |
), |
|
| 60 | ! |
style = style |
| 61 |
) |
|
| 62 | ! |
card |
| 63 |
} |
|
| 64 | ||
| 65 |
#' Get Choices |
|
| 66 |
#' |
|
| 67 |
#' This function returns choices based on the class of the input. |
|
| 68 |
#' If the input is of class `delayed_data`, it returns the `subset` of the input. |
|
| 69 |
#' If `subset` is NULL and the input contains `var_label` and `var_choices`, |
|
| 70 |
#' it throws an error prompting to resolve delayed inputs. |
|
| 71 |
#' Otherwise, it returns the input as is. |
|
| 72 |
#' |
|
| 73 |
#' @param choices An object that contains choices. |
|
| 74 |
#' @return A vector of choices. |
|
| 75 |
#' @keywords internal |
|
| 76 |
get_choices <- function(choices) {
|
|
| 77 | ! |
if (inherits(choices, "delayed_data")) {
|
| 78 | ! |
if (is.null(choices$subset)) {
|
| 79 | ! |
if (!is.null(choices$var_label) && !is.null(choices$var_choices)) {
|
| 80 | ! |
stop( |
| 81 | ! |
"Resolve delayed inputs by evaluating the code within the provided datasets. |
| 82 | ! |
Check ?teal.transform::resolve_delayed for more information." |
| 83 |
) |
|
| 84 |
} else {
|
|
| 85 | ! |
stop("Subset is NULL and necessary fields are missing.")
|
| 86 |
} |
|
| 87 |
} else {
|
|
| 88 | ! |
choices$subset |
| 89 |
} |
|
| 90 |
} else {
|
|
| 91 | ! |
choices |
| 92 |
} |
|
| 93 |
} |
| 1 |
.onLoad <- function(libname, pkgname) { # nolint
|
|
| 2 | ! |
teal.logger::register_logger(namespace = "teal.goshawk") |
| 3 | ! |
teal.logger::register_handlers("teal.goshawk")
|
| 4 |
} |
| 1 |
#' Scatter Plot Teal Module For Biomarker Analysis |
|
| 2 |
#' |
|
| 3 |
#' @description |
|
| 4 |
#' `r lifecycle::badge("deprecated")`
|
|
| 5 |
#' |
|
| 6 |
#' `tm_g_gh_scatterplot` is deprecated. |
|
| 7 |
#' Please use [tm_g_gh_correlationplot] instead. |
|
| 8 |
#' |
|
| 9 |
#' @param ... function is deprecated. |
|
| 10 |
#' |
|
| 11 |
#' @export |
|
| 12 |
tm_g_gh_scatterplot <- function(...) {
|
|
| 13 | ! |
lifecycle::deprecate_stop( |
| 14 | ! |
when = "0.1.15", |
| 15 | ! |
what = "tm_g_gh_scatterplot()", |
| 16 | ! |
details = "You should use teal.goshawk::tm_g_gh_correlationplot instead of teal.goshawk::tm_g_gh_scatterplot" |
| 17 |
) |
|
| 18 |
} |