code export clean up
Some checks are pending
pkgdown.yaml / pkgdown (push) Waiting to run

This commit is contained in:
Andreas Gammelgaard Damsbo 2025-04-10 15:46:42 +02:00
parent 347490605f
commit 8469a5ca64
No known key found for this signature in database
13 changed files with 1123 additions and 273 deletions

View file

@ -133,12 +133,14 @@ importFrom(rlang,sym)
importFrom(rlang,syms)
importFrom(shiny,NS)
importFrom(shiny,actionButton)
importFrom(shiny,actionLink)
importFrom(shiny,bindEvent)
importFrom(shiny,checkboxInput)
importFrom(shiny,column)
importFrom(shiny,fluidRow)
importFrom(shiny,getDefaultReactiveDomain)
importFrom(shiny,icon)
importFrom(shiny,is.reactive)
importFrom(shiny,isTruthy)
importFrom(shiny,modalDialog)
importFrom(shiny,moduleServer)
@ -147,6 +149,7 @@ importFrom(shiny,observeEvent)
importFrom(shiny,plotOutput)
importFrom(shiny,reactive)
importFrom(shiny,reactiveValues)
importFrom(shiny,removeUI)
importFrom(shiny,renderPlot)
importFrom(shiny,req)
importFrom(shiny,restoreInput)
@ -171,4 +174,5 @@ importFrom(toastui,grid_colorbar)
importFrom(toastui,grid_columns)
importFrom(toastui,renderDatagrid)
importFrom(toastui,renderDatagrid2)
importFrom(utils,data)
importFrom(utils,type.convert)

View file

@ -1 +1 @@
app_version <- function()'Version: 25.4.1.250409_1216'
app_version <- function()'Version: 25.4.1.250410_1545'

View file

@ -136,14 +136,18 @@ cut.POSIXt <- function(x, breaks, right = FALSE, include.lowest = TRUE, start.on
}
if (identical(breaks, "weekday")) {
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday")
days <- c(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday"
)
if (!start.on.monday) {
days <- days[c(7, 1:6)]
}
out <- factor(weekdays(x), levels = days) |> forcats::fct_drop()
} else if (identical(breaks, "month_only")) {
ms <- paste0("1970-",1:12,"-01") |> as.Date() |> months()
ms <- paste0("1970-", 1:12, "-01") |>
as.Date() |>
months()
out <- factor(months(x), levels = ms) |> forcats::fct_drop()
} else {
@ -182,14 +186,18 @@ cut.POSIXct <- cut.POSIXt
#' as.Date(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(breaks = "weekday")
cut.Date <- function(x, breaks, start.on.monday = TRUE, ...) {
if (identical(breaks, "weekday")) {
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday")
days <- c(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday"
)
if (!start.on.monday) {
days <- days[c(7, 1:6)]
}
out <- factor(weekdays(x), levels = days) |> forcats::fct_drop()
} else if (identical(breaks, "month_only")) {
ms <- paste0("1970-",1:12,"-01") |> as.Date() |> months()
ms <- paste0("1970-", 1:12, "-01") |>
as.Date() |>
months()
out <- factor(months(x), levels = ms) |> forcats::fct_drop()
} else {
@ -497,12 +505,16 @@ cut_variable_server <- function(id, data_r = reactive(NULL)) {
data_cutted_r <- reactive({
data <- req(data_r())
variable <- req(input$variable)
data[[paste0(variable, "_cut")]] <- cut(
new_variable <- data.frame(cut(
x = data[[variable]],
breaks = if (input$method %in% c("day", "weekday", "week", "month", "month_only", "quarter", "year", "hour")) input$method else breaks_r()$brks,
include.lowest = input$include_lowest,
right = input$right
)
)) |> setNames(paste0(variable, "_cut"))
data <- dplyr::bind_cols(data, new_variable, .name_repair = "unique_quiet")
code <- call2(
"mutate",
!!!set_names(

View file

@ -209,18 +209,28 @@ file_export <- function(data, output.format = c("df", "teal", "list"), filename,
#' mtcars |>
#' default_parsing() |>
#' str()
#' head(starwars,5) |> str()
#' starwars |>
#' default_parsing() |>
#' head(5) |>
#' str()
default_parsing <- function(data) {
name_labels <- lapply(data, \(.x) REDCapCAST::get_attr(.x, attr = "label"))
out <- data |>
setNames(make.names(names(data), unique = TRUE)) |>
## Temporary step to avoid nested list and crashing
remove_nested_list() |>
REDCapCAST::parse_data() |>
REDCapCAST::as_factor() |>
REDCapCAST::numchar2fct(numeric.threshold = 8, character.throshold = 10) |>
REDCapCAST::as_logical() |>
REDCapCAST::fct_drop()
purrr::map2(out, name_labels, \(.x, .l){
purrr::map2(
out,
name_labels[names(name_labels) %in% names(out)],
\(.x, .l){
if (!(is.na(.l) | .l == "")) {
REDCapCAST::set_attr(.x, .l, attr = "label")
} else {
@ -228,7 +238,8 @@ default_parsing <- function(data) {
.x
}
# REDCapCAST::set_attr(data = .x, label = .l,attr = "label", overwrite = FALSE)
}) |> dplyr::bind_cols()
}
) |> dplyr::bind_cols()
}
#' Remove NA labels
@ -425,9 +436,25 @@ merge_expression <- function(data){
#' list(
#' rlang::call2(.fn = "select", !!!list(c("cyl", "disp")), .ns = "dplyr"),
#' rlang::call2(.fn = "default_parsing", .ns = "FreesearchR")
#' ) |> merge_expression() |> expression_string()
#' ) |>
#' merge_expression() |>
#' expression_string()
expression_string <- function(data, assign.str = "data <- ") {
out <- paste0(assign.str, gsub("%>%", "|>\n", paste(gsub('"', "'", deparse(data)), collapse = "")))
gsub(" ", "", out)
}
#' Very simple function to remove nested lists, lik ewhen uploading .rds
#'
#' @param data data
#'
#' @returns data.frame
#' @export
#'
#' @examples
#' dplyr::tibble(a = 1:10, b = rep(list("a"), 10)) |> remove_nested_list()
#' dplyr::tibble(a = 1:10, b = rep(list(c("a", "b")), 10)) |> as.data.frame()
remove_nested_list <- function(data) {
data[!sapply(data, is.list)]
}

338
R/import-global-env-mod.R Normal file
View file

@ -0,0 +1,338 @@
#' @title Import data from an Environment
#'
#' @description Let the user select a dataset from its own environment or from a package's environment.
#'
#' @param id Module's ID.
#' @param globalenv Search for data in Global environment.
#' @param packages Name of packages in which to search data.
#' @param title Module's title, if `TRUE` use the default title,
#' use `NULL` for no title or a `shiny.tag` for a custom one.
#'
#'
#' @export
#'
#' @name import-globalenv
#'
#' @importFrom htmltools tags
#' @importFrom shiny NS actionButton icon textInput
#'
#' @example examples/from-globalenv.R
import_globalenv_ui <- function(id,
globalenv = TRUE,
packages = get_data_packages(),
title = TRUE) {
ns <- NS(id)
choices <- list()
if (isTRUE(globalenv)) {
choices <- append(choices, "Global Environment")
}
if (!is.null(packages)) {
choices <- append(choices, list(Packages = as.character(packages)))
}
if (isTRUE(globalenv)) {
selected <- "Global Environment"
} else {
selected <- packages[1]
}
if (isTRUE(title)) {
title <- tags$h4(
i18n("Import a dataset from an environment"),
class = "datamods-title"
)
}
tags$div(
class = "datamods-import",
datamods:::html_dependency_datamods(),
title,
shinyWidgets::pickerInput(
inputId = ns("data"),
label = i18n("Select a data.frame:"),
choices = NULL,
options = list(title = i18n("List of data.frame...")),
width = "100%"
),
shinyWidgets::pickerInput(
inputId = ns("env"),
label = i18n("Select an environment in which to search:"),
choices = choices,
selected = selected,
width = "100%",
options = list(
"title" = i18n("Select environment"),
"live-search" = TRUE,
"size" = 10
)
),
tags$div(
id = ns("import-placeholder"),
alert(
id = ns("import-result"),
status = "info",
tags$b(i18n("No data selected!")),
i18n("Use a data.frame from your environment or from the environment of a package."),
dismissible = TRUE
)
),
uiOutput(
outputId = ns("container_valid_btn"),
style = "margin-top: 20px;"
)
)
}
#' @param btn_show_data Display or not a button to display data in a modal window if import is successful.
#' @param show_data_in Where to display data: in a `"popup"` or in a `"modal"` window.
#' @param trigger_return When to update selected data:
#' `"button"` (when user click on button) or
#' `"change"` (each time user select a dataset in the list).
#' @param return_class Class of returned data: `data.frame`, `data.table`, `tbl_df` (tibble) or `raw`.
#' @param reset A `reactive` function that when triggered resets the data.
#'
#' @export
#'
#' @importFrom shiny moduleServer reactiveValues observeEvent reactive removeUI is.reactive icon actionLink isTruthy
#' @importFrom htmltools tags tagList
#'
#' @rdname import-globalenv
import_globalenv_server <- function(id,
btn_show_data = TRUE,
show_data_in = c("popup", "modal"),
trigger_return = c("button", "change"),
return_class = c("data.frame", "data.table", "tbl_df", "raw"),
reset = reactive(NULL)) {
trigger_return <- match.arg(trigger_return)
return_class <- match.arg(return_class)
module <- function(input, output, session) {
ns <- session$ns
imported_rv <- reactiveValues(data = NULL, name = NULL)
temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL)
observeEvent(reset(), {
temporary_rv$data <- NULL
temporary_rv$name <- NULL
temporary_rv$status <- NULL
})
output$container_valid_btn <- renderUI({
if (identical(trigger_return, "button")) {
button_import()
}
})
observeEvent(input$env, {
if (identical(input$env, "Global Environment")) {
choices <- datamods:::search_obj("data.frame")
} else {
choices <- datamods:::list_pkg_data(input$env)
}
if (is.null(choices)) {
choices <- i18n("No data.frame here...")
choicesOpt <- list(disabled = TRUE)
} else {
choicesOpt <- list(
subtext = get_dimensions(choices)
)
}
temporary_rv$package <- attr(choices, "package")
shinyWidgets::updatePickerInput(
session = session,
inputId = ns("data"),
choices = choices,
choicesOpt = choicesOpt
)
})
observeEvent(input$trigger, {
if (identical(trigger_return, "change")) {
hideUI(selector = paste0("#", ns("container_valid_btn")))
}
})
observeEvent(input$data, {
if (!isTruthy(input$data)) {
toggle_widget(inputId = "confirm", enable = FALSE)
insert_alert(
selector = ns("import"),
status = "info",
tags$b(i18n("No data selected!")),
i18n("Use a data.frame from your environment or from the environment of a package.")
)
} else {
name_df <- input$data
if (!is.null(temporary_rv$package)) {
attr(name_df, "package") <- temporary_rv$package
}
imported <- try(get_env_data(name_df), silent = TRUE)
if (inherits(imported, "try-error") || NROW(imported) < 1) {
toggle_widget(inputId = "confirm", enable = FALSE)
insert_error(mssg = i18n(attr(imported, "condition")$message))
temporary_rv$status <- "error"
temporary_rv$data <- NULL
temporary_rv$name <- NULL
} else {
toggle_widget(inputId = "confirm", enable = TRUE)
insert_alert(
selector = ns("import"),
status = "success",
make_success_alert(
imported,
trigger_return = trigger_return,
btn_show_data = btn_show_data
)
)
pkg <- attr(name_df, "package")
if (!is.null(pkg)) {
name <- paste(pkg, input$data, sep = "::")
} else {
name <- input$data
}
name <- trimws(sub("\\(([^\\)]+)\\)", "", name))
temporary_rv$status <- "success"
temporary_rv$data <- imported
temporary_rv$name <- name
}
}
}, ignoreInit = TRUE, ignoreNULL = FALSE)
observeEvent(input$see_data, {
show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in)
})
observeEvent(input$confirm, {
imported_rv$data <- temporary_rv$data
imported_rv$name <- temporary_rv$name
})
return(list(
status = reactive(temporary_rv$status),
name = reactive(temporary_rv$name),
data = reactive(datamods:::as_out(temporary_rv$data, return_class))
))
}
moduleServer(
id = id,
module = module
)
}
# utils -------------------------------------------------------------------
#' Get packages containing datasets
#'
#' @return a character vector of packages names
#' @export
#'
#' @importFrom utils data
#'
#' @examples
#' if (interactive()) {
#'
#' get_data_packages()
#'
#' }
get_data_packages <- function() {
suppressWarnings({
pkgs <- data(package = .packages(all.available = TRUE))
})
unique(pkgs$results[, 1])
}
#' List dataset contained in a package
#'
#' @param pkg Name of the package, must be installed.
#'
#' @return a \code{character} vector or \code{NULL}.
#' @export
#'
#' @importFrom utils data
#'
#' @examples
#'
#' list_pkg_data("ggplot2")
list_pkg_data <- function(pkg) {
if (isTRUE(requireNamespace(pkg, quietly = TRUE))) {
list_data <- data(package = pkg, envir = environment())$results[, "Item"]
list_data <- sort(list_data)
attr(list_data, "package") <- pkg
if (length(list_data) < 1) {
NULL
} else {
unname(list_data)
}
} else {
NULL
}
}
#' @importFrom utils data
get_env_data <- function(obj, env = globalenv()) {
pkg <- attr(obj, "package")
re <- regexpr(pattern = "\\(([^\\)]+)\\)", text = obj)
obj_ <- substr(x = obj, start = re + 1, stop = re + attr(re, "match.length") - 2)
obj <- gsub(pattern = "\\s.*", replacement = "", x = obj)
if (obj %in% ls(name = env)) {
get(x = obj, envir = env)
} else if (!is.null(pkg) && !identical(pkg, "")) {
res <- suppressWarnings(try(
get(utils::data(list = obj, package = pkg, envir = environment())), silent = TRUE
))
if (!inherits(res, "try-error"))
return(res)
data(list = obj_, package = pkg, envir = environment())
get(obj, envir = environment())
} else {
NULL
}
}
get_dimensions <- function(objs) {
if (is.null(objs))
return(NULL)
dataframes_dims <- Map(
f = function(name, pkg) {
attr(name, "package") <- pkg
tmp <- suppressWarnings(get_env_data(name))
if (is.data.frame(tmp)) {
sprintf("%d obs. of %d variables", nrow(tmp), ncol(tmp))
} else {
i18n("Not a data.frame")
}
},
name = objs,
pkg = if (!is.null(attr(objs, "package"))) {
attr(objs, "package")
} else {
character(1)
}
)
unlist(dataframes_dims)
}

View file

@ -655,7 +655,7 @@ regression_model_uv_list <- function(data,
## This is the very long version
## Handles deeply nested glue string
code <- glue::glue("dplyr::select(data,{paste0(paste(names(data[c(outcome.str, .var)]),collapse=','))})|>\nFreesearchR::regression_model({list2str(modifyList(parameters,list(formula.str = glue::glue(gsub('vars','.var',formula.str.c)))))})")
code <- glue::glue("FreesearchR::regression_model({list2str(modifyList(parameters,list(formula.str = glue::glue(gsub('vars','.var',formula.str.c)))))})")
REDCapCAST::set_attr(out, code, "code")
})

View file

@ -154,6 +154,7 @@ update_variables_server <- function(id,
updated_data$list_select <- NULL
updated_data$list_mutate <- NULL
updated_data$list_relabel <- NULL
# shiny::req(updated_data$x)
data <- data_r()
new_selections <- input$row_selected
if (length(new_selections) < 1) {
@ -169,11 +170,14 @@ update_variables_server <- function(id,
new_names[is.na(new_names)] <- old_names[is.na(new_names)]
new_names[new_names == ""] <- old_names[new_names == ""]
# browser()
old_label <- data_inputs$label
new_label <- data_inputs$label_toset
new_label[new_label == "New label"] <- ""
new_label[is.na(new_label)] <- old_label[is.na(new_label)]
new_label[new_label == ""] <- old_label[new_label == ""]
new_label <- setNames(new_label,new_names)
new_classes <- data_inputs$class_toset
new_classes[new_classes == "Select"] <- NA
@ -247,6 +251,8 @@ update_variables_server <- function(id,
# shiny::observeEvent(input$close,
# {
return(shiny::reactive({
shiny::req(updated_data$x)
# browser()
data <- updated_data$x
code <- list()
if (!is.null(data) && shiny::isTruthy(updated_data$list_mutate) && length(updated_data$list_mutate) > 0) {
@ -259,10 +265,21 @@ update_variables_server <- function(id,
code <- c(code, list(rlang::expr(select(-any_of(c(!!!updated_data$list_select))))))
}
if (!is.null(data) && shiny::isTruthy(updated_data$list_relabel) && length(updated_data$list_relabel) > 0) {
code <- c(code, list(rlang::call2("purrr::map2(list_relabel,
function(.data,.label){
REDCapCAST::set_attr(.data,.label,attr = 'label')
}) |> dplyr::bind_cols(.name_repair = 'unique_quiet')")))
code <- c(
code,
list(
rlang::expr(purrr::imap(.f=function(.data, .name) {
ls <- !!updated_data$list_relabel
ls <- ls[!is.na(ls)]
if (.name %in% names(ls)) {
REDCapCAST::set_attr(.data, ls[.name], attr = "label")
} else {
.data
}
}) %>% dplyr::bind_cols()
)
)
)
}
if (length(code) > 0) {
attr(data, "code") <- Reduce(
@ -309,7 +326,6 @@ update_variables_server <- function(id,
# return(data)
# }))
# })
}
)
}

View file

@ -4,9 +4,7 @@ The current state of the app is considered experimental, and a lot of things are
Below are some (the actual list is quite long and growing) of the planned features and improvements:
- [ ] Stratified analyses
- Additional study designs:
- Additional study designs in regression models (expansion of the regression analysis functionality have been put on hold for now to focus on the more basic use-cases):
- [x] Cross-sectional data analyses
@ -14,6 +12,8 @@ Below are some (the actual list is quite long and growing) of the planned featur
- [ ] Survival analysis
- [ ] Stratified analyses
- More detailed variable browser
- [x] Add histograms for data distribution. 2025-01-16
@ -22,13 +22,13 @@ Below are some (the actual list is quite long and growing) of the planned featur
- More output controls
- [ ] Theming output tables
- [x] ~~Theming output tables~~ The "JAMA" theme is the new standard.
- [ ] Select analyses to include in report
- [x] ~~Select analyses to include in report.~~ Includes characteristics table and regression table if present. No other analyses are intended for the report as of now.
- [x] Export modified data. 2025-01-16
- [ ] Include reproducible code for all steps (maybe not all, but most steps, and the final dataset can be exported)
- [x] Include reproducible code for all steps (maybe not all, but most steps, and the final dataset can be exported) 2025-04-10
- [x] ~~Modify factor levels~~ Factor level modifications is possible through converting factors to numeric > cutting numeric with desired fixed values. 2024-12-12
@ -41,3 +41,15 @@ Below are some (the actual list is quite long and growing) of the planned featur
- [x] Grotta bars for ordianl outcomes (and sankey) 2025-3-17
- [x] Coefficient plotting for regression analyses (forest plot) 2025-2-20
Documentation:
- [ ] Complete getting started page describing all functionality.
- [ ] Streamlined functions documentation
New features:
- [ ] Merge data from multiple sources (this would in itself be a great feature, but not of highest importance)
- [ ] Additional plot types (missingness, *others...*)

View file

@ -10,7 +10,7 @@
#### Current file: /Users/au301842/FreesearchR/R//app_version.R
########
app_version <- function()'Version: 25.4.1.250409_1216'
app_version <- function()'Version: 25.4.1.250410_1545'
########
@ -632,14 +632,18 @@ cut.POSIXt <- function(x, breaks, right = FALSE, include.lowest = TRUE, start.on
}
if (identical(breaks, "weekday")) {
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday")
days <- c(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday"
)
if (!start.on.monday) {
days <- days[c(7, 1:6)]
}
out <- factor(weekdays(x), levels = days) |> forcats::fct_drop()
} else if (identical(breaks, "month_only")) {
ms <- paste0("1970-",1:12,"-01") |> as.Date() |> months()
ms <- paste0("1970-", 1:12, "-01") |>
as.Date() |>
months()
out <- factor(months(x), levels = ms) |> forcats::fct_drop()
} else {
@ -678,14 +682,18 @@ cut.POSIXct <- cut.POSIXt
#' as.Date(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(breaks = "weekday")
cut.Date <- function(x, breaks, start.on.monday = TRUE, ...) {
if (identical(breaks, "weekday")) {
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday")
days <- c(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday"
)
if (!start.on.monday) {
days <- days[c(7, 1:6)]
}
out <- factor(weekdays(x), levels = days) |> forcats::fct_drop()
} else if (identical(breaks, "month_only")) {
ms <- paste0("1970-",1:12,"-01") |> as.Date() |> months()
ms <- paste0("1970-", 1:12, "-01") |>
as.Date() |>
months()
out <- factor(months(x), levels = ms) |> forcats::fct_drop()
} else {
@ -993,12 +1001,16 @@ cut_variable_server <- function(id, data_r = reactive(NULL)) {
data_cutted_r <- reactive({
data <- req(data_r())
variable <- req(input$variable)
data[[paste0(variable, "_cut")]] <- cut(
new_variable <- data.frame(cut(
x = data[[variable]],
breaks = if (input$method %in% c("day", "weekday", "week", "month", "month_only", "quarter", "year", "hour")) input$method else breaks_r()$brks,
include.lowest = input$include_lowest,
right = input$right
)
)) |> setNames(paste0(variable, "_cut"))
data <- dplyr::bind_cols(data, new_variable, .name_repair = "unique_quiet")
code <- call2(
"mutate",
!!!set_names(
@ -2706,18 +2718,28 @@ file_export <- function(data, output.format = c("df", "teal", "list"), filename,
#' mtcars |>
#' default_parsing() |>
#' str()
#' head(starwars,5) |> str()
#' starwars |>
#' default_parsing() |>
#' head(5) |>
#' str()
default_parsing <- function(data) {
name_labels <- lapply(data, \(.x) REDCapCAST::get_attr(.x, attr = "label"))
out <- data |>
setNames(make.names(names(data), unique = TRUE)) |>
## Temporary step to avoid nested list and crashing
remove_nested_list() |>
REDCapCAST::parse_data() |>
REDCapCAST::as_factor() |>
REDCapCAST::numchar2fct(numeric.threshold = 8, character.throshold = 10) |>
REDCapCAST::as_logical() |>
REDCapCAST::fct_drop()
purrr::map2(out, name_labels, \(.x, .l){
purrr::map2(
out,
name_labels[names(name_labels) %in% names(out)],
\(.x, .l){
if (!(is.na(.l) | .l == "")) {
REDCapCAST::set_attr(.x, .l, attr = "label")
} else {
@ -2725,7 +2747,8 @@ default_parsing <- function(data) {
.x
}
# REDCapCAST::set_attr(data = .x, label = .l,attr = "label", overwrite = FALSE)
}) |> dplyr::bind_cols()
}
) |> dplyr::bind_cols()
}
#' Remove NA labels
@ -2922,13 +2945,29 @@ merge_expression <- function(data){
#' list(
#' rlang::call2(.fn = "select", !!!list(c("cyl", "disp")), .ns = "dplyr"),
#' rlang::call2(.fn = "default_parsing", .ns = "FreesearchR")
#' ) |> merge_expression() |> expression_string()
#' ) |>
#' merge_expression() |>
#' expression_string()
expression_string <- function(data, assign.str = "data <- ") {
out <- paste0(assign.str, gsub("%>%", "|>\n", paste(gsub('"', "'", deparse(data)), collapse = "")))
gsub(" ", "", out)
}
#' Very simple function to remove nested lists, lik ewhen uploading .rds
#'
#' @param data data
#'
#' @returns data.frame
#' @export
#'
#' @examples
#' dplyr::tibble(a = 1:10, b = rep(list("a"), 10)) |> remove_nested_list()
#' dplyr::tibble(a = 1:10, b = rep(list(c("a", "b")), 10)) |> as.data.frame()
remove_nested_list <- function(data) {
data[!sapply(data, is.list)]
}
########
#### Current file: /Users/au301842/FreesearchR/R//import-file-ext.R
@ -3519,6 +3558,350 @@ import_file_demo_app <- function() {
}
########
#### Current file: /Users/au301842/FreesearchR/R//import-global-env-mod.R
########
#' @title Import data from an Environment
#'
#' @description Let the user select a dataset from its own environment or from a package's environment.
#'
#' @param id Module's ID.
#' @param globalenv Search for data in Global environment.
#' @param packages Name of packages in which to search data.
#' @param title Module's title, if `TRUE` use the default title,
#' use `NULL` for no title or a `shiny.tag` for a custom one.
#'
#'
#' @export
#'
#' @name import-globalenv
#'
#' @importFrom htmltools tags
#' @importFrom shiny NS actionButton icon textInput
#'
#' @example examples/from-globalenv.R
import_globalenv_ui <- function(id,
globalenv = TRUE,
packages = get_data_packages(),
title = TRUE) {
ns <- NS(id)
choices <- list()
if (isTRUE(globalenv)) {
choices <- append(choices, "Global Environment")
}
if (!is.null(packages)) {
choices <- append(choices, list(Packages = as.character(packages)))
}
if (isTRUE(globalenv)) {
selected <- "Global Environment"
} else {
selected <- packages[1]
}
if (isTRUE(title)) {
title <- tags$h4(
i18n("Import a dataset from an environment"),
class = "datamods-title"
)
}
tags$div(
class = "datamods-import",
datamods:::html_dependency_datamods(),
title,
shinyWidgets::pickerInput(
inputId = ns("data"),
label = i18n("Select a data.frame:"),
choices = NULL,
options = list(title = i18n("List of data.frame...")),
width = "100%"
),
shinyWidgets::pickerInput(
inputId = ns("env"),
label = i18n("Select an environment in which to search:"),
choices = choices,
selected = selected,
width = "100%",
options = list(
"title" = i18n("Select environment"),
"live-search" = TRUE,
"size" = 10
)
),
tags$div(
id = ns("import-placeholder"),
alert(
id = ns("import-result"),
status = "info",
tags$b(i18n("No data selected!")),
i18n("Use a data.frame from your environment or from the environment of a package."),
dismissible = TRUE
)
),
uiOutput(
outputId = ns("container_valid_btn"),
style = "margin-top: 20px;"
)
)
}
#' @param btn_show_data Display or not a button to display data in a modal window if import is successful.
#' @param show_data_in Where to display data: in a `"popup"` or in a `"modal"` window.
#' @param trigger_return When to update selected data:
#' `"button"` (when user click on button) or
#' `"change"` (each time user select a dataset in the list).
#' @param return_class Class of returned data: `data.frame`, `data.table`, `tbl_df` (tibble) or `raw`.
#' @param reset A `reactive` function that when triggered resets the data.
#'
#' @export
#'
#' @importFrom shiny moduleServer reactiveValues observeEvent reactive removeUI is.reactive icon actionLink isTruthy
#' @importFrom htmltools tags tagList
#'
#' @rdname import-globalenv
import_globalenv_server <- function(id,
btn_show_data = TRUE,
show_data_in = c("popup", "modal"),
trigger_return = c("button", "change"),
return_class = c("data.frame", "data.table", "tbl_df", "raw"),
reset = reactive(NULL)) {
trigger_return <- match.arg(trigger_return)
return_class <- match.arg(return_class)
module <- function(input, output, session) {
ns <- session$ns
imported_rv <- reactiveValues(data = NULL, name = NULL)
temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL)
observeEvent(reset(), {
temporary_rv$data <- NULL
temporary_rv$name <- NULL
temporary_rv$status <- NULL
})
output$container_valid_btn <- renderUI({
if (identical(trigger_return, "button")) {
button_import()
}
})
observeEvent(input$env, {
if (identical(input$env, "Global Environment")) {
choices <- datamods:::search_obj("data.frame")
} else {
choices <- datamods:::list_pkg_data(input$env)
}
if (is.null(choices)) {
choices <- i18n("No data.frame here...")
choicesOpt <- list(disabled = TRUE)
} else {
choicesOpt <- list(
subtext = get_dimensions(choices)
)
}
temporary_rv$package <- attr(choices, "package")
shinyWidgets::updatePickerInput(
session = session,
inputId = ns("data"),
choices = choices,
choicesOpt = choicesOpt
)
})
observeEvent(input$trigger, {
if (identical(trigger_return, "change")) {
hideUI(selector = paste0("#", ns("container_valid_btn")))
}
})
observeEvent(input$data, {
if (!isTruthy(input$data)) {
toggle_widget(inputId = "confirm", enable = FALSE)
insert_alert(
selector = ns("import"),
status = "info",
tags$b(i18n("No data selected!")),
i18n("Use a data.frame from your environment or from the environment of a package.")
)
} else {
name_df <- input$data
if (!is.null(temporary_rv$package)) {
attr(name_df, "package") <- temporary_rv$package
}
imported <- try(get_env_data(name_df), silent = TRUE)
if (inherits(imported, "try-error") || NROW(imported) < 1) {
toggle_widget(inputId = "confirm", enable = FALSE)
insert_error(mssg = i18n(attr(imported, "condition")$message))
temporary_rv$status <- "error"
temporary_rv$data <- NULL
temporary_rv$name <- NULL
} else {
toggle_widget(inputId = "confirm", enable = TRUE)
insert_alert(
selector = ns("import"),
status = "success",
make_success_alert(
imported,
trigger_return = trigger_return,
btn_show_data = btn_show_data
)
)
pkg <- attr(name_df, "package")
if (!is.null(pkg)) {
name <- paste(pkg, input$data, sep = "::")
} else {
name <- input$data
}
name <- trimws(sub("\\(([^\\)]+)\\)", "", name))
temporary_rv$status <- "success"
temporary_rv$data <- imported
temporary_rv$name <- name
}
}
}, ignoreInit = TRUE, ignoreNULL = FALSE)
observeEvent(input$see_data, {
show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in)
})
observeEvent(input$confirm, {
imported_rv$data <- temporary_rv$data
imported_rv$name <- temporary_rv$name
})
return(list(
status = reactive(temporary_rv$status),
name = reactive(temporary_rv$name),
data = reactive(datamods:::as_out(temporary_rv$data, return_class))
))
}
moduleServer(
id = id,
module = module
)
}
# utils -------------------------------------------------------------------
#' Get packages containing datasets
#'
#' @return a character vector of packages names
#' @export
#'
#' @importFrom utils data
#'
#' @examples
#' if (interactive()) {
#'
#' get_data_packages()
#'
#' }
get_data_packages <- function() {
suppressWarnings({
pkgs <- data(package = .packages(all.available = TRUE))
})
unique(pkgs$results[, 1])
}
#' List dataset contained in a package
#'
#' @param pkg Name of the package, must be installed.
#'
#' @return a \code{character} vector or \code{NULL}.
#' @export
#'
#' @importFrom utils data
#'
#' @examples
#'
#' list_pkg_data("ggplot2")
list_pkg_data <- function(pkg) {
if (isTRUE(requireNamespace(pkg, quietly = TRUE))) {
list_data <- data(package = pkg, envir = environment())$results[, "Item"]
list_data <- sort(list_data)
attr(list_data, "package") <- pkg
if (length(list_data) < 1) {
NULL
} else {
unname(list_data)
}
} else {
NULL
}
}
#' @importFrom utils data
get_env_data <- function(obj, env = globalenv()) {
pkg <- attr(obj, "package")
re <- regexpr(pattern = "\\(([^\\)]+)\\)", text = obj)
obj_ <- substr(x = obj, start = re + 1, stop = re + attr(re, "match.length") - 2)
obj <- gsub(pattern = "\\s.*", replacement = "", x = obj)
if (obj %in% ls(name = env)) {
get(x = obj, envir = env)
} else if (!is.null(pkg) && !identical(pkg, "")) {
res <- suppressWarnings(try(
get(utils::data(list = obj, package = pkg, envir = environment())), silent = TRUE
))
if (!inherits(res, "try-error"))
return(res)
data(list = obj_, package = pkg, envir = environment())
get(obj, envir = environment())
} else {
NULL
}
}
get_dimensions <- function(objs) {
if (is.null(objs))
return(NULL)
dataframes_dims <- Map(
f = function(name, pkg) {
attr(name, "package") <- pkg
tmp <- suppressWarnings(get_env_data(name))
if (is.data.frame(tmp)) {
sprintf("%d obs. of %d variables", nrow(tmp), ncol(tmp))
} else {
i18n("Not a data.frame")
}
},
name = objs,
pkg = if (!is.null(attr(objs, "package"))) {
attr(objs, "package")
} else {
character(1)
}
)
unlist(dataframes_dims)
}
########
#### Current file: /Users/au301842/FreesearchR/R//launch_FreesearchR.R
########
@ -5582,7 +5965,7 @@ regression_model_uv_list <- function(data,
## This is the very long version
## Handles deeply nested glue string
code <- glue::glue("dplyr::select(data,{paste0(paste(names(data[c(outcome.str, .var)]),collapse=','))})|>\nFreesearchR::regression_model({list2str(modifyList(parameters,list(formula.str = glue::glue(gsub('vars','.var',formula.str.c)))))})")
code <- glue::glue("FreesearchR::regression_model({list2str(modifyList(parameters,list(formula.str = glue::glue(gsub('vars','.var',formula.str.c)))))})")
REDCapCAST::set_attr(out, code, "code")
})
@ -7158,6 +7541,7 @@ update_variables_server <- function(id,
updated_data$list_select <- NULL
updated_data$list_mutate <- NULL
updated_data$list_relabel <- NULL
# shiny::req(updated_data$x)
data <- data_r()
new_selections <- input$row_selected
if (length(new_selections) < 1) {
@ -7173,11 +7557,14 @@ update_variables_server <- function(id,
new_names[is.na(new_names)] <- old_names[is.na(new_names)]
new_names[new_names == ""] <- old_names[new_names == ""]
# browser()
old_label <- data_inputs$label
new_label <- data_inputs$label_toset
new_label[new_label == "New label"] <- ""
new_label[is.na(new_label)] <- old_label[is.na(new_label)]
new_label[new_label == ""] <- old_label[new_label == ""]
new_label <- setNames(new_label,new_names)
new_classes <- data_inputs$class_toset
new_classes[new_classes == "Select"] <- NA
@ -7251,6 +7638,8 @@ update_variables_server <- function(id,
# shiny::observeEvent(input$close,
# {
return(shiny::reactive({
shiny::req(updated_data$x)
# browser()
data <- updated_data$x
code <- list()
if (!is.null(data) && shiny::isTruthy(updated_data$list_mutate) && length(updated_data$list_mutate) > 0) {
@ -7263,10 +7652,21 @@ update_variables_server <- function(id,
code <- c(code, list(rlang::expr(select(-any_of(c(!!!updated_data$list_select))))))
}
if (!is.null(data) && shiny::isTruthy(updated_data$list_relabel) && length(updated_data$list_relabel) > 0) {
code <- c(code, list(rlang::call2("purrr::map2(list_relabel,
function(.data,.label){
REDCapCAST::set_attr(.data,.label,attr = 'label')
}) |> dplyr::bind_cols(.name_repair = 'unique_quiet')")))
code <- c(
code,
list(
rlang::expr(purrr::imap(.f=function(.data, .name) {
ls <- !!updated_data$list_relabel
ls <- ls[!is.na(ls)]
if (.name %in% names(ls)) {
REDCapCAST::set_attr(.data, ls[.name], attr = "label")
} else {
.data
}
}) %>% dplyr::bind_cols()
)
)
)
}
if (length(code) > 0) {
attr(data, "code") <- Reduce(
@ -7313,7 +7713,6 @@ update_variables_server <- function(id,
# return(data)
# }))
# })
}
)
}
@ -8142,7 +8541,12 @@ ui_elements <- list(
IDEAFilter::IDEAFilter_ui("data_filter"),
shiny::tags$br()
)
)
),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br()
),
bslib::nav_panel(
title = "Modify",
@ -8542,8 +8946,10 @@ library(gtsummary)
# source("functions.R")
data(starwars)
data(mtcars)
trial <- gtsummary::trial |> default_parsing()
data(trial)
# light <- custom_theme()
#
@ -8648,9 +9054,9 @@ server <- function(input, output, session) {
shiny::observeEvent(from_env$data(), {
shiny::req(from_env$data())
browser()
rv$data_temp <- from_env$data()
# rv$code <- append_list(data = from_env$code(),list = rv$code,index = "import")
rv$code <- append_list(data = from_env$name(),list = rv$code,index = "import")
})
output$import_var <- shiny::renderUI({
@ -8792,7 +9198,8 @@ server <- function(input, output, session) {
title = "Update and select variables",
footer = tagList(
actionButton("ok", "OK")
))
)
)
)
output$data_info <- shiny::renderUI({
@ -8957,43 +9364,53 @@ server <- function(input, output, session) {
output$code_import <- shiny::renderPrint({
shiny::req(rv$code$import)
cat(rv$code$import)
cat(c("#Data import\n",rv$code$import))
})
output$code_data <- shiny::renderPrint({
shiny::req(rv$code$modify)
# browser()
ls <- rv$code$modify |> unique()
out <- paste("data <- data |>",
sapply(ls, \(.x) paste(deparse(.x), collapse = ",")),
collapse = "|>"
) |>
(\(.x){
gsub(
"\\|>", "\\|> \n",
gsub(
"%>%", "",
gsub(
"\\s{2,}", " ",
gsub(",\\s{,},", ", ", .x)
)
)
)
})()
cat(out)
out <- ls |>
merge_expression() |>
expression_string(assign.str = "data <- data |>\n")
# out <- paste("data <- data |>",
# sapply(ls, \(.x) paste(deparse(.x), collapse = ",")),
# collapse = "|>"
# ) |>
# (\(.x){
# gsub(
# "\\|>", "\\|> \n",
# gsub(
# "%>%", "",
# gsub(
# "\\s{2,}", " ",
# gsub(",\\s{,},", ", ", .x)
# )
# )
# )
# })()
cat(c("#Data modifications\n",out))
})
output$code_filter <- shiny::renderPrint({
cat(rv$code$filter)
cat(c("#Data filter\n",rv$code$filter))
})
output$code_table1 <- shiny::renderPrint({
shiny::req(rv$code$table1)
cat(rv$code$table1)
cat(c("#Data characteristics table\n",rv$code$table1))
})
## Just a note to self
## This is a very rewarding couple of lines marking new insights to dynamically rendering code
shiny::observe({
rv$regression()$regression$models |> purrr::imap(\(.x, .i){
output[[paste0("code_",tolower(.i))]] <- shiny::renderPrint({cat(.x$code_table)})
output[[paste0("code_", tolower(.i))]] <- shiny::renderPrint({
cat(.x$code_table)
})
})
})
@ -9141,7 +9558,6 @@ server <- function(input, output, session) {
# ) |>
# merge_expression() |>
# expression_string()
}
)

View file

@ -5,6 +5,6 @@ account: agdamsbo
server: shinyapps.io
hostUrl: https://api.shinyapps.io/v1
appId: 13611288
bundleId: 10084710
bundleId: 10085560
url: https://agdamsbo.shinyapps.io/freesearcheR/
version: 1

View file

@ -30,8 +30,10 @@ library(gtsummary)
# source("functions.R")
data(starwars)
data(mtcars)
trial <- gtsummary::trial |> default_parsing()
data(trial)
# light <- custom_theme()
#
@ -138,7 +140,7 @@ server <- function(input, output, session) {
shiny::req(from_env$data())
rv$data_temp <- from_env$data()
# rv$code <- append_list(data = from_env$code(),list = rv$code,index = "import")
rv$code <- append_list(data = from_env$name(),list = rv$code,index = "import")
})
output$import_var <- shiny::renderUI({
@ -280,7 +282,8 @@ server <- function(input, output, session) {
title = "Update and select variables",
footer = tagList(
actionButton("ok", "OK")
))
)
)
)
output$data_info <- shiny::renderUI({
@ -445,43 +448,53 @@ server <- function(input, output, session) {
output$code_import <- shiny::renderPrint({
shiny::req(rv$code$import)
cat(rv$code$import)
cat(c("#Data import\n",rv$code$import))
})
output$code_data <- shiny::renderPrint({
shiny::req(rv$code$modify)
# browser()
ls <- rv$code$modify |> unique()
out <- paste("data <- data |>",
sapply(ls, \(.x) paste(deparse(.x), collapse = ",")),
collapse = "|>"
) |>
(\(.x){
gsub(
"\\|>", "\\|> \n",
gsub(
"%>%", "",
gsub(
"\\s{2,}", " ",
gsub(",\\s{,},", ", ", .x)
)
)
)
})()
cat(out)
out <- ls |>
merge_expression() |>
expression_string(assign.str = "data <- data |>\n")
# out <- paste("data <- data |>",
# sapply(ls, \(.x) paste(deparse(.x), collapse = ",")),
# collapse = "|>"
# ) |>
# (\(.x){
# gsub(
# "\\|>", "\\|> \n",
# gsub(
# "%>%", "",
# gsub(
# "\\s{2,}", " ",
# gsub(",\\s{,},", ", ", .x)
# )
# )
# )
# })()
cat(c("#Data modifications\n",out))
})
output$code_filter <- shiny::renderPrint({
cat(rv$code$filter)
cat(c("#Data filter\n",rv$code$filter))
})
output$code_table1 <- shiny::renderPrint({
shiny::req(rv$code$table1)
cat(rv$code$table1)
cat(c("#Data characteristics table\n",rv$code$table1))
})
## Just a note to self
## This is a very rewarding couple of lines marking new insights to dynamically rendering code
shiny::observe({
rv$regression()$regression$models |> purrr::imap(\(.x, .i){
output[[paste0("code_",tolower(.i))]] <- shiny::renderPrint({cat(.x$code_table)})
output[[paste0("code_", tolower(.i))]] <- shiny::renderPrint({
cat(.x$code_table)
})
})
})
@ -629,7 +642,6 @@ server <- function(input, output, session) {
# ) |>
# merge_expression() |>
# expression_string()
}
)

View file

@ -159,7 +159,12 @@ ui_elements <- list(
IDEAFilter::IDEAFilter_ui("data_filter"),
shiny::tags$br()
)
)
),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br(),
shiny::tags$br()
),
bslib::nav_panel(
title = "Modify",

View file

@ -59,12 +59,20 @@ This will unfold options to preview your data dictionary (the main database meta
### Local or sample data
When opening the online hosted app, this is mainly for testing purposes. When running the app locally from *R* on your own computer, you will find all data.frames in the current environment here. This extends the possible uses of this app to allow for quick and easy data insights and code generation for basic plotting to fine tune.
## Evaluate
### Baseline
This panel allows for basic data evaluation.
### Characteristics
Create a classical baseline characteristics table with optional data stratification and comparisons.
### Correlation matrix
Visualise variable correlations and get suggestions to exclude highly correlated variables.
## Visuals
There are a number of plotting options to visualise different aspects of the data.