tern icon indicating copy to clipboard operation
tern copied to clipboard

[Bug]: Keeping NAs with `analyze_vars(na_rm = TRUE)` raise an error

Open llrs-roche opened this issue 2 months ago • 0 comments

What happened?

One user complained that teal modules don't handle well NAs. After some exploration it was shown that analyze_vars() can remove NAs correctly and there was some problem on the modules (https://github.com/insightsengineering/teal.modules.clinical/issues/1437). Modules used tern::df_explicit_na() to show NAs. However, they use it because analyze_vars() doesn't show NAs when na_rm = FALSE, instead it raises an error.

See the last two table layouts were NAs are not shown on tables even if they are not removed (layouts tern_w_NA*) and an error is raised, while the table using just rtables shows there are NAs present (layouts w_NA*).

library("tern")
#> Loading required package: rtables
#> ...
dta <- data.frame(risk = factor(c(NA, "High Risk", "High Risk","High Risk", "Low Risk", "High Risk", "Low Risk", "High Risk", "High Risk"), levels = c(NA, "High Risk", "Low Risk")),
                  arm = factor(c("A", "C", "C", "C", "C", "C", "C", "C", "C"),levels = c("A", "B", "C")),
                  random  = as.factor(sample(letters, 9)))
w_NA <- basic_table() |>
  split_cols_by(var = "arm") |>
  split_rows_by(var = "random") |>
  analyze("risk", afun = function(x){
    as.matrix(table(x, useNA = "always")
  )}, inclNAs = TRUE)
w_NA_wo_split_rows <- basic_table() |>
  split_cols_by(var = "arm") |>
  analyze("risk", afun = function(x){
    as.matrix(table(x, useNA = "always")
  )}, inclNAs = TRUE)
tern_w_NA <- basic_table() |>
  split_cols_by(var = "arm") |>
  split_rows_by(var = "random") |>
  analyze_vars(vars = "risk", na_rm = FALSE)
tern_w_NA_wo_split_rows <- basic_table() |>
  split_cols_by(var = "arm") |>
  analyze_vars(vars = "risk", na_rm = FALSE)

build_table(w_NA, df = dta)
#>             A         B         C   
#> ————————————————————————————————————
#> d                                   
#>   risk   0, 0, 0   0, 0, 0   1, 0, 0
#> i                                   
#>   risk   0, 0, 0   0, 0, 0   0, 1, 0
#> n                                   
#>   risk   0, 0, 1   0, 0, 0   0, 0, 0
#> r                                   
#>   risk   0, 0, 0   0, 0, 0   1, 0, 0
#> s                                   
#>   risk   0, 0, 0   0, 0, 0   1, 0, 0
#> u                                   
#>   risk   0, 0, 0   0, 0, 0   1, 0, 0
#> w                                   
#>   risk   0, 0, 0   0, 0, 0   1, 0, 0
#> x                                   
#>   risk   0, 0, 0   0, 0, 0   1, 0, 0
#> y                                   
#>   risk   0, 0, 0   0, 0, 0   0, 1, 0
build_table(w_NA_wo_split_rows, df = dta)
#>           A         B         C   
#> ——————————————————————————————————
#> risk   0, 0, 1   0, 0, 0   6, 2, 0
build_table(tern_w_NA, df = dta)
#> Error: Error applying analysis function (var - risk): Number of rows generated by analysis function do not match across all columns. 
#>  occured at (row) path: random[n]
build_table(tern_w_NA_wo_split_rows, df = dta)
#> Error: Error applying analysis function (var - risk): Number of rows generated by analysis function do not match across all columns. 
#>  occured at (row) path: root

Created on 2025-11-03 with reprex v2.1.1

Some exploration suggest that the output of a_summary is not consistent when some cells have NA and others don't. This prevent combining them and creating the corresponding rows including NA as they are not returned by levels() of the variable.

sessionInfo()

R version 4.5.1 (2025-06-13 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 22631)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=Spanish_Spain.utf8  LC_CTYPE=Spanish_Spain.utf8   
[3] LC_MONETARY=Spanish_Spain.utf8 LC_NUMERIC=C                  
[5] LC_TIME=Spanish_Spain.utf8    

time zone: Europe/Madrid
tzcode source: internal

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

other attached packages:
[1] tern_0.9.9.9004        testthat_3.2.3         rtables_0.6.13.9001   
[4] magrittr_2.0.4         formatters_0.5.11.9002 pkgdown_2.1.3         
[7] devtools_2.4.6         usethis_3.2.1         

loaded via a namespace (and not attached):
 [1] gtable_0.3.6         xfun_0.53            ggplot2_4.0.0       
 [4] remotes_2.5.0.9000   processx_3.8.6       lattice_0.22-7      
 [7] callr_3.7.6          Rdpack_2.6.4         vctrs_0.6.5         
[10] tools_4.5.1          ps_1.9.1             generics_0.1.4      
[13] sandwich_3.1-1       tibble_3.3.0         pkgconfig_2.0.3     
[16] Matrix_1.7-4         checkmate_2.3.3      RColorBrewer_1.1-3  
[19] S7_0.2.0             desc_1.4.3           lifecycle_1.0.4     
[22] compiler_4.5.1       farver_2.1.2         brio_1.1.5          
[25] codetools_0.2-20     carData_3.0-5        htmltools_0.5.8.1   
[28] yaml_2.3.10          Formula_1.2-5        pillar_1.11.1       
[31] car_3.1-3            tidyr_1.3.1          MASS_7.3-65         
[34] ellipsis_0.3.2       cachem_1.1.0         sessioninfo_1.2.3   
[37] abind_1.4-8          multcomp_1.4-29      nestcolor_0.1.3.9000
[40] tidyselect_1.2.1     digest_0.6.37        mvtnorm_1.3-3       
[43] stringi_1.8.7        dplyr_1.1.4          purrr_1.1.0         
[46] labeling_0.4.3       forcats_1.0.1        splines_4.5.1       
[49] cowplot_1.2.0        rprojroot_2.1.1      fastmap_1.2.0       
[52] grid_4.5.1           cli_3.6.5            survival_3.8-3      
[55] pkgbuild_1.4.8       TH.data_1.1-4        broom_1.0.10        
[58] clipr_0.8.0          withr_3.0.2          rutils_0.0.2.93     
[61] scales_1.4.0         backports_1.5.0      estimability_1.5.1  
[64] rmarkdown_2.30       emmeans_2.0.0        gridExtra_2.3       
[67] zoo_1.8-14           memoise_2.0.1        evaluate_1.0.5      
[70] knitr_1.50           rbibutils_2.3        rlang_1.1.6         
[73] xtable_1.8-4         glue_1.8.0           reprex_2.1.1        
[76] pkgload_1.4.1        rstudioapi_0.17.1    R6_2.6.1            
[79] fs_1.6.6

Relevant log output


Code of Conduct

  • [x] I agree to follow this project's Code of Conduct.

Contribution Guidelines

  • [x] I agree to follow this project's Contribution Guidelines.

Security Policy

  • [x] I agree to follow this project's Security Policy.

llrs-roche avatar Nov 03 '25 09:11 llrs-roche