2025-02-25 09:51:42 +01:00
|
|
|
# source(here::here("functions.R"))
|
|
|
|
|
|
|
|
#' Data correlations evaluation module
|
|
|
|
#'
|
|
|
|
#' @param id Module id. (Use 'ns("id")')
|
|
|
|
#'
|
2025-03-13 12:41:50 +01:00
|
|
|
#' @name data-plots
|
2025-02-25 09:51:42 +01:00
|
|
|
#' @returns Shiny ui module
|
|
|
|
#' @export
|
|
|
|
#'
|
2025-03-05 21:13:06 +01:00
|
|
|
data_visuals_ui <- function(id, tab_title = "Plots", ...) {
|
2025-02-25 09:51:42 +01:00
|
|
|
ns <- shiny::NS(id)
|
|
|
|
|
|
|
|
# bslib::navset_bar(
|
|
|
|
list(
|
|
|
|
|
|
|
|
# Sidebar with a slider input
|
|
|
|
sidebar = bslib::sidebar(
|
|
|
|
bslib::accordion(
|
|
|
|
multiple = FALSE,
|
|
|
|
bslib::accordion_panel(
|
|
|
|
title = "Creating plot",
|
|
|
|
icon = bsicons::bs_icon("graph-up"),
|
|
|
|
shiny::uiOutput(outputId = ns("primary")),
|
|
|
|
shiny::uiOutput(outputId = ns("type")),
|
|
|
|
shiny::uiOutput(outputId = ns("secondary")),
|
2025-03-13 12:41:50 +01:00
|
|
|
shiny::uiOutput(outputId = ns("tertiary")),
|
|
|
|
shiny::br(),
|
|
|
|
shiny::actionButton(
|
|
|
|
inputId = ns("act_plot"),
|
|
|
|
label = "Plot",
|
|
|
|
width = "100%",
|
|
|
|
icon = shiny::icon("palette"),
|
|
|
|
disabled = FALSE
|
|
|
|
),
|
|
|
|
shiny::helpText('Adjust settings, then press "Plot".')
|
2025-03-05 21:13:06 +01:00
|
|
|
),
|
2025-03-13 12:41:50 +01:00
|
|
|
# bslib::accordion_panel(
|
|
|
|
# title = "Advanced",
|
|
|
|
# icon = bsicons::bs_icon("gear")
|
|
|
|
# ),
|
2025-02-25 09:51:42 +01:00
|
|
|
bslib::accordion_panel(
|
|
|
|
title = "Download",
|
|
|
|
icon = bsicons::bs_icon("download"),
|
|
|
|
shinyWidgets::noUiSliderInput(
|
|
|
|
inputId = ns("height"),
|
|
|
|
label = "Plot height (mm)",
|
|
|
|
min = 50,
|
|
|
|
max = 300,
|
|
|
|
value = 100,
|
|
|
|
step = 1,
|
2025-03-05 21:13:06 +01:00
|
|
|
format = shinyWidgets::wNumbFormat(decimals = 0),
|
2025-02-25 09:51:42 +01:00
|
|
|
color = datamods:::get_primary_color()
|
|
|
|
),
|
|
|
|
shinyWidgets::noUiSliderInput(
|
|
|
|
inputId = ns("width"),
|
|
|
|
label = "Plot width (mm)",
|
|
|
|
min = 50,
|
|
|
|
max = 300,
|
|
|
|
value = 100,
|
|
|
|
step = 1,
|
2025-03-05 21:13:06 +01:00
|
|
|
format = shinyWidgets::wNumbFormat(decimals = 0),
|
2025-02-25 09:51:42 +01:00
|
|
|
color = datamods:::get_primary_color()
|
|
|
|
),
|
|
|
|
shiny::selectInput(
|
|
|
|
inputId = ns("plot_type"),
|
|
|
|
label = "File format",
|
|
|
|
choices = list(
|
|
|
|
"png",
|
|
|
|
"tiff",
|
|
|
|
"eps",
|
|
|
|
"pdf",
|
|
|
|
"jpeg",
|
|
|
|
"svg"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
shiny::br(),
|
|
|
|
# Button
|
|
|
|
shiny::downloadButton(
|
|
|
|
outputId = ns("download_plot"),
|
|
|
|
label = "Download plot",
|
|
|
|
icon = shiny::icon("download")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
),
|
|
|
|
bslib::nav_panel(
|
|
|
|
title = tab_title,
|
|
|
|
shiny::plotOutput(ns("plot"))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#'
|
|
|
|
#' @param data data
|
|
|
|
#' @param ... ignored
|
|
|
|
#'
|
2025-03-13 12:41:50 +01:00
|
|
|
#' @name data-plots
|
2025-02-25 09:51:42 +01:00
|
|
|
#' @returns shiny server module
|
|
|
|
#' @export
|
|
|
|
data_visuals_server <- function(id,
|
|
|
|
data,
|
|
|
|
...) {
|
|
|
|
shiny::moduleServer(
|
|
|
|
id = id,
|
|
|
|
module = function(input, output, session) {
|
|
|
|
ns <- session$ns
|
|
|
|
|
|
|
|
rv <- shiny::reactiveValues(
|
|
|
|
plot.params = NULL,
|
|
|
|
plot = NULL
|
|
|
|
)
|
|
|
|
|
2025-03-19 13:10:56 +01:00
|
|
|
# ## --- New attempt
|
|
|
|
#
|
|
|
|
# rv$plot.params <- shiny::reactive({
|
|
|
|
# get_plot_options(input$type) |> purrr::pluck(1)
|
|
|
|
# })
|
|
|
|
#
|
|
|
|
# c(output,
|
|
|
|
# list(shiny::renderUI({
|
|
|
|
# columnSelectInput(
|
|
|
|
# inputId = ns("primary"),
|
|
|
|
# data = data,
|
|
|
|
# placeholder = "Select variable",
|
|
|
|
# label = "Response variable",
|
|
|
|
# multiple = FALSE
|
|
|
|
# )
|
|
|
|
# }),
|
|
|
|
# shiny::renderUI({
|
|
|
|
# shiny::req(input$primary)
|
|
|
|
# # browser()
|
|
|
|
#
|
|
|
|
# if (!input$primary %in% names(data())) {
|
|
|
|
# plot_data <- data()[1]
|
|
|
|
# } else {
|
|
|
|
# plot_data <- data()[input$primary]
|
|
|
|
# }
|
|
|
|
#
|
|
|
|
# plots <- possible_plots(
|
|
|
|
# data = plot_data
|
|
|
|
# )
|
|
|
|
#
|
|
|
|
# plots_named <- get_plot_options(plots) |>
|
|
|
|
# lapply(\(.x){
|
|
|
|
# stats::setNames(.x$descr, .x$note)
|
|
|
|
# })
|
|
|
|
#
|
|
|
|
# vectorSelectInput(
|
|
|
|
# inputId = ns("type"),
|
|
|
|
# selected = NULL,
|
|
|
|
# label = shiny::h4("Plot type"),
|
|
|
|
# choices = Reduce(c, plots_named),
|
|
|
|
# multiple = FALSE
|
|
|
|
# )
|
|
|
|
# }),
|
|
|
|
# shiny::renderUI({
|
|
|
|
# shiny::req(input$type)
|
|
|
|
#
|
|
|
|
# cols <- c(
|
|
|
|
# rv$plot.params()[["secondary.extra"]],
|
|
|
|
# all_but(
|
|
|
|
# colnames(subset_types(
|
|
|
|
# data(),
|
|
|
|
# rv$plot.params()[["secondary.type"]]
|
|
|
|
# )),
|
|
|
|
# input$primary
|
|
|
|
# )
|
|
|
|
# )
|
|
|
|
#
|
|
|
|
# columnSelectInput(
|
|
|
|
# inputId = ns("secondary"),
|
|
|
|
# data = data,
|
|
|
|
# selected = cols[1],
|
|
|
|
# placeholder = "Please select",
|
|
|
|
# label = if (isTRUE(rv$plot.params()[["secondary.multi"]])) "Additional variables" else "Secondary variable",
|
|
|
|
# multiple = rv$plot.params()[["secondary.multi"]],
|
|
|
|
# maxItems = rv$plot.params()[["secondary.max"]],
|
|
|
|
# col_subset = cols,
|
|
|
|
# none_label = "No variable"
|
|
|
|
# )
|
|
|
|
# }),
|
|
|
|
# shiny::renderUI({
|
|
|
|
# shiny::req(input$type)
|
|
|
|
# columnSelectInput(
|
|
|
|
# inputId = ns("tertiary"),
|
|
|
|
# data = data,
|
|
|
|
# placeholder = "Please select",
|
|
|
|
# label = "Grouping variable",
|
|
|
|
# multiple = FALSE,
|
|
|
|
# col_subset = c(
|
|
|
|
# "none",
|
|
|
|
# all_but(
|
|
|
|
# colnames(subset_types(
|
|
|
|
# data(),
|
|
|
|
# rv$plot.params()[["tertiary.type"]]
|
|
|
|
# )),
|
|
|
|
# input$primary,
|
|
|
|
# input$secondary
|
|
|
|
# )
|
|
|
|
# ),
|
|
|
|
# none_label = "No stratification"
|
|
|
|
# )
|
|
|
|
# })
|
|
|
|
# )|> setNames(c("primary","type","secondary","tertiary")),keep.null = TRUE)
|
|
|
|
|
2025-02-25 09:51:42 +01:00
|
|
|
output$primary <- shiny::renderUI({
|
|
|
|
columnSelectInput(
|
|
|
|
inputId = ns("primary"),
|
|
|
|
data = data,
|
|
|
|
placeholder = "Select variable",
|
|
|
|
label = "Response variable",
|
|
|
|
multiple = FALSE
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
output$type <- shiny::renderUI({
|
|
|
|
shiny::req(input$primary)
|
|
|
|
# browser()
|
|
|
|
|
|
|
|
if (!input$primary %in% names(data())) {
|
|
|
|
plot_data <- data()[1]
|
|
|
|
} else {
|
|
|
|
plot_data <- data()[input$primary]
|
|
|
|
}
|
|
|
|
|
|
|
|
plots <- possible_plots(
|
|
|
|
data = plot_data
|
|
|
|
)
|
|
|
|
|
2025-03-12 18:27:46 +01:00
|
|
|
plots_named <- get_plot_options(plots) |>
|
|
|
|
lapply(\(.x){
|
2025-03-13 12:41:50 +01:00
|
|
|
stats::setNames(.x$descr, .x$note)
|
2025-03-12 18:27:46 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
vectorSelectInput(
|
2025-02-25 09:51:42 +01:00
|
|
|
inputId = ns("type"),
|
|
|
|
selected = NULL,
|
|
|
|
label = shiny::h4("Plot type"),
|
2025-03-13 12:41:50 +01:00
|
|
|
choices = Reduce(c, plots_named),
|
2025-02-25 09:51:42 +01:00
|
|
|
multiple = FALSE
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
rv$plot.params <- shiny::reactive({
|
2025-03-12 18:27:46 +01:00
|
|
|
get_plot_options(input$type) |> purrr::pluck(1)
|
2025-02-25 09:51:42 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
output$secondary <- shiny::renderUI({
|
|
|
|
shiny::req(input$type)
|
|
|
|
|
2025-03-12 18:27:46 +01:00
|
|
|
cols <- c(
|
|
|
|
rv$plot.params()[["secondary.extra"]],
|
|
|
|
all_but(
|
|
|
|
colnames(subset_types(
|
|
|
|
data(),
|
|
|
|
rv$plot.params()[["secondary.type"]]
|
|
|
|
)),
|
|
|
|
input$primary
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2025-02-25 09:51:42 +01:00
|
|
|
columnSelectInput(
|
|
|
|
inputId = ns("secondary"),
|
|
|
|
data = data,
|
2025-03-13 12:41:50 +01:00
|
|
|
selected = cols[1],
|
|
|
|
placeholder = "Please select",
|
|
|
|
label = if (isTRUE(rv$plot.params()[["secondary.multi"]])) "Additional variables" else "Secondary variable",
|
2025-03-12 18:27:46 +01:00
|
|
|
multiple = rv$plot.params()[["secondary.multi"]],
|
|
|
|
maxItems = rv$plot.params()[["secondary.max"]],
|
|
|
|
col_subset = cols,
|
2025-02-25 09:51:42 +01:00
|
|
|
none_label = "No variable"
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
output$tertiary <- shiny::renderUI({
|
|
|
|
shiny::req(input$type)
|
|
|
|
columnSelectInput(
|
|
|
|
inputId = ns("tertiary"),
|
|
|
|
data = data,
|
2025-03-13 12:41:50 +01:00
|
|
|
placeholder = "Please select",
|
|
|
|
label = "Grouping variable",
|
2025-02-25 09:51:42 +01:00
|
|
|
multiple = FALSE,
|
|
|
|
col_subset = c(
|
|
|
|
"none",
|
|
|
|
all_but(
|
|
|
|
colnames(subset_types(
|
|
|
|
data(),
|
2025-03-12 18:27:46 +01:00
|
|
|
rv$plot.params()[["tertiary.type"]]
|
2025-02-25 09:51:42 +01:00
|
|
|
)),
|
|
|
|
input$primary,
|
|
|
|
input$secondary
|
|
|
|
)
|
|
|
|
),
|
|
|
|
none_label = "No stratification"
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2025-03-13 12:41:50 +01:00
|
|
|
shiny::observeEvent(input$act_plot,
|
|
|
|
{
|
|
|
|
tryCatch(
|
|
|
|
{
|
|
|
|
rv$plot <- create_plot(
|
|
|
|
data = data(),
|
|
|
|
type = rv$plot.params()[["fun"]],
|
|
|
|
x = input$primary,
|
|
|
|
y = input$secondary,
|
|
|
|
z = input$tertiary
|
|
|
|
)
|
|
|
|
},
|
2025-03-20 11:45:37 +01:00
|
|
|
# warning = function(warn) {
|
|
|
|
# showNotification(paste0(warn), type = "warning")
|
|
|
|
# },
|
2025-03-13 12:41:50 +01:00
|
|
|
error = function(err) {
|
|
|
|
showNotification(paste0(err), type = "err")
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
ignoreInit = TRUE
|
|
|
|
)
|
2025-02-25 09:51:42 +01:00
|
|
|
|
|
|
|
output$plot <- shiny::renderPlot({
|
2025-03-13 12:41:50 +01:00
|
|
|
shiny::req(rv$plot)
|
|
|
|
rv$plot
|
2025-02-25 09:51:42 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
output$download_plot <- shiny::downloadHandler(
|
|
|
|
filename = shiny::reactive({
|
|
|
|
paste0("plot.", input$plot_type)
|
|
|
|
}),
|
|
|
|
content = function(file) {
|
|
|
|
shiny::withProgress(message = "Drawing the plot. Hold on for a moment..", {
|
2025-03-05 21:13:06 +01:00
|
|
|
ggplot2::ggsave(
|
|
|
|
filename = file,
|
2025-03-13 12:41:50 +01:00
|
|
|
plot = rv$plot,
|
2025-03-05 21:13:06 +01:00
|
|
|
width = input$width,
|
|
|
|
height = input$height,
|
|
|
|
dpi = 300,
|
|
|
|
units = "mm", scale = 2
|
|
|
|
)
|
2025-02-25 09:51:42 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
shiny::observe(
|
|
|
|
return(rv$plot)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#' Select all from vector but
|
|
|
|
#'
|
|
|
|
#' @param data vector
|
|
|
|
#' @param ... exclude
|
|
|
|
#'
|
2025-03-05 21:13:06 +01:00
|
|
|
#' @returns vector
|
2025-02-25 09:51:42 +01:00
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' all_but(1:10, c(2, 3), 11, 5)
|
|
|
|
all_but <- function(data, ...) {
|
|
|
|
data[!data %in% c(...)]
|
|
|
|
}
|
|
|
|
|
|
|
|
#' Easily subset by data type function
|
|
|
|
#'
|
|
|
|
#' @param data data
|
|
|
|
#' @param types desired types
|
|
|
|
#' @param type.fun function to get type. Default is outcome_type
|
|
|
|
#'
|
2025-03-05 21:13:06 +01:00
|
|
|
#' @returns vector
|
2025-02-25 09:51:42 +01:00
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' default_parsing(mtcars) |> subset_types("ordinal")
|
2025-03-20 11:45:37 +01:00
|
|
|
#' default_parsing(mtcars) |> subset_types(c("dichotomous", "ordinal" ,"categorical"))
|
2025-02-25 09:51:42 +01:00
|
|
|
#' #' default_parsing(mtcars) |> subset_types("factor",class)
|
2025-03-20 11:45:37 +01:00
|
|
|
subset_types <- function(data, types, type.fun = data_type) {
|
2025-02-25 09:51:42 +01:00
|
|
|
data[sapply(data, type.fun) %in% types]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#' Implemented functions
|
|
|
|
#'
|
|
|
|
#' @description
|
|
|
|
#' Library of supported functions. The list name and "descr" element should be
|
|
|
|
#' unique for each element on list.
|
|
|
|
#'
|
|
|
|
#' - descr: Plot description
|
|
|
|
#'
|
|
|
|
#' - primary.type: Primary variable data type (continuous, dichotomous or ordinal)
|
|
|
|
#'
|
|
|
|
#' - secondary.type: Secondary variable data type (continuous, dichotomous or ordinal)
|
|
|
|
#'
|
|
|
|
#' - secondary.extra: "none" or NULL to have option to choose none.
|
|
|
|
#'
|
|
|
|
#' - tertiary.type: Tertiary variable data type (continuous, dichotomous or ordinal)
|
|
|
|
#'
|
|
|
|
#'
|
|
|
|
#' @returns list
|
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' supported_plots() |> str()
|
|
|
|
supported_plots <- function() {
|
|
|
|
list(
|
|
|
|
plot_hbars = list(
|
2025-03-12 18:27:46 +01:00
|
|
|
fun = "plot_hbars",
|
2025-03-05 21:13:06 +01:00
|
|
|
descr = "Stacked horizontal bars",
|
|
|
|
note = "A classical way of visualising the distribution of an ordinal scale like the modified Ranking Scale and known as Grotta bars",
|
2025-03-20 11:45:37 +01:00
|
|
|
primary.type = c("dichotomous", "ordinal" ,"categorical"),
|
|
|
|
secondary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-12 18:27:46 +01:00
|
|
|
secondary.multi = FALSE,
|
2025-03-20 11:45:37 +01:00
|
|
|
tertiary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-02-25 09:51:42 +01:00
|
|
|
secondary.extra = "none"
|
|
|
|
),
|
|
|
|
plot_violin = list(
|
2025-03-12 18:27:46 +01:00
|
|
|
fun = "plot_violin",
|
2025-02-25 09:51:42 +01:00
|
|
|
descr = "Violin plot",
|
2025-03-05 21:13:06 +01:00
|
|
|
note = "A modern alternative to the classic boxplot to visualise data distribution",
|
2025-03-20 11:45:37 +01:00
|
|
|
primary.type = c("continuous", "dichotomous", "ordinal" ,"categorical"),
|
|
|
|
secondary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-12 18:27:46 +01:00
|
|
|
secondary.multi = FALSE,
|
|
|
|
secondary.extra = "none",
|
2025-03-20 11:45:37 +01:00
|
|
|
tertiary.type = c("dichotomous", "ordinal" ,"categorical")
|
2025-02-25 09:51:42 +01:00
|
|
|
),
|
2025-03-11 13:42:57 +01:00
|
|
|
# plot_ridge = list(
|
|
|
|
# descr = "Ridge plot",
|
|
|
|
# note = "An alternative option to visualise data distribution",
|
|
|
|
# primary.type = "continuous",
|
2025-03-20 11:45:37 +01:00
|
|
|
# secondary.type = c("dichotomous", "ordinal" ,"categorical"),
|
|
|
|
# tertiary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-11 13:42:57 +01:00
|
|
|
# secondary.extra = NULL
|
|
|
|
# ),
|
2025-03-05 21:13:06 +01:00
|
|
|
plot_sankey = list(
|
2025-03-12 18:27:46 +01:00
|
|
|
fun = "plot_sankey",
|
2025-03-05 21:13:06 +01:00
|
|
|
descr = "Sankey plot",
|
|
|
|
note = "A way of visualising change between groups",
|
2025-03-20 11:45:37 +01:00
|
|
|
primary.type = c("dichotomous", "ordinal" ,"categorical"),
|
|
|
|
secondary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-12 18:27:46 +01:00
|
|
|
secondary.multi = FALSE,
|
|
|
|
secondary.extra = NULL,
|
2025-03-20 11:45:37 +01:00
|
|
|
tertiary.type = c("dichotomous", "ordinal" ,"categorical")
|
2025-03-05 21:13:06 +01:00
|
|
|
),
|
2025-02-25 09:51:42 +01:00
|
|
|
plot_scatter = list(
|
2025-03-12 18:27:46 +01:00
|
|
|
fun = "plot_scatter",
|
2025-02-25 09:51:42 +01:00
|
|
|
descr = "Scatter plot",
|
2025-03-05 21:13:06 +01:00
|
|
|
note = "A classic way of showing the association between to variables",
|
2025-02-25 09:51:42 +01:00
|
|
|
primary.type = "continuous",
|
2025-03-20 11:45:37 +01:00
|
|
|
secondary.type = c("continuous", "ordinal" ,"categorical"),
|
2025-03-12 18:27:46 +01:00
|
|
|
secondary.multi = FALSE,
|
2025-03-20 11:45:37 +01:00
|
|
|
tertiary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-12 18:27:46 +01:00
|
|
|
secondary.extra = NULL
|
|
|
|
),
|
2025-03-19 13:10:56 +01:00
|
|
|
plot_box = list(
|
|
|
|
fun = "plot_box",
|
|
|
|
descr = "Box plot",
|
|
|
|
note = "A classic way to plot data distribution by groups",
|
2025-03-20 11:45:37 +01:00
|
|
|
primary.type = c("continuous", "dichotomous", "ordinal" ,"categorical"),
|
|
|
|
secondary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-19 13:10:56 +01:00
|
|
|
secondary.multi = FALSE,
|
2025-03-20 11:45:37 +01:00
|
|
|
tertiary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-03-19 13:10:56 +01:00
|
|
|
secondary.extra = "none"
|
|
|
|
),
|
2025-03-12 18:27:46 +01:00
|
|
|
plot_euler = list(
|
|
|
|
fun = "plot_euler",
|
|
|
|
descr = "Euler diagram",
|
|
|
|
note = "Generate area-proportional Euler diagrams to display set relationships",
|
|
|
|
primary.type = "dichotomous",
|
|
|
|
secondary.type = "dichotomous",
|
|
|
|
secondary.multi = TRUE,
|
|
|
|
secondary.max = 4,
|
2025-03-20 11:45:37 +01:00
|
|
|
tertiary.type = c("dichotomous", "ordinal" ,"categorical"),
|
2025-02-25 09:51:42 +01:00
|
|
|
secondary.extra = NULL
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#' Get possible regression models
|
|
|
|
#'
|
|
|
|
#' @param data data
|
|
|
|
#'
|
|
|
|
#' @returns character vector
|
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' mtcars |>
|
|
|
|
#' default_parsing() |>
|
|
|
|
#' dplyr::pull("cyl") |>
|
|
|
|
#' possible_plots()
|
|
|
|
#'
|
|
|
|
#' mtcars |>
|
|
|
|
#' default_parsing() |>
|
|
|
|
#' dplyr::select("mpg") |>
|
|
|
|
#' possible_plots()
|
|
|
|
possible_plots <- function(data) {
|
|
|
|
# browser()
|
2025-03-20 13:13:14 +01:00
|
|
|
# data <- if (is.reactive(data)) data() else data
|
2025-02-25 09:51:42 +01:00
|
|
|
if (is.data.frame(data)) {
|
|
|
|
data <- data[[1]]
|
|
|
|
}
|
|
|
|
|
2025-03-20 11:45:37 +01:00
|
|
|
type <- data_type(data)
|
2025-02-25 09:51:42 +01:00
|
|
|
|
|
|
|
if (type == "unknown") {
|
|
|
|
out <- type
|
|
|
|
} else {
|
|
|
|
out <- supported_plots() |>
|
|
|
|
lapply(\(.x){
|
|
|
|
if (type %in% .x$primary.type) {
|
|
|
|
.x$descr
|
|
|
|
}
|
|
|
|
}) |>
|
|
|
|
unlist()
|
|
|
|
}
|
|
|
|
unname(out)
|
|
|
|
}
|
|
|
|
|
|
|
|
#' Get the function options based on the selected function description
|
|
|
|
#'
|
|
|
|
#' @param data vector
|
|
|
|
#'
|
|
|
|
#' @returns list
|
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' ls <- mtcars |>
|
|
|
|
#' default_parsing() |>
|
|
|
|
#' dplyr::pull(mpg) |>
|
|
|
|
#' possible_plots() |>
|
|
|
|
#' (\(.x){
|
|
|
|
#' .x[[1]]
|
|
|
|
#' })() |>
|
|
|
|
#' get_plot_options()
|
|
|
|
get_plot_options <- function(data) {
|
|
|
|
descrs <- supported_plots() |>
|
|
|
|
lapply(\(.x){
|
|
|
|
.x$descr
|
|
|
|
}) |>
|
|
|
|
unlist()
|
|
|
|
supported_plots() |>
|
|
|
|
(\(.x){
|
|
|
|
.x[match(data, descrs)]
|
|
|
|
})()
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#' Wrapper to create plot based on provided type
|
|
|
|
#'
|
2025-03-11 13:42:57 +01:00
|
|
|
#' @param data data.frame
|
|
|
|
#' @param x primary variable
|
|
|
|
#' @param y secondary variable
|
|
|
|
#' @param z tertiary variable
|
2025-02-25 09:51:42 +01:00
|
|
|
#' @param type plot type (derived from possible_plots() and matches custom function)
|
|
|
|
#' @param ... ignored for now
|
|
|
|
#'
|
2025-03-05 21:13:06 +01:00
|
|
|
#' @name data-plots
|
|
|
|
#'
|
|
|
|
#' @returns ggplot2 object
|
2025-02-25 09:51:42 +01:00
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' create_plot(mtcars, "plot_violin", "mpg", "cyl")
|
|
|
|
create_plot <- function(data, type, x, y, z = NULL, ...) {
|
2025-03-12 18:27:46 +01:00
|
|
|
if (!any(y %in% names(data))) {
|
2025-02-25 09:51:42 +01:00
|
|
|
y <- NULL
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!z %in% names(data)) {
|
|
|
|
z <- NULL
|
|
|
|
}
|
|
|
|
|
|
|
|
do.call(
|
|
|
|
type,
|
|
|
|
list(data, x, y, z, ...)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#' Print label, and if missing print variable name
|
|
|
|
#'
|
|
|
|
#' @param data vector or data frame
|
2025-03-11 13:42:57 +01:00
|
|
|
#' @param var variable name. Optional.
|
2025-02-25 09:51:42 +01:00
|
|
|
#'
|
|
|
|
#' @returns character string
|
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
|
|
|
#' mtcars |> get_label(var = "mpg")
|
2025-03-05 21:13:06 +01:00
|
|
|
#' mtcars |> get_label()
|
2025-02-25 09:51:42 +01:00
|
|
|
#' mtcars$mpg |> get_label()
|
|
|
|
#' gtsummary::trial |> get_label(var = "trt")
|
|
|
|
#' 1:10 |> get_label()
|
|
|
|
get_label <- function(data, var = NULL) {
|
2025-03-20 13:13:14 +01:00
|
|
|
# data <- if (is.reactive(data)) data() else data
|
2025-03-11 13:42:57 +01:00
|
|
|
if (!is.null(var) & is.data.frame(data)) {
|
2025-02-25 09:51:42 +01:00
|
|
|
data <- data[[var]]
|
|
|
|
}
|
|
|
|
out <- REDCapCAST::get_attr(data = data, attr = "label")
|
|
|
|
if (is.na(out)) {
|
|
|
|
if (is.null(var)) {
|
|
|
|
out <- deparse(substitute(data))
|
|
|
|
} else {
|
2025-03-05 21:13:06 +01:00
|
|
|
if (is.symbol(var)) {
|
|
|
|
out <- gsub('\"', "", deparse(substitute(var)))
|
|
|
|
} else {
|
|
|
|
out <- var
|
|
|
|
}
|
2025-02-25 09:51:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-05 21:13:06 +01:00
|
|
|
#' Line breaking at given number of characters for nicely plotting labels
|
|
|
|
#'
|
2025-03-11 13:42:57 +01:00
|
|
|
#' @param data string
|
|
|
|
#' @param lineLength maximum line length
|
|
|
|
#' @param fixed flag to force split at exactly the value given in lineLength.
|
|
|
|
#' Default is FALSE, only splitting at spaces.
|
2025-03-05 21:13:06 +01:00
|
|
|
#'
|
2025-03-11 13:42:57 +01:00
|
|
|
#' @returns character string
|
2025-03-05 21:13:06 +01:00
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
#' @examples
|
2025-03-11 13:42:57 +01:00
|
|
|
#' "Lorem ipsum... you know the routine" |> line_break()
|
2025-03-12 18:27:46 +01:00
|
|
|
#' paste(sample(letters[1:10], 100, TRUE), collapse = "") |> line_break(fixed = TRUE)
|
2025-03-11 13:42:57 +01:00
|
|
|
line_break <- function(data, lineLength = 20, fixed = FALSE) {
|
|
|
|
if (isTRUE(force)) {
|
|
|
|
gsub(paste0("(.{1,", lineLength, "})(\\s|[[:alnum:]])"), "\\1\n", data)
|
|
|
|
} else {
|
|
|
|
paste(strwrap(data, lineLength), collapse = "\n")
|
|
|
|
}
|
2025-03-05 21:13:06 +01:00
|
|
|
## https://stackoverflow.com/a/29847221
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-19 13:10:56 +01:00
|
|
|
#' Wrapping
|
|
|
|
#'
|
|
|
|
#' @param data list of ggplot2 objects
|
|
|
|
#' @param tag_levels passed to patchwork::plot_annotation if given. Default is NULL
|
|
|
|
#'
|
|
|
|
#' @returns list of ggplot2 objects
|
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
wrap_plot_list <- function(data, tag_levels = NULL) {
|
|
|
|
if (ggplot2::is.ggplot(data[[1]])) {
|
|
|
|
if (length(data) > 1) {
|
|
|
|
out <- data |>
|
|
|
|
(\(.x){
|
|
|
|
if (rlang::is_named(.x)) {
|
|
|
|
purrr::imap(.x, \(.y, .i){
|
|
|
|
.y + ggplot2::ggtitle(.i)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
.x
|
|
|
|
}
|
|
|
|
})() |>
|
|
|
|
allign_axes() |>
|
|
|
|
patchwork::wrap_plots(guides = "collect", axes = "collect", axis_titles = "collect")
|
|
|
|
if (!is.null(tag_levels)) {
|
|
|
|
out <- out + patchwork::plot_annotation(tag_levels = tag_levels)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
out <- data
|
|
|
|
}
|
2025-03-13 12:41:50 +01:00
|
|
|
} else {
|
2025-03-19 13:10:56 +01:00
|
|
|
cli::cli_abort("Can only wrap lists of {.cls ggplot} objects")
|
2025-03-13 12:41:50 +01:00
|
|
|
}
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-03-19 13:10:56 +01:00
|
|
|
#' Alligns axes between plots
|
|
|
|
#'
|
|
|
|
#' @param ... ggplot2 objects or list of ggplot2 objects
|
|
|
|
#'
|
|
|
|
#' @returns list of ggplot2 objects
|
|
|
|
#' @export
|
|
|
|
#'
|
2025-03-13 12:41:50 +01:00
|
|
|
allign_axes <- function(...) {
|
|
|
|
# https://stackoverflow.com/questions/62818776/get-axis-limits-from-ggplot-object
|
|
|
|
# https://github.com/thomasp85/patchwork/blob/main/R/plot_multipage.R#L150
|
|
|
|
if (ggplot2::is.ggplot(..1)) {
|
|
|
|
p <- list(...)
|
|
|
|
} else if (is.list(..1)) {
|
|
|
|
p <- ..1
|
|
|
|
} else {
|
|
|
|
cli::cli_abort("Can only align {.cls ggplot} objects or a list of them")
|
|
|
|
}
|
|
|
|
|
2025-03-19 13:10:56 +01:00
|
|
|
yr <- clean_common_axis(p, "y")
|
2025-03-13 12:41:50 +01:00
|
|
|
|
2025-03-19 13:10:56 +01:00
|
|
|
xr <- clean_common_axis(p, "x")
|
2025-03-13 12:41:50 +01:00
|
|
|
|
2025-03-20 11:45:37 +01:00
|
|
|
suppressWarnings({
|
|
|
|
p |> purrr::map(~ .x + ggplot2::xlim(xr) + ggplot2::ylim(yr))
|
|
|
|
})
|
2025-03-13 12:41:50 +01:00
|
|
|
}
|
2025-03-19 13:10:56 +01:00
|
|
|
|
|
|
|
#' Extract and clean axis ranges
|
|
|
|
#'
|
|
|
|
#' @param p plot
|
|
|
|
#' @param axis axis. x or y.
|
|
|
|
#'
|
|
|
|
#' @returns vector
|
|
|
|
#' @export
|
|
|
|
#'
|
|
|
|
clean_common_axis <- function(p, axis) {
|
|
|
|
purrr::map(p, ~ ggplot2::layer_scales(.x)[[axis]]$get_limits()) |>
|
|
|
|
unlist() |>
|
|
|
|
(\(.x){
|
|
|
|
if (is.numeric(.x)) {
|
|
|
|
range(.x)
|
|
|
|
} else {
|
2025-03-20 11:45:37 +01:00
|
|
|
as.character(.x)
|
2025-03-19 13:10:56 +01:00
|
|
|
}
|
|
|
|
})() |>
|
|
|
|
unique()
|
|
|
|
}
|