Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug when transposing to have toxicity grades in columns using tm_t_events_by_grade #1310

Open
3 tasks done
jenko1979 opened this issue Jan 9, 2025 · 19 comments
Open
3 tasks done
Labels
bug Something isn't working core sme

Comments

@jenko1979
Copy link

jenko1979 commented Jan 9, 2025

What happened?

Using the dummy rds dataframes ADSL and ADAE I have supplied in this post, we can see the bug occurring with the transposed version of the table.

Result in App after filtering out missing gives a count of 43 subjects in SOC1/AECodedterm1which yields a percentage of 82.7% in the original view. There are not actually any missing with AECodedterm1 so we know that filtering out missing or not will always yield 43 subjects and 82.7% in this section.

However, when this table is transposed using the “display grade groupings in nested columns” checkbox, the percentages no longer match this. The percentage has gone down to 79%.

Teal Bug_tm_t_events_by_grade_images.docx

I think this is to do with the percentages now being calculated out of a different denominator.
This change in percentage can only happen if the numerator has gone down in performing this transpose (which should not happen here as mentioned this AECodedterm1 has no missing toxicity grades so this numerator should always be 43 when only missings are being filtered out in the app), or the denominator has gone up, which again, should not happen as 52 is our max. number of subjects we have in this trt arm. So percentage should still remain as 82.7% in this table.
I also propose that in this transposed version we need to move the N=xx here to directly below the treatment arm. It makes no sense at all below the grades as this doesn’t reflect the number of subjects in each of these columns, just the number of subjects in each treatment arm. This is now possible in rtables actually as there are now arguments to add these N=xx for each spanning column header. (within the split_cols_by function split_cols_by("TRT01A", show_colcounts = TRUE and split_cols_by("AETOXGR", show_colcounts = FALSE) %>%). Then we would only get one occurrence of this N=xx under each trt arm. It’s also possible to add the N for number of subjects that are in each grade spanning header (split_cols_by("AETOXGR", show_colcounts = TRUE), and maybe this will also help debug what denominator Is being used in this transposed version of the table to construct these percentages.

Code to reproduce this, using rds data supplied (please reach out to me on [email protected] and i can supply the dummy .rds data as this is not supported to attach here):

my_data <- within(teal_data(),
                  {
                    ADSL <- readRDS("ADSL.rds")
                    ADAE <- readRDS("ADAE.rds") 
                  })

datanames <- c("ADSL", "ADAE")
datanames(my_data) <- datanames
join_keys(my_data) <- join_keys(
  join_key("ADSL", keys = c("STUDYID", "USUBJID")),
  join_key("ADAE", keys = c("STUDYID", "USUBJID")),
  
  join_key(
    "ADSL",
    "ADAE",
    keys = c("STUDYID" = "STUDYID", "USUBJID" = "USUBJID")
  )
)

app <- init(
  data = my_data,
  modules = modules(
    tm_t_events_by_grade(
      label = "Adverse Events by Grade Table",
      dataname = "ADAE",
      arm_var = choices_selected(c("TRT01A"), "TRT01A"),
      llt = choices_selected(
        choices = variable_choices("ADAE", c("AEDECOD")),
        selected = c("AEDECOD")
      ),
      hlt = choices_selected(
        choices = variable_choices("ADAE", c("AEBODSYS")),
        selected = "AEBODSYS"
      ),
      grade = choices_selected(
        choices = variable_choices("ADAE", c("AETOXGR")),
        selected = "AETOXGR"
      )
    )
  )
)
if (interactive()) {
  shinyApp(app$ui, app$server)
}

sessionInfo()

No response

Relevant log output

No response

Code of Conduct

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

Contribution Guidelines

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

Security Policy

  • I agree to follow this project's Security Policy.
@jenko1979 jenko1979 added the bug Something isn't working label Jan 9, 2025
@llrs-roche
Copy link
Contributor

llrs-roche commented Jan 10, 2025

Hi, I think I something similar was reported recently but I don't find it now to check if it was fixed or what was done. Thanks for the report. We'll see what we can do. I edited your question to set code formatting. Could you report the sessionInfo() to know the versions of the packages where this is present?

@jenko1979
Copy link
Author

jenko1979 commented Jan 10, 2025

Thanks, please do reach out to me for the dummy .rds files I have created so that you can re-produce the issue with my reprex code. [email protected]

Sessioninfo() below:

R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Running under: Rocky Linux 8.10 (Green Obsidian)

Matrix products: default
BLAS/LAPACK: /usr/lib64/libopenblasp-r0.3.15.so;  LAPACK version 3.9.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Etc/UTC
tzcode source: system (glibc)

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

other attached packages:
 [1] openxlsx_4.2.7.1            fs_1.6.4                   
 [3] stringr_1.5.1               tidyr_1.3.1                
 [5] dplyr_1.1.4                 haven_2.5.4                
 [7] teal.modules.clinical_0.9.1 tern_0.9.6                 
 [9] rtables_0.6.10              magrittr_2.0.3             
[11] formatters_0.5.9            teal.modules.general_0.3.0 
[13] teal.transform_0.5.0        teal_0.15.2                
[15] teal.slice_0.5.1            teal.data_0.6.0            
[17] teal.code_0.5.0             ggmosaic_0.3.3             
[19] ggplot2_3.5.1               shiny_1.8.1.1              
[21] forcats_1.0.0               teal.widgets_0.4.2         
[23] DT_0.33                    

loaded via a namespace (and not attached):
 [1] gtable_0.3.6       htmlwidgets_1.6.4  ggrepel_0.9.6      lattice_0.22-6    
 [5] vctrs_0.6.5        tools_4.4.1        Rdpack_2.6.2       generics_0.1.3    
 [9] tibble_3.2.1       fansi_1.0.6        pkgconfig_2.0.3    Matrix_1.7-0      
[13] data.table_1.16.2  checkmate_2.3.2    lifecycle_1.0.4    compiler_4.4.1    
[17] munsell_0.5.1      httpuv_1.6.15      htmltools_0.5.8.1  lazyeval_0.2.2    
[21] plotly_4.10.4      later_1.3.2        pillar_1.9.0       MASS_7.3-60.2     
[25] tern.gee_0.1.5     nlme_3.1-164       mime_0.12          zip_2.3.1         
[29] tidyselect_1.2.1   digest_0.6.36      mvtnorm_1.3-2      stringi_1.8.4     
[33] purrr_1.0.2        geepack_1.3.12     splines_4.4.1      fastmap_1.2.0     
[37] grid_4.4.1         colorspace_2.1-1   cli_3.6.3          logger_0.4.0      
[41] survival_3.6-4     utf8_1.2.4         broom_1.0.7        withr_3.0.2       
[45] scales_1.3.0       promises_1.3.0     backports_1.5.0    estimability_1.5.1
[49] httr_1.4.7         emmeans_1.10.6     hms_1.1.3          rbibutils_2.3     
[53] viridisLite_0.4.2  rlang_1.1.4        Rcpp_1.0.12        xtable_1.8-4      
[57] glue_1.7.0         jsonlite_1.8.8     rstudioapi_0.16.0  teal.logger_0.3.0 
[61] R6_2.5.1          

@llrs-roche
Copy link
Contributor

Thanks! I am not sure if I will be fixing this or someone else, but in any case it will be easier to not need to pass the data files via email.
If it is dummy data can you post the script to create it or the output of dput(ADSL) and dput(ADAE)?

@jenko1979
Copy link
Author

jenko1979 commented Jan 10, 2025

Sure, dput outputs below:

ADSL <- 
structure(list(STUDYID = structure(c("XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX"), label = "Study Identifier"), 
    USUBJID = structure(c("1", "2", "3", "4", "5", "6", "7", 
    "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", 
    "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", 
    "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 
    "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", 
    "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", 
    "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", 
    "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", 
    "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", 
    "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 
    "98", "99", "100", "101", "102", "103", "104", "105"), label = "Unique Subject Identifier"), 
    TRT01A = structure(c(2L, 1L, 2L, 1L, 3L, 1L, 1L, 3L, 1L, 
    2L, 2L, 3L, 3L, 1L, 2L, 3L, 1L, 1L, 2L, 3L, 1L, 3L, 2L, 1L, 
    1L, 3L, 3L, 2L, 1L, 1L, 2L, 1L, 1L, 2L, 3L, 1L, 1L, 3L, 1L, 
    1L, 2L, 3L, 2L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 2L, 
    2L, 1L, 2L, 2L, 1L, 1L, 1L, 3L, 1L, 1L, 3L, 1L, 3L, 1L, 2L, 
    3L, 1L, 1L, 3L, 1L, 1L, 2L, 3L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 
    3L, 1L, 1L, 2L, 1L, 1L, 2L, 3L, 1L, 1L, 2L, 3L, 2L, 3L, 2L, 
    1L, 3L, 1L, 3L, 3L, 1L), levels = c("Dummy A", "Dummy B", 
    "Dummy C"), class = "factor")), row.names = c(NA, -105L), class = c("tbl_df", 
"tbl", "data.frame"))
ADAE <-
structure(list(STUDYID = structure(c("XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
"XXXXXXXXX"), label = "Study Identifier"), USUBJID = structure(c("1", 
"2", "2", "3", "4", "5", "5", "5", "6", "7", "8", "9", "10", 
"11", "12", "13", "13", "14", "15", "16", "16", "17", "17", "18", 
"18", "18", "19", "19", "20", "20", "21", "22", "23", "23", "23", 
"23", "23", "24", "24", "25", "26", "26", "26", "27", "27", "28", 
"28", "29", "29", "29", "30", "30", "31", "31", "32", "33", "33", 
"34", "35", "35", "36", "36", "36", "37", "38", "38", "38", "38", 
"39", "40", "41", "41", "42", "42", "43", "44", "44", "45", "46", 
"47", "48", "48", "48", "49", "49", "50", "51", "51", "52", "53", 
"54", "55", "56", "57", "58", "58", "59", "60", "60", "60", "61", 
"62", "62", "63", "63", "63", "63", "63", "64", "65", "66", "67", 
"68", "69", "70", "70", "71", "71", "72", "73", "74", "74", "74", 
"74", "75", "76", "77", "78", "78", "78", "78", "78", "78", "79", 
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "89", 
"89", "90", "91", "92", "93", "94", "95", "95", "95", "96", "97", 
"97", "98", "99", "99", "100", "100", "101", "102", "103", "104", 
"104", "105"), label = "Unique Subject Identifier"), AEDECOD = structure(c("AECodedterm1", 
"AECodedterm3", "AECodedterm2", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
"AECodedterm2", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
"AECodedterm2", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm2", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm3", "AECodedterm3", "AECodedterm3", 
"AECodedterm1", "AECodedterm2", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
"AECodedterm2", "AECodedterm1", "AECodedterm3", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
"AECodedterm1", "AECodedterm1", "AECodedterm3", "AECodedterm2", 
"AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm3", 
"AECodedterm1", "AECodedterm2", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm3", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
"AECodedterm1", "AECodedterm1", "AECodedterm3", "AECodedterm1", 
"AECodedterm2", "AECodedterm1", "AECodedterm2"), label = "Dictionary-Derived Term"), 
    AEBODSYS = structure(c("SOC1", "SOC3", "SOC2", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC2", 
    "SOC2", "SOC1", "SOC1", "SOC2", "SOC2", "SOC1", "SOC1", "SOC1", 
    "SOC2", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC3", "SOC3", "SOC3", 
    "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC2", 
    "SOC2", "SOC1", "SOC3", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC3", "SOC2", 
    "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC3", "SOC1", "SOC2", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC3", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
    "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", 
    "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC3", "SOC1", 
    "SOC2", "SOC1", "SOC2"), label = "Body System or Organ Class"), 
    AETOXGR = structure(c(4L, 2L, 2L, 4L, 2L, 3L, 4L, 3L, 3L, 
    4L, 4L, 3L, 3L, 4L, 4L, 3L, 4L, 4L, 5L, 1L, 3L, 4L, 3L, 4L, 
    3L, 3L, 4L, 3L, 3L, 2L, 4L, 4L, 3L, 3L, 4L, 4L, 3L, 2L, 4L, 
    2L, 4L, 4L, 4L, 4L, 3L, 3L, 2L, 4L, 3L, 4L, 4L, 2L, 3L, 3L, 
    3L, 3L, 4L, 3L, 4L, 3L, 3L, 4L, 4L, 2L, 4L, 3L, 4L, 3L, 4L, 
    4L, 5L, 6L, 3L, 4L, 3L, 1L, 3L, 3L, 4L, 2L, 2L, 4L, 3L, 2L, 
    3L, 4L, 4L, 6L, 3L, 4L, 4L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 
    3L, 3L, 5L, 6L, 3L, 4L, 3L, 3L, 4L, 3L, 3L, 3L, 3L, 2L, 3L, 
    1L, 4L, 5L, 6L, 4L, 3L, 4L, 3L, 4L, 3L, 4L, 3L, 4L, 4L, 4L, 
    3L, 3L, 5L, 4L, 4L, 4L, 3L, 3L, 3L, 4L, 3L, 4L, 5L, 5L, 4L, 
    5L, 6L, 4L, 4L, 4L, 4L, 3L, 3L, 3L, 3L, 4L, 3L, 4L, 4L, 3L, 
    3L, 3L, 3L, 4L, 3L, 5L, 3L, 4L, 3L), levels = c("<Missing>", 
    "1", "2", "3", "4", "5"), class = "factor", label = "Standard Toxicity Grade"), 
    TRT01A = structure(c(2L, 1L, 1L, 2L, 1L, 3L, 3L, 3L, 1L, 
    1L, 3L, 1L, 2L, 2L, 3L, 3L, 3L, 1L, 2L, 3L, 3L, 1L, 1L, 1L, 
    1L, 1L, 2L, 2L, 3L, 3L, 1L, 3L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 
    1L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
    1L, 1L, 1L, 2L, 3L, 3L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 1L, 
    1L, 2L, 2L, 3L, 3L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 
    1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 
    1L, 1L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 1L, 3L, 1L, 2L, 
    3L, 3L, 1L, 1L, 1L, 3L, 1L, 1L, 1L, 1L, 1L, 2L, 3L, 1L, 1L, 
    1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 3L, 1L, 1L, 2L, 1L, 
    1L, 1L, 1L, 2L, 3L, 1L, 1L, 2L, 2L, 2L, 3L, 2L, 2L, 3L, 2L, 
    2L, 1L, 1L, 3L, 1L, 3L, 3L, 3L, 1L), levels = c("Dummy A", 
    "Dummy B", "Dummy C"), class = "factor")), row.names = c(NA, 
-168L), label = "Adverse Event Analysis Dataset", class = c("tbl_df", 
"tbl", "data.frame"))

@llrs-roche
Copy link
Contributor

llrs-roche commented Jan 10, 2025

I used your data with the development version of teal.modules.clinical and rtables (among others) and run your reproducible example (reprex).

When I check the "Display grade groupings in nested columns" I see a message: "Data includes records with grade levels outside of 1-5. Please use filter panel to exclude from analysis in order to display grade grouping in nested columns.". On the right panel "Active Filter Variables" I see that AETOXGR variable has <Missing>, 1, 2, 3, 4, 5, apparently there are 3 Missing values.

If I use dplyr::count(ADAE, AEDECOD, AEBODSYS, AETOXGR, sort = TRUE), I see that there are some missing AETOXGR in AEDECOD == "AECodedterm2" & AEBODSYS == "SOC2" and AEDECOD == "AECodedterm3" & AEBODSYS == "SOC3".

When I deselect those missing then I see the following table:

image

I hope this help clarify the problem and updates we made on the package to avoid this kind of confusions.
I will tag the issue so that other people chime in to see how to address this.

reprex
my_data <- within(teal_data(),
                  {
                    ADSL <- 
                      structure(list(STUDYID = structure(c("XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX"), label = "Study Identifier"), 
                                     USUBJID = structure(c("1", "2", "3", "4", "5", "6", "7", 
                                                           "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", 
                                                           "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", 
                                                           "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", 
                                                           "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", 
                                                           "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", 
                                                           "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", 
                                                           "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", 
                                                           "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", 
                                                           "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", 
                                                           "98", "99", "100", "101", "102", "103", "104", "105"), label = "Unique Subject Identifier"), 
                                     TRT01A = structure(c(2L, 1L, 2L, 1L, 3L, 1L, 1L, 3L, 1L, 
                                                          2L, 2L, 3L, 3L, 1L, 2L, 3L, 1L, 1L, 2L, 3L, 1L, 3L, 2L, 1L, 
                                                          1L, 3L, 3L, 2L, 1L, 1L, 2L, 1L, 1L, 2L, 3L, 1L, 1L, 3L, 1L, 
                                                          1L, 2L, 3L, 2L, 1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 2L, 
                                                          2L, 1L, 2L, 2L, 1L, 1L, 1L, 3L, 1L, 1L, 3L, 1L, 3L, 1L, 2L, 
                                                          3L, 1L, 1L, 3L, 1L, 1L, 2L, 3L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 
                                                          3L, 1L, 1L, 2L, 1L, 1L, 2L, 3L, 1L, 1L, 2L, 3L, 2L, 3L, 2L, 
                                                          1L, 3L, 1L, 3L, 3L, 1L), levels = c("Dummy A", "Dummy B", 
                                                                                              "Dummy C"), class = "factor")), row.names = c(NA, -105L), class = c("tbl_df", 
                                                                                                                                                                  "tbl", "data.frame"))
                    
                    ADAE <-
                      structure(list(STUDYID = structure(c("XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", "XXXXXXXXX", 
                                                           "XXXXXXXXX"), label = "Study Identifier"), USUBJID = structure(c("1", 
                                                                                                                            "2", "2", "3", "4", "5", "5", "5", "6", "7", "8", "9", "10", 
                                                                                                                            "11", "12", "13", "13", "14", "15", "16", "16", "17", "17", "18", 
                                                                                                                            "18", "18", "19", "19", "20", "20", "21", "22", "23", "23", "23", 
                                                                                                                            "23", "23", "24", "24", "25", "26", "26", "26", "27", "27", "28", 
                                                                                                                            "28", "29", "29", "29", "30", "30", "31", "31", "32", "33", "33", 
                                                                                                                            "34", "35", "35", "36", "36", "36", "37", "38", "38", "38", "38", 
                                                                                                                            "39", "40", "41", "41", "42", "42", "43", "44", "44", "45", "46", 
                                                                                                                            "47", "48", "48", "48", "49", "49", "50", "51", "51", "52", "53", 
                                                                                                                            "54", "55", "56", "57", "58", "58", "59", "60", "60", "60", "61", 
                                                                                                                            "62", "62", "63", "63", "63", "63", "63", "64", "65", "66", "67", 
                                                                                                                            "68", "69", "70", "70", "71", "71", "72", "73", "74", "74", "74", 
                                                                                                                            "74", "75", "76", "77", "78", "78", "78", "78", "78", "78", "79", 
                                                                                                                            "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "89", 
                                                                                                                            "89", "90", "91", "92", "93", "94", "95", "95", "95", "96", "97", 
                                                                                                                            "97", "98", "99", "99", "100", "100", "101", "102", "103", "104", 
                                                                                                                            "104", "105"), label = "Unique Subject Identifier"), AEDECOD = structure(c("AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm3", "AECodedterm2", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
                                                                                                                                                                                                       "AECodedterm2", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
                                                                                                                                                                                                       "AECodedterm2", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm2", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm3", "AECodedterm3", "AECodedterm3", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm2", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
                                                                                                                                                                                                       "AECodedterm2", "AECodedterm1", "AECodedterm3", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm3", "AECodedterm2", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm3", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm2", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm3", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm2", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm2", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm1", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm1", "AECodedterm1", "AECodedterm3", "AECodedterm1", 
                                                                                                                                                                                                       "AECodedterm2", "AECodedterm1", "AECodedterm2"), label = "Dictionary-Derived Term"), 
                                     AEBODSYS = structure(c("SOC1", "SOC3", "SOC2", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC2", 
                                                            "SOC2", "SOC1", "SOC1", "SOC2", "SOC2", "SOC1", "SOC1", "SOC1", 
                                                            "SOC2", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC3", "SOC3", "SOC3", 
                                                            "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC2", 
                                                            "SOC2", "SOC1", "SOC3", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC3", "SOC2", 
                                                            "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC3", "SOC1", "SOC2", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC3", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", 
                                                            "SOC1", "SOC1", "SOC2", "SOC1", "SOC1", "SOC1", "SOC2", "SOC1", 
                                                            "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC1", "SOC3", "SOC1", 
                                                            "SOC2", "SOC1", "SOC2"), label = "Body System or Organ Class"), 
                                     AETOXGR = structure(c(4L, 2L, 2L, 4L, 2L, 3L, 4L, 3L, 3L, 
                                                           4L, 4L, 3L, 3L, 4L, 4L, 3L, 4L, 4L, 5L, 1L, 3L, 4L, 3L, 4L, 
                                                           3L, 3L, 4L, 3L, 3L, 2L, 4L, 4L, 3L, 3L, 4L, 4L, 3L, 2L, 4L, 
                                                           2L, 4L, 4L, 4L, 4L, 3L, 3L, 2L, 4L, 3L, 4L, 4L, 2L, 3L, 3L, 
                                                           3L, 3L, 4L, 3L, 4L, 3L, 3L, 4L, 4L, 2L, 4L, 3L, 4L, 3L, 4L, 
                                                           4L, 5L, 6L, 3L, 4L, 3L, 1L, 3L, 3L, 4L, 2L, 2L, 4L, 3L, 2L, 
                                                           3L, 4L, 4L, 6L, 3L, 4L, 4L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 4L, 
                                                           3L, 3L, 5L, 6L, 3L, 4L, 3L, 3L, 4L, 3L, 3L, 3L, 3L, 2L, 3L, 
                                                           1L, 4L, 5L, 6L, 4L, 3L, 4L, 3L, 4L, 3L, 4L, 3L, 4L, 4L, 4L, 
                                                           3L, 3L, 5L, 4L, 4L, 4L, 3L, 3L, 3L, 4L, 3L, 4L, 5L, 5L, 4L, 
                                                           5L, 6L, 4L, 4L, 4L, 4L, 3L, 3L, 3L, 3L, 4L, 3L, 4L, 4L, 3L, 
                                                           3L, 3L, 3L, 4L, 3L, 5L, 3L, 4L, 3L), levels = c("<Missing>", 
                                                                                                           "1", "2", "3", "4", "5"), class = "factor", label = "Standard Toxicity Grade"), 
                                     TRT01A = structure(c(2L, 1L, 1L, 2L, 1L, 3L, 3L, 3L, 1L, 
                                                          1L, 3L, 1L, 2L, 2L, 3L, 3L, 3L, 1L, 2L, 3L, 3L, 1L, 1L, 1L, 
                                                          1L, 1L, 2L, 2L, 3L, 3L, 1L, 3L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 
                                                          1L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
                                                          1L, 1L, 1L, 2L, 3L, 3L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 1L, 
                                                          1L, 2L, 2L, 3L, 3L, 2L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 1L, 
                                                          1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 2L, 2L, 1L, 1L, 1L, 
                                                          1L, 1L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 1L, 3L, 1L, 2L, 
                                                          3L, 3L, 1L, 1L, 1L, 3L, 1L, 1L, 1L, 1L, 1L, 2L, 3L, 1L, 1L, 
                                                          1L, 1L, 1L, 1L, 2L, 1L, 1L, 2L, 2L, 1L, 3L, 1L, 1L, 2L, 1L, 
                                                          1L, 1L, 1L, 2L, 3L, 1L, 1L, 2L, 2L, 2L, 3L, 2L, 2L, 3L, 2L, 
                                                          2L, 1L, 1L, 3L, 1L, 3L, 3L, 3L, 1L), levels = c("Dummy A", 
                                                                                                          "Dummy B", "Dummy C"), class = "factor")), row.names = c(NA, 
                                                                                                                                                                   -168L), label = "Adverse Event Analysis Dataset", class = c("tbl_df", 
                                                                                                                                                                                                                               "tbl", "data.frame"))
                  })

join_keys(my_data) <- join_keys(
  join_key("ADSL", keys = c("STUDYID", "USUBJID")),
  join_key("ADAE", keys = c("STUDYID", "USUBJID")),
  
  join_key(
    "ADSL",
    "ADAE",
    keys = c("STUDYID" = "STUDYID", "USUBJID" = "USUBJID")
  )
)

app <- init(
  data = my_data,
  modules = modules(
    tm_t_events_by_grade(
      label = "Adverse Events by Grade Table",
      dataname = "ADAE",
      arm_var = choices_selected(c("TRT01A"), "TRT01A"),
      llt = choices_selected(
        choices = variable_choices("ADAE", c("AEDECOD")),
        selected = c("AEDECOD")
      ),
      hlt = choices_selected(
        choices = variable_choices("ADAE", c("AEBODSYS")),
        selected = "AEBODSYS"
      ),
      grade = choices_selected(
        choices = variable_choices("ADAE", c("AETOXGR")),
        selected = "AETOXGR"
      )
    )
  )
)
if (interactive()) {
  shinyApp(app$ui, app$server)
}

@llrs-roche llrs-roche added the sme label Jan 10, 2025
@jenko1979
Copy link
Author

jenko1979 commented Jan 10, 2025

Hi, yes, when you deselect missing's (as i mentioned in my document) it makes no difference for the initial table, still 82.7%, yet the transposed version is 79%. This doesn't make sense. There are no missing's for this particular AEcodedterm2 - can you inform me how it arrives at 79% (ie what is being used as num. and denom.)?

I guess what I'm saying is when you filter out missing's in the module - both tables (original and transposed versions) should surely be showing the same thing (ie 43/52=82.7%) for the 'any grade' column.

@llrs-roche
Copy link
Contributor

@jenko1979 I'm not familiar with the part of the code base responsible of these calculations, I hope other people can help you better.

@llrs-roche
Copy link
Contributor

If one checks the code produced by teal, the relevant results is calculated by:

....
lyt <- rtables::basic_table(title = "Adverse Event summary by : Body System or Organ Class and Dictionary-Derived Term") %>%
  rtables::split_cols_by(var = "TRT01A", split_fun = add_overall_level("All Patients", first = FALSE)) %>%
  split_cols_by_groups("MAXAETOXGR", groups_list = list(`Any Grade (%)` = c("1", "2", "3", "4", "5"), `Grade 1-2 (%)` = c("1", "2"), `Grade 3-4 (%)` = c("3", "4"), `Grade 5 (%)` = "5")) %>%
  rtables::split_rows_by("AEBODSYS", child_labels = "visible", nested = FALSE, split_fun = trim_levels_in_group("AEDECOD")) %>%
  append_varlabels(df = anl, vars = "AEBODSYS") %>%
  summarize_num_patients(var = "USUBJID", .stats = "unique", .labels = "Total number of patients with at least one adverse event", ) %>%
  analyze_vars("AEDECOD", na.rm = FALSE, denom = "N_col", .stats = "count_fraction", .formats = c(count_fraction = format_fraction_threshold(0.01))) %>%
  append_varlabels(df = anl, vars = "AEDECOD", indent = 1L)
result <- rtables::build_table(lyt = lyt, df = anl, col_counts = col_counts)
result

So it seems the issue is with rtables, as no teal.modules.clinical code is needed to reproduce the issue (but it might need to be updated to calculate the right value). Could you @jenko1979 explore more about this issue, with this information? In any case I hope @insightsengineering/nest-sme can help on how to update this module to fix this issue.

@Melkiades
Copy link
Contributor

If one checks the code produced by teal, the relevant results is calculated by:

....
lyt <- rtables::basic_table(title = "Adverse Event summary by : Body System or Organ Class and Dictionary-Derived Term") %>%
rtables::split_cols_by(var = "TRT01A", split_fun = add_overall_level("All Patients", first = FALSE)) %>%
split_cols_by_groups("MAXAETOXGR", groups_list = list(Any Grade (%) = c("1", "2", "3", "4", "5"), Grade 1-2 (%) = c("1", "2"), Grade 3-4 (%) = c("3", "4"), Grade 5 (%) = "5")) %>%
rtables::split_rows_by("AEBODSYS", child_labels = "visible", nested = FALSE, split_fun = trim_levels_in_group("AEDECOD")) %>%
append_varlabels(df = anl, vars = "AEBODSYS") %>%
summarize_num_patients(var = "USUBJID", .stats = "unique", .labels = "Total number of patients with at least one adverse event", ) %>%
analyze_vars("AEDECOD", na.rm = FALSE, denom = "N_col", .stats = "count_fraction", .formats = c(count_fraction = format_fraction_threshold(0.01))) %>%
append_varlabels(df = anl, vars = "AEDECOD", indent = 1L)
result <- rtables::build_table(lyt = lyt, df = anl, col_counts = col_counts)
result
So it seems the issue is with rtables, as no teal.modules.clinical code is needed to reproduce the issue (but it might need to be updated to calculate the right value). Could you @jenko1979 explore more about this issue, with this information? In any case I hope @insightsengineering/nest-sme can help on how to update this module to fix this issue.

Thanks @llrs-roche for flagging this issue. It seems to me that the two calculations may differ in what is filtered out at different splits. @jenko1979 could you copy the above code with the different data inputs and make a reproducible code snippet so we can close in the issue? I find also helpful to filter and grouping the data as if it is under different split, and then use dplyr::summarize(n = n()) to see the expected counts

@jenko1979
Copy link
Author

Hi, I must admit I haven't got experience with using col_counts = in the rtables framework as this is a depreciated feature I think. I can see that the numerator seems to still be 43 if i use this code below over my dummy data without giving a col_count argument, hence I really do think that its something to do with column counts being used and the denominator that is being used is incorrect in the transposed version, with the missing values playing a bit of a part in that.

I am not sure how MAXAETOXGR is being calculated either and whether this is causing any issues when you have missing's - this must be something that the Teal module is adding as this is not part of the variables in the ADAE contained in the reprex from the Teal module documentation.

I don't know how much more i can help, I guess i have already provided a complete reprex to show that the module is not giving the correct percentages in the transposed version of this table.

I will chat to @kpagacz about this - he may be able to help us identify where the issue is as we are working on something similar for a custom module for this internally.

@kpagacz
Copy link
Contributor

kpagacz commented Jan 21, 2025

@jenko1979 let's talk about this and see if and how I can help. Also, I am unsure if we are using the same things this module uses, so we might not be affected.

All in all, let's talk about:

  • whether our internal modules are affected
  • if they are, how can we debug this
  • if they are not, whether it makes sense for us to spend time debugging what looks like an rtables issue.

We can schedule a call for this :)

@yli110-stat697
Copy link
Contributor

yli110-stat697 commented Jan 22, 2025

Hi @kpagacz @jenko1979 @llrs-roche sorry for joining late to the party, but I was one of the developers to add the "transpose" feature in this module, so I just did a little experiment. It seemed the issue was from the numerator really. See my code below to reproduce the error.

anl <- ADAE %>% dplyr::group_by(USUBJID, TRT01A, AEBODSYS, AEDECOD) %>% dplyr::summarize(MAXAETOXGR = factor(max(as.numeric(AETOXGR))))

lyt <- rtables::basic_table(title = "Adverse Event summary by : Body System or Organ Class and Dictionary-Derived Term") %>%
    rtables::split_cols_by(var = "TRT01A", split_fun = add_overall_level("All Patients", first = FALSE)) %>%
    split_cols_by_groups("MAXAETOXGR", groups_list = list("Any Grade (%)" = c("1", "2", "3", "4", "5"), "Grade 1-2 (%)" = c("1", "2"), "Grade 3-4 (%)" = c("3", "4"), "Grade 5 (%)" = "5")) %>%
    rtables::split_rows_by("AEBODSYS", child_labels = "visible", nested = FALSE, split_fun = trim_levels_in_group("AEDECOD")) %>%
    append_varlabels(df = anl, vars = "AEBODSYS") %>%
    summarize_num_patients(var = "USUBJID", .stats = "unique") 

result <- rtables::build_table(lyt = lyt, df = anl, col_counts = c(rep(52,4), rep(28,4), rep(25, 4), rep(105, 4)))
Image

@yli110-stat697
Copy link
Contributor

yli110-stat697 commented Jan 22, 2025

Hi @jenko1979 I think I figured the reason. Your ADAE has grade of 6. We only accept Grade 1-5, so the transpose version is missing out the records with grade being "6". Can you try updating your grading_groups argument in tm_t_events_by_grade to something like

tm_t_events_by_grade(
## all your other args stay the same,
grading_groups = list(
                                   "Any Grade (%)" = c("1", "2", "3", "4", "5", "6"),
                                   "Grade 1-2 (%)" = c("1", "2"),
                                   "Grade 3-4 (%)" = c("3", "4"),
                                   "Grade 5-6 (%)" = c("5", "6")
                                 ),
)

@jenko1979
Copy link
Author

Hi @yli110-stat697 , I think the grade 6 thing is a bit of a red herring. I don't actually have any grade 6's in the real data i am using, but something funny happened when i created the dummy data for the reprex.

Its odd as when i look at the real data there are no grade 6's either, but when i take a dput of my real data it shows 1-6, but these are just factor levels, not the actual grade values. so i think what is happening here is that indeed it is causing an issue because of the factor levels currently in the data are not the same as the grades, and the module must be using the factor levels

so the levels are:

Missing = 1
1 = 2
2 = 3
3 = 4
4 = 5
5 = 6

So what is happening therefore is that the transposed version is not using 6 even though that is actually a grade 5 value.
So i guess I will have to re-order the factor levels so we have:

1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
Missing = 6

I will try this and see if this fixes my issues.

A little bit off topic, but relevant to this transposed module, is there a way to get the n (%) rather than just the % in the transposed version? I think this would be useful as well

@jenko1979
Copy link
Author

Hi, so the issue can be resolved now by me changing the factor levels in AETOXGR. I think because when we are doing the transposed version and rtables is now using a split_cols_by I think its the levels that are being used now, so its imperative that these match the grades you expect - i don't think its necessarily the same issue when these are coming out as data rows.

Anyway, still a worthwhile discussion as if anyone else reports something similar it will most likely be that their levels in AETOXGR are incorrect (or not matching the grade they expect to see)

@kpagacz
Copy link
Contributor

kpagacz commented Jan 23, 2025

Nicely done figuring this one out, Paul!

@llrs-roche
Copy link
Contributor

llrs-roche commented Jan 23, 2025

The module could check if the factors are within what we expect so that users like you have an easier time to uncover this. For instance in this case the conversion to detect the max value is with:

expr = dplyr::summarize(MAXAETOXGR = factor(max(as.numeric(grade)))),

Instead of the recommended as.numeric(levels(f))[f] (from ?as.factor documentation), that results in a warning and a different value (within what is expected):

factor(max(as.numeric(ADAE$AETOXGR)))
## [1] 6
## Levels: 6
factor(max(as.numeric((as.numeric(levels(ADAE$AETOXGR))[ADAE$AETOXGR])), na.rm = TRUE))
## [1] 5
## Levels: 5
## Warning message:
## In factor(max(as.numeric((as.numeric(levels(ADAE$AETOXGR))[ADAE$AETOXGR])),  :
##   NAs introduced by coercion

@yli110-stat697
Copy link
Contributor

Instead of the recommended as.numeric(levels(f))[f] (from ?as.factor documentation), that results in a warning and a different value (within what is expected):

Hi @llrs-roche I don't know the current development guidance on teal.modules.clinical since I have stopped the work for almost two years now, but we used to avoid warnings or error during execution phase. It worth a check upfront to see if grading_groups passed by user covers all the possible gradings levels

@yli110-stat697
Copy link
Contributor

A little bit off topic, but relevant to this transposed module, is there a way to get the n (%) rather than just the % in the transposed version? I think this would be useful as well

Hi @jenko1979 so we hardcoded the format here. It was due to some internal requests of a certain format, but I agree, user should have the ability to change into the formats they preferred. This could be a feature enhancement for the teal.modules.clinical team.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working core sme
Projects
None yet
Development

No branches or pull requests

5 participants