rtables icon indicating copy to clipboard operation
rtables copied to clipboard

Add an empty row at the end of each split_rows_by() section

Open freestatman opened this issue 3 years ago • 1 comments

Add an empty row at the end of each split_rows_by() section

Firstly, thanks for the great package, which is a valuable asset for the community!

I would like to have an empty row in btw split_rows_by() sections, and I have a work-around example below, as showed in cfun_lvl2(). Are there more elegant solutions in {rtables}? I wonder if it's reasonable to consider opening a new argument in split_rows_by(), such as something like add_empty_row flag, or there is a more appropriate place for such customization?

Thanks, Freeman

library(dplyr)
library(rtables)

cfun_lvl1 <- function(df, labelstr, .N_col, .spl_context){
  n <- df %>% dplyr::select(USUBJID) %>% dplyr::n_distinct()
  ret <- in_rows(.names = labelstr, 
                 rcell(c(n, n/.N_col*100), formatters::sprintf_format("%.0f (%.1f)"))
                 )
  return(ret)
}

cfun_lvl2 <- function(df, labelstr, .N_col, .spl_context){
  n <- df %>% dplyr::select(USUBJID) %>% dplyr::n_distinct()
  cust_value <- c(n, n/.N_col*100)
  cust_format <- formatters::sprintf_format("%.0f (%.1f)")

  ## a temp work-around for adding empty row at the end of 2nd-level category value within
  ## 1st-level category. 
  curr_lvl2_val <- .spl_context$value[[nrow(.spl_context)]]
  last_lvl2_val <- .spl_context$full_parent_df[[nrow(.spl_context)]][[.spl_context$split[2]]] %>% levels %>% tail(1)
  if ( curr_lvl2_val == last_lvl2_val ) {
    ret <- in_rows(.names = c(labelstr, " "), 
                   .list = list(c(n, n/.N_col*100), NULL), 
                   .formats = c(cust_format, "xx")
                   )
  } else {
    rc <- rcell(cust_value, format = cust_format)
    ret <- in_rows(.names = labelstr, rc)
  }
  return(ret)
}

tbl_recipe <- basic_table() %>% 
    split_cols_by("ARM") %>%
    split_rows_by("COUNTRY", split_fun = drop_split_levels) %>%
    summarize_row_groups(cfun = cfun_lvl1) %>%
    split_rows_by("SEX", split_fun = drop_split_levels) %>%
    summarize_row_groups(cfun = cfun_lvl2) 

DM2 <- DM %>%
    rename(USUBJID = ID) %>%
    filter(COUNTRY %in% c("USA", "CHN", "BRA"))

tbl <- build_table(tbl_recipe, DM2)
tbl
##       A: Drug X   B: Placebo   C: Combination
## ---------------------------------------------
## CHN   62 (73.8)   48 (64.0)      69 (74.2)
##   F   34 (40.5)   29 (38.7)      31 (33.3)
##   M   28 (33.3)   19 (25.3)      38 (40.9)
## 
## USA   13 (15.5)   14 (18.7)      17 (18.3)
##   F    8 (9.5)     6 (8.0)       10 (10.8)
##   M    5 (6.0)     8 (10.7)       7 (7.5)
## 
## BRA   9 (10.7)    13 (17.3)       7 (7.5)
##   F    6 (7.1)     6 (8.0)        3 (3.2)
##   M    3 (3.6)     7 (9.3)        4 (4.3)

utils::sessionInfo()
## R version 4.1.1 (2021-08-10)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19042)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=English_United States.1252
## [2] LC_CTYPE=English_United States.1252
## [3] LC_MONETARY=English_United States.1252
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.1252
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base
## 
## other attached packages:
## [1] rtables_0.5.0    formatters_0.2.0 magrittr_2.0.2   dplyr_1.0.8
## 
## loaded via a namespace (and not attached):
##  [1] fansi_1.0.2      digest_0.6.29    assertthat_0.2.1 utf8_1.2.2
##  [5] crayon_1.5.0     grid_4.1.1       R6_2.5.1         DBI_1.1.2
##  [9] lifecycle_1.0.1  jsonlite_1.8.0   pillar_1.7.0     rlang_1.0.1
## [13] cli_3.1.1        vctrs_0.3.8      generics_0.1.2   ellipsis_0.3.2
## [17] glue_1.6.0       purrr_0.3.4      fastmap_1.1.0    compiler_4.1.1
## [21] pkgconfig_2.0.3  htmltools_0.5.2  tidyselect_1.1.2 tibble_3.1.6    

freestatman avatar Apr 07 '22 01:04 freestatman

@freestatman great to see rtables usage and interest 'in the wild'.

What you're describing isn't currently possible without hacky workarounds (your approach being one of the possible ways to do it that come to mind).

That said, this exact feature IS already on our roadmap for rtables development, so we agree that it is a good idea for a feature and it should land in an rtables near you sometime this year.

gmbecker avatar Apr 08 '22 20:04 gmbecker

@freestatman This feature has been added to the development version (found here on github) and should be in the next CRAN release (no hard date scheduled yet, though probably soonish). See section_div in ?split_rows_by

 lyt <- basic_table() %>%
+         split_rows_by("ARM", section_div = "-") %>%
+         split_rows_by("STRATA1", section_div = " ") %>%
+         analyze("AGE")
> 
>     tbl <- build_table(lyt, DM)
> tbl
                 all obs
————————————————————————
A: Drug X               
  A                     
    Mean          32.53 
                        
  B                     
    Mean          35.46 
                        
  C                     
    Mean          36.34 
------------------------
B: Placebo              
  A                     
    Mean          32.30 
                        
  B                     
    Mean          32.42 
                        
  C                     
    Mean          34.45 
------------------------
C: Combination          
  A                     
    Mean          35.76 
                        
  B                     
    Mean          34.39 
                        
  C                     
    Mean          33.54 

I'll close this issue, please give it a try and feel free to reopen the issue if this doesn't meet your needs.

gmbecker avatar Aug 22 '22 20:08 gmbecker