insightsengineering / teal.slice

Reproducible slice module for teal applications
https://insightsengineering.github.io/teal.slice/
Other
11 stars 5 forks source link

[Bug]: There is a validation error in the active filters when this example is run #586

Closed vedhav closed 2 months ago

vedhav commented 2 months ago

What happened?

Error observed during assertion in countBar():

Screenshot 2024-05-03 at 6 17 16 PM

Example app code to reproduce the error:

library(teal.modules.clinical)
data <- teal.data::teal_data() %>%
  within({
    library(dplyr)

    ADSL <- teal.data::rADSL
    ADLB <- teal.data::rADLB %>%
      filter(!AVISIT %in% c("SCREENING", "BASELINE"))
  })

datanames <- c("ADSL", "ADLB")
teal.data::datanames(data) <- datanames
teal.data::join_keys(data) <- teal.data::default_cdisc_join_keys[datanames]

app <- init(
  data = data,
  modules = tm_t_abnormality_by_worst_grade(
    label = "Laboratory Test Results with Highest Grade Post-Baseline",
    dataname = "ADLB",
    arm_var = teal.transform::choices_selected(
      choices = teal.transform::variable_choices(data[["ADSL"]], subset = c("ARM", "ARMCD")),
      selected = "ARM"
    ),
    paramcd = teal.transform::choices_selected(
      choices = teal.transform::value_choices(data[["ADLB"]], "PARAMCD", "PARAM"),
      selected = c("ALT", "CRP", "IGA")
    ),
    add_total = FALSE
  ),
  filter = teal::teal_slices(
    teal.slice::teal_slice("ADSL", "SAFFL", selected = "Y"),
    teal.slice::teal_slice("ADLB", "ONTRTFL", selected = "Y")
  )
)
shinyApp(app$ui, app$server)

sessionInfo()

R version 4.3.2 (2023-10-31)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Sonoma 14.4.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Asia/Kolkata
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] dplyr_1.1.4                      teal.modules.clinical_0.9.0.9024
 [3] testthat_3.2.1.1                 tern_0.9.4.9004                 
 [5] rtables_0.6.7.9001               magrittr_2.0.3                  
 [7] formatters_0.5.6.9003            teal.transform_0.5.0.9005       
 [9] teal_0.15.2.9032                 teal.slice_0.5.1.9000           
[11] teal.data_0.6.0.9000             teal.code_0.5.0.9005            
[13] shiny_1.8.1.1                   

loaded via a namespace (and not attached):
  [1] Rdpack_2.6               mmrm_0.3.11.9000         tern.mmrm_0.3.0.9007    
  [4] formatR_1.14             remotes_2.5.0            tern.gee_0.1.3.9006     
  [7] logger_0.3.0             sandwich_3.1-0           rlang_1.1.3             
 [10] multcomp_1.4-25          compiler_4.3.2           vctrs_0.6.5             
 [13] stringr_1.5.1            profvis_0.3.8            crayon_1.5.2            
 [16] pkgconfig_2.0.3          fastmap_1.1.1            backports_1.4.1         
 [19] ellipsis_0.3.2           fontawesome_0.5.2        utf8_1.2.4              
 [22] promises_1.3.0           rmarkdown_2.26           sessioninfo_1.2.2       
 [25] purrr_1.0.2              xfun_0.43                shinyvalidate_0.1.3     
 [28] cachem_1.0.8             teal.reporter_0.3.1.9005 jsonlite_1.8.8          
 [31] later_1.3.2              broom_1.0.5              parallel_4.3.2          
 [34] R6_2.5.1                 bslib_0.7.0              stringi_1.8.3           
 [37] parallelly_1.37.1        pkgload_1.3.4            brio_1.1.5              
 [40] jquerylib_0.1.4          estimability_1.5         assertthat_0.2.1        
 [43] Rcpp_1.0.12              knitr_1.45               usethis_2.2.3           
 [46] zoo_1.8-12               teal.logger_0.2.0.9002   httpuv_1.6.15           
 [49] Matrix_1.6-5             splines_4.3.2            tidyselect_1.2.1        
 [52] rstudioapi_0.16.0        yaml_2.3.8               codetools_0.2-19        
 [55] miniUI_0.1.1.1           teal.widgets_0.4.2.9011  rlistings_0.2.8.9001    
 [58] pkgbuild_1.4.4           lattice_0.22-6           tibble_3.2.1            
 [61] withr_3.0.0              coda_0.19-4              evaluate_0.23           
 [64] desc_1.4.3               survival_3.5-8           urlchecker_1.0.1        
 [67] shinycssloaders_1.0.0    pillar_1.9.0             checkmate_2.3.1         
 [70] DT_0.33                  shinyjs_2.1.0            plotly_4.10.4           
 [73] generics_0.1.3           rprojroot_2.0.4          ggplot2_3.5.1           
 [76] munsell_0.5.1            scales_1.3.0             xtable_1.8-4            
 [79] glue_1.7.0               lazyeval_0.2.2           emmeans_1.10.1          
 [82] tools_4.3.2              data.table_1.15.4        fs_1.6.4                
 [85] mvtnorm_1.2-4            cowplot_1.1.3            grid_4.3.2              
 [88] tidyr_1.3.1              rbibutils_2.2.16         devtools_2.4.5          
 [91] colorspace_2.1-0         nlme_3.1-164             cli_3.6.2               
 [94] vistime_1.2.4            fansi_1.0.6              viridisLite_0.4.2       
 [97] geepack_1.3.10           gtable_0.3.5             sass_0.4.9              
[100] digest_0.6.35            ggrepel_0.9.5            TH.data_1.1-2           
[103] htmlwidgets_1.6.4        memoise_2.0.1            htmltools_0.5.8.1       
[106] lifecycle_1.0.4          httr_1.4.7               shinyWidgets_0.8.6      
[109] mime_0.12                MASS_7.3-60.0.1

Relevant log output

Warning: Error in <Anonymous>: Assertion on 'countmax' failed: May not be NA.
  141: <Anonymous>
  140: stop
  139: mstop
  138: makeAssertion
  137: checkmate::assert_number
  136: <Anonymous>
  135: mapply
  134: countBars
  121: private$ui_inputs
  114: state$ui
  101: FUN
  100: lapply
   99: renderUI
   98: func
   85: renderFunc
   84: output$teal-main_ui-filter_panel-active-ADLB-filter-cards
    3: runApp
    2: print.shiny.appobj
    1: <Anonymous>

Code of Conduct

Contribution Guidelines

Security Policy

donyunardi commented 2 months ago

Yes, I was able to reproduce this error and I may have some idea on why this is happening.

I think it has something to do with the way teal.slice treat elements with "" (empty string) factor level.

By default, ADLB$ONTRTFL is a factorial vector with 2 levels: "" and Y. image

ChoicesFilterState$set_choices() is the method in question for this variable, specifically this line: https://github.com/insightsengineering/teal.slice/blob/f184938c73b063423c179c9734c7ebfe69f8a317/R/FilterStateChoices.R#L269

When I run this for ADLB$ONTRTFL, this is what I see: image

If you notice, there's an empty spaces before the number 6000.

I think teal.slice is currently returning NA for the empty string level which causing the countmax error message.

The quick workaround is to treat these empty values as explicit NA and rebuild the factor levels for ADLB$ONTRTFL. In the code example below, I'm using na_if() and factor() to rebuild the variable. The app now runs properly.

Code ```r options(teal.log_level = "TRACE", teal.show_js_log = TRUE) library(teal.modules.clinical) data <- teal.data::teal_data() %>% within({ library(dplyr) ADSL <- teal.data::rADSL ADLB <- teal.data::rADLB |> filter(!AVISIT %in% c("SCREENING", "BASELINE")) ADLB$ONTRTFL <- na_if(ADLB$ONTRTFL, "") ADLB$ONTRTFL <- factor(ADLB$ONTRTFL) }) datanames <- c("ADSL", "ADLB") teal.data::datanames(data) <- datanames teal.data::join_keys(data) <- teal.data::default_cdisc_join_keys[datanames] app <- init( data = data, modules = tm_t_abnormality_by_worst_grade( label = "Laboratory Test Results with Highest Grade Post-Baseline", dataname = "ADLB", arm_var = teal.transform::choices_selected( choices = teal.transform::variable_choices(data[["ADSL"]], subset = c("ARM", "ARMCD")), selected = "ARM" ), paramcd = teal.transform::choices_selected( choices = teal.transform::value_choices(data[["ADLB"]], "PARAMCD", "PARAM"), selected = c("ALT", "CRP", "IGA") ) # add_total = FALSE ), filter = teal::teal_slices( teal.slice::teal_slice("ADSL", "SAFFL", selected = "Y"), teal.slice::teal_slice("ADLB", "ONTRTFL", selected = "Y") ) ) shinyApp(app$ui, app$server) ```

However, this is still a bug and will need to be addressed.

donyunardi commented 2 months ago

Here's a simpler code to play around while debugging:

x <- data.frame(
  a = factor(c("", "Y", "", "Y")),
  b = LETTERS[1:4]
)

library(teal)

app <- init(
  data = teal_data(
    x = x
  ),
  modules = modules(
    example_module(label = "my module")
  ),
  title = "my teal app",
  filter = teal_slices(
    teal_slice("x", "a")
  )
)

shinyApp(app$ui, app$server)
donyunardi commented 2 months ago

I found out that using tern's df_explicit_na() function also works because this basically replaces the empty string level with <missing> during data preprocessing.

Code ```r x <- data.frame( a = factor(c("", "Y", "", "Y")), b = LETTERS[1:4] ) x <- tern::df_explicit_na(x) library(teal) app <- init( data = teal_data( x = x ), modules = modules( example_module(label = "my module") ), title = "my teal app", filter = teal_slices( teal_slice("x", "a", keep_na = FALSE) ) ) shinyApp(app$ui, app$server) ```

It's also work on @vedhav code

@vedhav code ```r library(teal.modules.clinical) data <- teal.data::teal_data() %>% within({ library(dplyr) ADSL <- teal.data::rADSL ADLB <- teal.data::rADLB %>% filter(!AVISIT %in% c("SCREENING", "BASELINE")) ADLB <- tern::df_explicit_na(ADLB) }) datanames <- c("ADSL", "ADLB") teal.data::datanames(data) <- datanames teal.data::join_keys(data) <- teal.data::default_cdisc_join_keys[datanames] app <- init( data = data, modules = tm_t_abnormality_by_worst_grade( label = "Laboratory Test Results with Highest Grade Post-Baseline", dataname = "ADLB", arm_var = teal.transform::choices_selected( choices = teal.transform::variable_choices(data[["ADSL"]], subset = c("ARM", "ARMCD")), selected = "ARM" ), paramcd = teal.transform::choices_selected( choices = teal.transform::value_choices(data[["ADLB"]], "PARAMCD", "PARAM"), selected = c("ALT", "CRP", "IGA") ), add_total = FALSE ), filter = teal::teal_slices( teal.slice::teal_slice("ADSL", "SAFFL", selected = "Y"), teal.slice::teal_slice("ADLB", "ONTRTFL", selected = "Y") ) ) shinyApp(app$ui, app$server) ```