feat: collapsed options and alert when no missings

This commit is contained in:
Andreas Gammelgaard Damsbo 2026-02-23 13:21:29 +01:00
commit 13c5603245
No known key found for this signature in database

View file

@ -9,8 +9,8 @@
data_missings_ui <- function(id, ...) {
ns <- shiny::NS(id)
list(
bslib::layout_sidebar(
list(bslib::layout_sidebar(
uiOutput(outputId = ns("feedback")),
sidebar = bslib::sidebar(
bslib::accordion(
id = ns("acc_mis"),
@ -20,9 +20,17 @@ data_missings_ui <- function(id, ...) {
value = "acc_pan_mis",
title = "Settings",
icon = bsicons::bs_icon("gear"),
shiny::conditionalPanel(
condition = "output.missings == true",
shiny::uiOutput(ns("missings_method")),
shiny::uiOutput(ns("missings_var")),
shiny::helpText(i18n$t("Evaluate missingness by either comparing missing values across variables (optionally grouped by af categorical or dichotomous variable) or compare variables grouped by the missing status (missing or not) of an outcome variable. If there is a significant difference i the missingness, this may cause a bias in you data and should be considered carefully interpreting the data and analyses as data may not be missing at random.")),
ns = ns
),
shiny::helpText(
i18n$t(
"Evaluate missingness by either comparing missing values across variables (optionally grouped by af categorical or dichotomous variable) or compare variables grouped by the missing status (missing or not) of an outcome variable. If there is a significant difference i the missingness, this may cause a bias in you data and should be considered carefully interpreting the data and analyses as data may not be missing at random."
)
),
shiny::br(),
shiny::actionButton(
inputId = ns("act_miss"),
@ -32,22 +40,18 @@ data_missings_ui <- function(id, ...) {
disabled = FALSE
)
),
do.call(
bslib::accordion_panel,
c(
do.call(bslib::accordion_panel, c(
list(
title = "Download",
icon = bsicons::bs_icon("file-earmark-arrow-down")
),
table_download_ui(id = ns("tbl_dwn"), title = NULL)
)
)
))
)
),
...,
gt::gt_output(outputId = ns("missings_table"))
)
)
))
}
## This should really just be rebuild to only contain a function
@ -59,46 +63,65 @@ data_missings_ui <- function(id, ...) {
#' @name data-missings
#' @returns shiny server module
#' @export
data_missings_server <- function(id,
data,
max_level = 20,
...) {
data_missings_server <- function(id, data, max_level = 20, ...) {
shiny::moduleServer(
id = id,
module = function(input, output, session) {
ns <- session$ns
datar <- if (is.reactive(data)) data else reactive(data)
datar <- if (is.reactive(data))
data
else
reactive(data)
rv <- shiny::reactiveValues(
data = NULL,
table = NULL
rv <- shiny::reactiveValues(data = NULL,
table = NULL,
feedback = NULL)
## Case with no missings
info_alert <- shinyWidgets::alert(
status = "info",
phosphoricons::ph("question"),
i18n$t("You have provided a complete dataset with no missing values.")
)
output$missings <- shiny::reactive({
shiny::req(data())
any(is.na(data()))
})
shiny::outputOptions(output, "missings", suspendWhenHidden = FALSE)
observe({
shiny::req(data())
if (!any(is.na(data()))) {
rv$feedback <- info_alert
} else {
rv$feedback <- NULL
}
})
output$feedback <- renderUI(rv$feedback)
## Notes
##
## Code export is still missing
## Direct table export would be nice
shiny::observe(
output$missings_method <- shiny::renderUI({
shiny::observe(output$missings_method <- shiny::renderUI({
shiny::req(data())
vectorSelectInput(
inputId = ns("missings_method"),
label = i18n$t("Analysis method for missingness overview"),
choices = setNames(
c(
"predictors",
"outcome"
),
c(
choices = setNames(c("predictors", "outcome"), c(
i18n$t("Overview of missings across variables"),
i18n$t("Overview of difference in variables by missing status in outcome")
i18n$t(
"Overview of difference in variables by missing status in outcome"
)
))
)
)
})
)
}))
shiny::observe({
output$missings_var <- shiny::renderUI({
@ -125,9 +148,7 @@ data_missings_server <- function(id,
})
shiny::observeEvent(
list(input$act_miss),
{
shiny::observeEvent(list(input$act_miss), {
shiny::req(datar())
shiny::req(input$missings_var)
# browser()
@ -140,21 +161,18 @@ data_missings_server <- function(id,
type = input$missings_method
)
tryCatch(
{
tryCatch({
shiny::withProgress(message = i18n$t("Calculating. Hold tight for a moment.."), {
out <- do.call(
compare_missings,
modifyList(parameters, list(data = df_tbl))
)
out <- do.call(compare_missings, modifyList(parameters, list(data = df_tbl)))
})
},
error = function(err) {
}, error = function(err) {
showNotification(paste0("Error: ", err), type = "err")
}
)
})
if (is.null(input$missings_var) || input$missings_var == "" || !input$missings_var %in% names(datar()) || input$missings_var == "none") {
if (is.null(input$missings_var) ||
input$missings_var == "" ||
!input$missings_var %in% names(datar()) ||
input$missings_var == "none") {
# if (is.null(variabler()) || variabler() == "" || !variabler() %in% names(data()) || variabler() == "none") {
# tbl <- rv$data()
if (anyNA(datar())) {
@ -171,24 +189,29 @@ data_missings_server <- function(id,
if (input$missings_var == "predictors") {
title <- glue::glue(i18n$t("Missings across variables by the variable **'{input$missings_var}'**"))
title <- glue::glue(
i18n$t(
"Missings across variables by the variable **'{input$missings_var}'**"
)
)
} else {
title <- glue::glue(i18n$t("Missing vs non-missing observations in the variable **'{input$missings_var}'**"))
title <- glue::glue(
i18n$t(
"Missing vs non-missing observations in the variable **'{input$missings_var}'**"
)
)
}
}
attr(out, "tbl_title") <- title
rv$data <- shiny::reactive(out)
}
)
})
shiny::observeEvent(
list(
shiny::observeEvent(list(
# input$act_miss
rv$data
),
{
), {
output$missings_table <- gt::render_gt({
shiny::req(rv$data)
# shiny::req(input$missings_var)
@ -211,8 +234,7 @@ data_missings_server <- function(id,
out
})
}
)
})
table_download_server(
@ -228,25 +250,20 @@ data_missings_server <- function(id,
missing_demo_app <- function() {
ui <- do.call(
bslib::page,
c(
ui <- do.call(bslib::page, c(
list(
title = i18n$t("Missings"),
icon = bsicons::bs_icon("x-circle")
),
data_missings_ui(id = "data"),
gt::gt_output("table_p")
)
)
))
server <- function(input, output, session) {
data_demo <- mtcars
data_demo[sample(1:32, 10), "cyl"] <- NA
data_demo[sample(1:32, 8), "vs"] <- NA
rv <- shiny::reactiveValues(
table = NULL
)
rv <- shiny::reactiveValues(table = NULL)
rv$table <- data_missings_server(id = "data", data = data_demo)
@ -280,12 +297,10 @@ missing_demo_app <- function() {
#' @returns gtsummary list object
#' @export
#'
compare_missings <- function(
data,
compare_missings <- function(data,
by_var,
max_level = 20,
type = c("predictors", "outcome")
) {
type = c("predictors", "outcome")) {
type <- match.arg(type)
if (!is.null(by_var) && by_var != "" && by_var %in% names(data)) {
@ -345,7 +360,12 @@ missings_logic_across <- function(data, exclude = NULL) {
}
if (!is.na(lab)) {
# Restoring original labels, if not NA
REDCapCAST::set_attr(data = out, label = lab, attr = "label", overwrite = TRUE)
REDCapCAST::set_attr(
data = out,
label = lab,
attr = "label",
overwrite = TRUE
)
} else {
out
}