79581386

Date: 2025-04-18 16:16:23
Score: 1.5
Natty:
Report link

Dan, I followed your approach to re-build the table as I was trying to do this for multiple PARAMCDs as well. Below is my updated code.

## Load all required packages
packages <- c("cards", "tidyverse", "gtsummary", "labelled", "gt")
invisible(lapply(packages, library, character.only = TRUE))

# Apply gtsummary theme
theme_gtsummary_compact()

# Load data
advs <- pharmaverseadam::advs %>%
  filter(SAFFL == "Y" & VSTESTCD %in% c('SYSBP', "DIABP") & !is.na(AVISIT)) %>%
  select(c(USUBJID, TRT01A, PARAMCD, PARAM, AVISIT, AVISITN, ADT, AVAL, CHG, PCHG, VSPOS, VSTPT))

# Summary mean prior to process
advs.smr <- advs %>%
  group_by(USUBJID, TRT01A, PARAMCD, PARAM, AVISIT, AVISITN, ADT) %>%
  summarise(AVAL.MEAN = mean(AVAL, na.rm = TRUE),
            CHG.MEAN = mean(CHG, na.rm = TRUE),
            .groups = 'drop') %>%
  mutate(visit_id = paste("Vis", sprintf("%03d", AVISITN), AVISIT, sep = "_")) %>%
  arrange(USUBJID, PARAMCD, AVISITN) %>%
  filter(AVISITN <= 4)

# Wide to Long
advs.smr.l <- advs.smr %>%
  pivot_longer(cols = c(AVAL.MEAN, CHG.MEAN),
               names_to = "anls_var",
               values_to = "Value") %>%
  filter(!is.nan(Value)) %>%
  mutate(anls_var = if_else(grepl("AVAL", anls_var), "Actual Value", "Change From Baseline"))

# Long to Wide
advs.smr.w <- advs.smr.l %>%
  select(-c(AVISITN, AVISIT, ADT)) %>%
  pivot_wider(names_from = visit_id,
              values_from = Value)

# Upcase column names
colnames(advs.smr.w) <- toupper(colnames(advs.smr.w))

# Create List of visit names
alvis <- unique(colnames(advs.smr.w)[grep("^VIS", colnames(advs.smr.w), ignore.case = TRUE)])
vis.nam <- setNames(as.list(sub(".*_", "", alvis)), alvis)

# Table for AVAL by Visit
rpt_body <- function(res.typ) {

  # Filter for AVAL/CHG
  tmp.dat <- advs.smr.w  %>%
    filter(grepl(res.typ, ANLS_VAR)) %>%
    select(where(~ !all(is.na(.))))

  # Create table body
  tbl.body <- tmp.dat %>%
    tbl_strata_nested_stack(
      strata = PARAM,
      ~ .x %>%
        tbl_summary(
          by = TRT01A,
          include = c(starts_with("VIS")),
          type = all_continuous() ~ "continuous2",
          statistic = all_continuous() ~ c("{N_nonmiss}", "{mean} ({sd})",
                                           "{median}", "{min}, {max}"),
          digits = all_continuous() ~ c(N_nonmiss = 0, mean = 2, sd = 2,
                                        median = 2, min = 2, max = 2),
          label = vis.nam,
          missing = "no") %>%
        # Update Stat Labels
        add_stat_label(
          label = list(all_continuous() ~ c("n", "MEAN (SD)", "MEDIAN", "MIN, MAX")))
      )

  return(tbl.body)
}

# Create table summary
tbl.aval <- rpt_body(res.typ = "Actual")
tbl.chg <- rpt_body(res.typ = "Change")

# Merge tables together and apply styling
vs.tbl <- list(tbl.aval, tbl.chg) %>%
  tbl_merge(tab_spanner = FALSE,
            merge_vars = c("tbl_id1", "variable", "row_type", "var_label", "label")) %>%
  # Update spanning header with TRT (level from tbl_summary by)
  modify_spanning_header(all_stat_cols() ~ "**{level}**  \n(N = {n})") %>%
  # Update header
  modify_header(
    label ~ "*Vital Signs Parameter* \n\U0A0\U0A0\U0A0\U0A0**Visit**",
    all_stat_cols() & ends_with("_1") ~ "**Actual Value**",
    all_stat_cols() & ends_with("_2") ~ "***Change from Baseline***") %>%
  # Update header
  modify_table_body(
    ~ .x %>%
      dplyr::mutate(tbl_id1_1 = tbl_id1,
                    tbl_id1_2 = tbl_id1) %>%
      dplyr::relocate(
        c(starts_with("stat_1"), starts_with("stat_2"), starts_with("stat_3")),
        .after = "label")
    )

There is one small issue. The header counts on spanning header for AVAL & CHG are different (screenshot below). I want to display the counts from AVAL, but not too sure how to achieve this.

enter image description here

Reasons:
  • Blacklisted phrase (1): how to achieve
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: BlueKnight2025