rgtsummary

GTSUMMARY Table with TBL_STRATA2: Zero count rows & Level ordering issue


This is a continuation of my question posted here. I am still facing some issues with usage of tbl_summary. For code below, I have forced TRT01A, PARAMCD etc. as factors. This helps me generate consistent shell data with zero counts and provides ability to control the display order in table body.

# Load data
advs <- pharmaverseadam::advs %>%
  filter(SAFFL == "Y" & !is.na(ANRIND)) %>%
  select(c(USUBJID, TRT01A, PARAMCD, PARAM, AVISIT, AVISITN, ADT, AVAL, ANRIND)) |> 
  mutate(TRT01AN = case_when(
    TRT01A == 'Xanomeline High Dose' ~ 1,
    TRT01A == 'Xanomeline Low Dose' ~ 2,
    TRT01A == 'Placebo' ~ 3
  ))

# Summary prior to process
advs.smr <- advs %>%
  group_by(USUBJID, TRT01A, TRT01AN, PARAMCD, PARAM) %>%
  # Can use slice_max or summarize
  summarise(AVL.NRIND = max(ANRIND, na.rm = TRUE), .groups = 'drop') |> 
  arrange(USUBJID, PARAMCD) |>
  mutate(
    TRT01A = factor(TRT01A, levels = unique(TRT01A[order(TRT01AN)])),
    PARAMCD = factor(PARAMCD, levels = c("SYSBP", "DIABP", "TEMP")),
    AVL.NRIND = factor(AVL.NRIND, levels = sort(unique(AVL.NRIND))[order(c(3,2,1))])
  ) |> 
  filter((PARAMCD == 'SYSBP' & !grepl("2$", USUBJID)) | (PARAMCD == 'DIABP' & !grepl("3$", USUBJID))|(PARAMCD == 'TEMP' & !grepl("1$", USUBJID)))

Using above source data tbl_summary with percent=adsl call have issues. From below code, tbl.smry1 can generate output successfully, whereas the tbl.smry2 fails.

tbl.smry1 <- advs.smr |> 
  tbl_strata2(
    strata = PARAMCD,
    ~ .x |> 
      tbl_summary(
        include = AVL.NRIND,
        by = TRT01A,
        label = list(AVL.NRIND = .y),
        percent = "column"
      ) |> 
      add_overall(last = TRUE),
    .combine_with = "tbl_stack",
    .combine_args = list(group_header = NULL)
  )

tbl.smry2 <- advs.smr |> 
  tbl_strata2(
    strata = PARAMCD,
    ~ .x |> 
      tbl_summary(
        include = AVL.NRIND,
        by = TRT01A,
        label = list(AVL.NRIND = .y),
        percent = adsl
      ) |> 
      add_overall(last = TRUE),
    .combine_with = "tbl_stack",
    .combine_args = list(group_header = NULL)
  )

Please let me know if I am missing something here. Output from tbl.smry1 below with zero count rows and PARAMCD, Treatment, ANRIND levels in order as forced using factor. enter image description here


Solution

  • I think there were two issues here:

    1. adsl was missing the pharmaverseadam::adsl prefix.
    2. The treatment variable was character in adsl and a factor in advs. (I should have better messaging when there is a mis-match, however. I'll add that.)
    library(tidyverse)
    library(gtsummary)
    packageVersion("gtsummary")
    #> [1] '2.4.0.9002'
    
    advs <- pharmaverseadam::advs %>%
      filter(SAFFL == "Y", !is.na(ANRIND), PARAMCD %in% c("SYSBP", "DIABP", "TEMP")) %>%
      select(c(USUBJID, TRT01A, PARAMCD, PARAM, AVISIT, AVISITN, ADT, AVAL, ANRIND)) |> 
      mutate(
        TRT01A = factor(TRT01A, levels = c('Xanomeline High Dose', 'Xanomeline Low Dose', 'Placebo'))
      )
    
    # Summary prior to process
    advs.smr <- advs %>%
      group_by(USUBJID, TRT01A, PARAMCD, PARAM) %>%
      # Can use slice_max or summarize
      summarise(AVL.NRIND = max(ANRIND, na.rm = TRUE), .groups = 'drop') |> 
      mutate(
        PARAMCD = factor(PARAMCD, levels = c("SYSBP", "DIABP", "TEMP")),
        AVL.NRIND = factor(AVL.NRIND, levels = sort(unique(AVL.NRIND))[order(c(3,2,1))])
      )
    
    advs.smr |> 
      tbl_strata2(
        strata = PARAMCD,
        ~ .x |> 
          tbl_summary(
            include = AVL.NRIND,
            by = TRT01A,
            label = list(AVL.NRIND = .y),
            percent = 
              pharmaverseadam::adsl |> 
              mutate(
                TRT01A = factor(TRT01A, levels = c('Xanomeline High Dose', 'Xanomeline Low Dose', 'Placebo'))
              )
          ) |> 
          add_overall(last = TRUE),
        .combine_with = "tbl_stack",
        .combine_args = list(group_header = NULL)
      ) |> 
      bold_labels() |> 
      # convert to markdown to display on stackoverflow
      as_kable()
    
    Characteristic Xanomeline High Dose N = 72 Xanomeline Low Dose N = 96 Placebo N = 86 Overall N = 306
    SYSBP
    NORMAL 67 (93%) 84 (88%) 81 (94%) 232 (76%)
    LOW 0 (0%) 0 (0%) 0 (0%) 0 (0%)
    HIGH 5 (6.9%) 12 (13%) 5 (5.8%) 22 (7.2%)
    DIABP
    NORMAL 71 (99%) 95 (99%) 84 (98%) 250 (82%)
    LOW 0 (0%) 0 (0%) 1 (1.2%) 1 (0.3%)
    HIGH 1 (1.4%) 1 (1.0%) 1 (1.2%) 3 (1.0%)
    TEMP
    NORMAL 70 (97%) 88 (92%) 85 (99%) 243 (79%)
    LOW 2 (2.8%) 8 (8.3%) 1 (1.2%) 11 (3.6%)
    HIGH 0 (0%) 0 (0%) 0 (0%) 0 (0%)

    Created on 2025-10-07 with reprex v2.1.1