TLG Catalog - Stable
  • Stable
    • Dev
  1. Tables
  2. Adverse Events
  3. AET06_SMQ
  • Introduction

  • Tables
    • ADA
      • ADAT01
      • ADAT02
      • ADAT03
      • ADAT04A
      • ADAT04B
    • Adverse Events
      • AET01
      • AET01_AESI
      • AET02
      • AET02_SMQ
      • AET03
      • AET04
      • AET04_PI
      • AET05
      • AET05_ALL
      • AET06
      • AET06_SMQ
      • AET07
      • AET09
      • AET09_SMQ
      • AET10
    • Concomitant Medications
      • CMT01
      • CMT01A
      • CMT01B
      • CMT02_PT
    • Deaths
      • DTHT01
    • Demography
      • DMT01
    • Disclosures
      • DISCLOSUREST01
      • EUDRAT01
      • EUDRAT02
    • Disposition
      • DST01
      • PDT01
      • PDT02
    • ECG
      • EGT01
      • EGT02
      • EGT03
      • EGT04
      • EGT05_QTCAT
    • Efficacy
      • AOVT01
      • AOVT02
      • AOVT03
      • CFBT01
      • CMHT01
      • COXT01
      • COXT02
      • DORT01
      • LGRT02
      • MMRMT01
      • ONCT05
      • RATET01
      • RBMIT01
      • RSPT01
      • TTET01
    • Exposure
      • EXT01
    • Lab Results
      • LBT01
      • LBT02
      • LBT03
      • LBT04
      • LBT05
      • LBT06
      • LBT07
      • LBT08
      • LBT09
      • LBT10
      • LBT10_BL
      • LBT11
      • LBT11_BL
      • LBT12
      • LBT12_BL
      • LBT13
      • LBT14
      • LBT15
    • Medical History
      • MHT01
    • Pharmacokinetic
      • PKCT01
      • PKPT02
      • PKPT03
      • PKPT04
      • PKPT05
      • PKPT06
      • PKPT07
      • PKPT08
      • PKPT11
    • Risk Management Plan
      • RMPT01
      • RMPT03
      • RMPT04
      • RMPT05
      • RMPT06
    • Safety
      • ENTXX
    • Vital Signs
      • VST01
      • VST02
  • Listings
    • ADA
      • ADAL02
    • Adverse Events
      • AEL01
      • AEL01_NOLLT
      • AEL02
      • AEL02_ED
      • AEL03
      • AEL04
    • Concomitant Medications
      • CML01
      • CML02A_GL
      • CML02B_GL
    • Development Safety Update Report
      • DSUR4
    • Disposition
      • DSL01
      • DSL02
    • ECG
      • EGL01
    • Efficacy
      • ONCL01
    • Exposure
      • EXL01
    • Lab Results
      • LBL01
      • LBL01_RLS
      • LBL02A
      • LBL02A_RLS
      • LBL02B
    • Medical History
      • MHL01
    • Pharmacokinetic
      • ADAL01
      • PKCL01
      • PKCL02
      • PKPL01
      • PKPL02
      • PKPL04
    • Vital Signs
      • VSL01
  • Graphs
    • Efficacy
      • FSTG01
      • FSTG02
      • KMG01
      • MMRMG01
      • MMRMG02
    • Other
      • BRG01
      • BWG01
      • CIG01
      • IPPG01
      • LTG01
      • MNG01
    • Pharmacokinetic
      • PKCG01
      • PKCG02
      • PKCG03
      • PKPG01
      • PKPG02
      • PKPG03
      • PKPG04
      • PKPG06

  • Appendix
    • Reproducibility

  • Index

On this page

  • Output
  • teal App
  • Reproducibility
    • Timestamp
    • Session Info
    • .lock file
  • Edit this page
  • Report an issue
  1. Tables
  2. Adverse Events
  3. AET06_SMQ

AET06_SMQ

Adverse Events by Baseline Characteristic, by SMQ and Preferred Term


Output

  • Adverse Events by Sex,
    by SMQ and Preferred Term
  • Adverse Events by Sex, by SMQ and
    Preferred Term (with Customized Queries)
  • Adverse Events by Other Baseline
    Characteristics (e.g. Age Group)
  • Data Setup
  • Preview
  • Try this using WebR
Code
criteria_fun <- function(tr) !is(tr, "ContentRow") && all_zero_or_na(tr)

lyt <- basic_table(show_colcounts = TRUE) %>%
  split_cols_by("ACTARMCD") %>%
  split_cols_by("SEX") %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique"),
    .labels = c(unique = "Total number of patients with at least one adverse event")
  ) %>%
  split_rows_by(
    "SMQ",
    child_labels = "visible",
    nested = FALSE,
    split_fun = trim_levels_in_group("AEDECOD", drop_outlevs = FALSE),
    label_pos = "topleft",
    split_label = "Standardized MedDRA Query"
  ) %>%
  summarize_num_patients(
    var = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique = "Total number of patients with at least one adverse event",
      nonunique = "Total number of events"
    )
  ) %>%
  count_occurrences(vars = "AEDECOD", drop = FALSE) %>%
  append_varlabels(adae_smq_1, "AEDECOD", indent = 1L)

result <- build_table(
  lyt = lyt,
  df = adae_smq_1,
  alt_counts_df = adsl
) %>%
  sort_at_path(path = c("SMQ"), scorefun = cont_n_allcols) %>%
  sort_at_path(path = c("SMQ", "*", "AEDECOD"), scorefun = score_occurrences, na.pos = "last") %>%
  trim_rows(criteria = criteria_fun)

result
                                                                      ARM A                     ARM B                     ARM C         
Standardized MedDRA Query                                        F            M            F            M            F            M     
  Dictionary-Derived Term                                      (N=79)       (N=55)       (N=82)       (N=52)       (N=70)       (N=62)  
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Total number of patients with at least one adverse event     47 (59.5%)   25 (45.5%)   51 (62.2%)   28 (53.8%)   42 (60.0%)   33 (53.2%)
C.1.1.1.3/B.2.2.3.1 AESI(BROAD)                                                                                                         
  Total number of patients with at least one adverse event   47 (59.5%)   25 (45.5%)   51 (62.2%)   28 (53.8%)   42 (60.0%)   33 (53.2%)
  Total number of events                                         79           40           95           44           81           60    
    dcd B.2.2.3.1                                            30 (38.0%)   18 (32.7%)   32 (39.0%)   22 (42.3%)   26 (37.1%)   25 (40.3%)
    dcd C.1.1.1.3                                            30 (38.0%)   13 (23.6%)   36 (43.9%)   10 (19.2%)   27 (38.6%)   16 (25.8%)
Experimental use!

WebR is a tool allowing you to run R code in the web browser. Modify the code below and click run to see the results. Alternatively, copy the code and click here to open WebR in a new tab.

  • Preview
  • Try this using WebR
Code
criteria_fun <- function(tr) {
  !is(tr, "ContentRow") && all_zero_or_na(tr) && !grepl("Total number of", obj_label(tr))
}

lyt <- basic_table(show_colcounts = TRUE) %>%
  split_cols_by("ACTARMCD") %>%
  split_cols_by("SEX") %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique"),
    .labels = c(unique = "Total number of patients with at least one adverse event")
  ) %>%
  split_rows_by(
    "SMQ",
    child_labels = "visible",
    nested = FALSE,
    split_fun = trim_levels_in_group("AEDECOD", drop_outlevs = FALSE),
    label_pos = "topleft",
    split_label = "Standardized MedDRA Query"
  ) %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique = "Total number of patients with at least one adverse event",
      nonunique = "Total number of events"
    ),
    show_labels = "hidden"
  ) %>%
  count_occurrences(vars = "AEDECOD", drop = FALSE) %>%
  append_varlabels(adae_smq_all, "AEDECOD", indent = 1L)

score_ae_pts <- function(tt) {
  count_vals <- cell_values(tree_children(tt)[["USUBJID"]])[[1]]
  sum(matrix(unlist(count_vals), nrow = length(count_vals), byrow = TRUE)[, 1])
}

result <- build_table(
  lyt = lyt,
  df = adae_smq_all,
  alt_counts_df = adsl
) %>%
  sort_at_path(path = c("SMQ"), scorefun = score_ae_pts) %>%
  sort_at_path(path = c("SMQ", "*", "AEDECOD"), scorefun = score_occurrences, na.pos = "last") %>%
  trim_rows(criteria = criteria_fun)

result
                                                                      ARM A                     ARM B                     ARM C         
Standardized MedDRA Query                                        F            M            F            M            F            M     
  Dictionary-Derived Term                                      (N=79)       (N=55)       (N=82)       (N=52)       (N=70)       (N=62)  
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Total number of patients with at least one adverse event     59 (74.7%)   36 (65.5%)   68 (82.9%)   35 (67.3%)   61 (87.1%)   47 (75.8%)
D.2.1.5.3/A.1.1.1.1 AESI                                                                                                                
  Total number of patients with at least one adverse event   45 (57.0%)   29 (52.7%)   54 (65.9%)   26 (50.0%)   50 (71.4%)   37 (59.7%)
  Total number of events                                         78           48           91           43           88           74    
  dcd D.2.1.5.3                                              26 (32.9%)   21 (38.2%)   40 (48.8%)   18 (34.6%)   34 (48.6%)   23 (37.1%)
  dcd A.1.1.1.1                                              34 (43.0%)   16 (29.1%)   31 (37.8%)   14 (26.9%)   33 (47.1%)   30 (48.4%)
C.1.1.1.3/B.2.2.3.1 AESI(BROAD)                                                                                                         
  Total number of patients with at least one adverse event   47 (59.5%)   25 (45.5%)   51 (62.2%)   28 (53.8%)   42 (60.0%)   33 (53.2%)
  Total number of events                                         79           40           95           44           81           60    
  dcd B.2.2.3.1                                              30 (38.0%)   18 (32.7%)   32 (39.0%)   22 (42.3%)   26 (37.1%)   25 (40.3%)
  dcd C.1.1.1.3                                              30 (38.0%)   13 (23.6%)   36 (43.9%)   10 (19.2%)   27 (38.6%)   16 (25.8%)
non observed SMQ02NAM level(BROAD)                                                                                                      
  Total number of patients with at least one adverse event       0            0            0            0            0            0     
  Total number of events                                         0            0            0            0            0            0     
Experimental use!

WebR is a tool allowing you to run R code in the web browser. Modify the code below and click run to see the results. Alternatively, copy the code and click here to open WebR in a new tab.

  • Preview
  • Try this using WebR
Code
criteria_fun <- function(tr) !is(tr, "ContentRow") && all_zero_or_na(tr)

lyt <- basic_table(show_colcounts = TRUE) %>%
  split_cols_by("ACTARMCD") %>%
  split_cols_by("AGE65") %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique"),
    .labels = c(unique = "Total number of patients with at least one adverse event")
  ) %>%
  split_rows_by(
    "SMQ",
    child_labels = "visible",
    nested = FALSE,
    split_fun = trim_levels_in_group("AEDECOD", drop_outlevs = FALSE),
    label_pos = "topleft",
    split_label = "Standardized MedDRA Query"
  ) %>%
  summarize_num_patients(
    var = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique = "Total number of patients with at least one adverse event",
      nonunique = "Total number of events"
    )
  ) %>%
  count_occurrences(vars = "AEDECOD", drop = FALSE) %>%
  append_varlabels(adae_smq_1, "AEDECOD", indent = 1L)

result <- build_table(
  lyt = lyt,
  df = adae_smq_1,
  alt_counts_df = adsl
) %>%
  sort_at_path(path = c("SMQ"), scorefun = cont_n_allcols) %>%
  sort_at_path(path = c("SMQ", "*", "AEDECOD"), scorefun = score_occurrences, na.pos = "last") %>%
  trim_rows(criteria = criteria_fun)

result
                                                                   ARM A                ARM B                ARM C       
Standardized MedDRA Query                                       < 65      >= 65      < 65      >= 65      < 65      >= 65
  Dictionary-Derived Term                                     (N=134)     (N=0)    (N=134)     (N=0)    (N=131)     (N=1)
—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Total number of patients with at least one adverse event     72 (53.7%)     0     79 (59.0%)     0     75 (57.3%)     0  
C.1.1.1.3/B.2.2.3.1 AESI(BROAD)                                                                                          
  Total number of patients with at least one adverse event   72 (53.7%)     0     79 (59.0%)     0     75 (57.3%)     0  
  Total number of events                                        119         0        139         0        141         0  
    dcd B.2.2.3.1                                            48 (35.8%)     0     54 (40.3%)     0     51 (38.9%)     0  
    dcd C.1.1.1.3                                            43 (32.1%)     0     46 (34.3%)     0     43 (32.8%)     0  
Experimental use!

WebR is a tool allowing you to run R code in the web browser. Modify the code below and click run to see the results. Alternatively, copy the code and click here to open WebR in a new tab.

Code
library(dplyr)
library(tern)
library(stringr)

adsl <- random.cdisc.data::cadsl
adae <- random.cdisc.data::cadae

adsl_labels <- var_labels(adsl)
adae_labels <- var_labels(adae)

adsl <- adsl %>%
  mutate(
    AGE65 = case_when(
      AGE >= 65 ~ ">= 65",
      TRUE ~ "< 65"
    ),
    AGE65 = factor(
      AGE65,
      levels = c("< 65", ">= 65")
    )
  )

adae <- adae %>%
  mutate(
    AGE65 = case_when(
      AGE >= 65 ~ ">= 65",
      TRUE ~ "< 65"
    ),
    AGE65 = factor(
      AGE65,
      levels = c("< 65", ">= 65")
    )
  )

# Ensure character variables are converted to factors and empty strings and NAs are explicit missing
# levels. For details, refer to Teal and Study Data article.
adsl <- df_explicit_na(adsl)
adae <- df_explicit_na(adae)

# Simulate a random AAG dataset.
aag <- data.frame(
  NAMVAR = c("SMQ01NAM", "SMQ01NAM", "SMQ02NAM", "CQ01NAM", "CQ01NAM"),
  SRCVAR = rep("AEDECOD", 5),
  GRPTYPE = c("SMQ", "SMQ", "SMQ", "CUSTOM", "CUSTOM"),
  REFID = c(1, 1, 2, 3, 3),
  REFNAME = c(rep("C.1.1.1.3/B.2.2.3.1 AESI", 2), "non observed SMQ02NAM level", rep("D.2.1.5.3/A.1.1.1.1 AESI", 2)),
  SCOPE = c("BROAD", "BROAD", "BROAD", "", ""),
  REFTERM = c("C.1.1.1.3", "B.2.2.3.1", "Z.9.9.9.9", "D.2.1.5.3", "A.1.1.1.1"),
  stringsAsFactors = FALSE
)

# Create summary AAG dataset (used for labelling).
# Note it's important to incorporate SCOPE into the basket names for SMQs so as to
# match the flags present in ADAE.
aag_summary <- aag %>%
  select(NAMVAR, REFNAME, SCOPE) %>%
  unique() %>%
  mutate(
    REFNAME_SCOPE = ifelse(SCOPE == "", REFNAME, paste0(REFNAME, "(", SCOPE, ")"))
  ) %>%
  rename(basket = NAMVAR, basket_name = REFNAME_SCOPE) %>%
  select(basket, basket_name)

# Make a summary of the full ADAE based on AAG by using h_stack_by_baskets helper function
adae_smq_all <- h_stack_by_baskets(
  df = adae,
  aag_summary = aag_summary,
  keys = c("STUDYID", "USUBJID", "ACTARMCD", "AEDECOD", "SEX", "AGE65")
)
# Post-process adae_smq_all to keep only certain baskets of interest for variant 1
# Not need to drop baskets for variant 2 as all baskets will be considered.
baskets_to_drop_variant <- aag_summary$basket_name[!aag_summary$basket %in% c("SMQ01NAM")]

# Adjust the analysis dataset based on basket subset for variant 1.
adae_smq_1 <- adae_smq_all %>%
  filter(!adae_smq_all$SMQ %in% baskets_to_drop_variant) %>%
  mutate(
    SMQ = tern::fct_discard(SMQ, discard = baskets_to_drop_variant)
  )

teal App

  • Preview
  • Try this using shinylive
Code
library(teal.modules.clinical)

## Data reproducible code
data <- teal_data()
data <- within(data, {
  library(dplyr)

  ADSL <- random.cdisc.data::cadsl %>%
    mutate(
      AGE65 = case_when(
        AGE >= 65 ~ ">= 65",
        TRUE ~ "< 65"
      ),
      AGE65 = factor(
        AGE65,
        levels = c("< 65", ">= 65")
      )
    )
  ADAE <- random.cdisc.data::cadae %>%
    mutate(
      AGE65 = case_when(
        AGE >= 65 ~ ">= 65",
        TRUE ~ "< 65"
      ),
      AGE65 = factor(
        AGE65,
        levels = c("< 65", ">= 65")
      )
    )
})
datanames <- c("ADSL", "ADAE")
datanames(data) <- datanames
Warning: `datanames<-()` was deprecated in teal.data 0.7.0.
ℹ invalid to use `datanames()<-` or `names()<-` on an object of class
  `teal_data`. See ?names.teal_data
Code
join_keys(data) <- default_cdisc_join_keys[datanames]

## Reusable Configuration For Modules
ADSL <- data[["ADSL"]]
ADAE <- data[["ADAE"]]

names_baskets <- grep("^(SMQ|CQ).*NAM$", names(ADAE), value = TRUE)
names_scopes <- grep("^SMQ.*SC$", names(ADAE), value = TRUE)

cs_baskets <- choices_selected(
  choices = variable_choices(ADAE, subset = names_baskets),
  selected = names_baskets
)
cs_scopes <- choices_selected(
  choices = variable_choices(ADAE, subset = names_scopes),
  selected = names_scopes,
  fixed = TRUE
)

## Setup App
app <- init(
  data = data,
  modules = modules(
    tm_t_smq(
      label = "Adverse events by `SMQ` Table",
      dataname = "ADAE",
      arm_var = choices_selected(
        choices = variable_choices(ADSL, subset = c("ARM", "SEX", "AGE65")),
        selected = c("ARM", "SEX")
      ),
      add_total = FALSE,
      baskets = cs_baskets,
      scopes = cs_scopes,
      llt = choices_selected(
        choices = variable_choices(ADAE, subset = c("AEDECOD")),
        selected = "AEDECOD"
      )
    )
  )
)

shinyApp(app$ui, app$server)

Experimental use!

shinylive allow you to modify to run shiny application entirely in the web browser. Modify the code below and click re-run the app to see the results. The performance is slighly worse and some of the features (e.g. downloading) might not work at all.

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 800
#| editorHeight: 200
#| components: [viewer, editor]
#| layout: vertical

# -- WEBR HELPERS --
options(webr_pkg_repos = c("r-universe" = "https://insightsengineering.r-universe.dev", getOption("webr_pkg_repos")))

# -- APP CODE --
library(teal.modules.clinical)

## Data reproducible code
data <- teal_data()
data <- within(data, {
  library(dplyr)

  ADSL <- random.cdisc.data::cadsl %>%
    mutate(
      AGE65 = case_when(
        AGE >= 65 ~ ">= 65",
        TRUE ~ "< 65"
      ),
      AGE65 = factor(
        AGE65,
        levels = c("< 65", ">= 65")
      )
    )
  ADAE <- random.cdisc.data::cadae %>%
    mutate(
      AGE65 = case_when(
        AGE >= 65 ~ ">= 65",
        TRUE ~ "< 65"
      ),
      AGE65 = factor(
        AGE65,
        levels = c("< 65", ">= 65")
      )
    )
})
datanames <- c("ADSL", "ADAE")
datanames(data) <- datanames
join_keys(data) <- default_cdisc_join_keys[datanames]

## Reusable Configuration For Modules
ADSL <- data[["ADSL"]]
ADAE <- data[["ADAE"]]

names_baskets <- grep("^(SMQ|CQ).*NAM$", names(ADAE), value = TRUE)
names_scopes <- grep("^SMQ.*SC$", names(ADAE), value = TRUE)

cs_baskets <- choices_selected(
  choices = variable_choices(ADAE, subset = names_baskets),
  selected = names_baskets
)
cs_scopes <- choices_selected(
  choices = variable_choices(ADAE, subset = names_scopes),
  selected = names_scopes,
  fixed = TRUE
)

## Setup App
app <- init(
  data = data,
  modules = modules(
    tm_t_smq(
      label = "Adverse events by `SMQ` Table",
      dataname = "ADAE",
      arm_var = choices_selected(
        choices = variable_choices(ADSL, subset = c("ARM", "SEX", "AGE65")),
        selected = c("ARM", "SEX")
      ),
      add_total = FALSE,
      baskets = cs_baskets,
      scopes = cs_scopes,
      llt = choices_selected(
        choices = variable_choices(ADAE, subset = c("AEDECOD")),
        selected = "AEDECOD"
      )
    )
  )
)

shinyApp(app$ui, app$server)

Reproducibility

Timestamp

[1] "2025-06-11 17:55:32 UTC"

Session Info

─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.0 (2025-04-11)
 os       Ubuntu 24.04.2 LTS
 system   x86_64, linux-gnu
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Etc/UTC
 date     2025-06-11
 pandoc   3.6.4 @ /usr/bin/ (via rmarkdown)
 quarto   1.7.31 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 package               * version   date (UTC) lib source
 backports               1.5.0     2024-05-23 [1] RSPM
 brio                    1.1.5     2024-04-24 [1] RSPM
 broom                   1.0.8     2025-03-28 [1] RSPM
 bslib                   0.9.0     2025-01-30 [1] RSPM
 cachem                  1.1.0     2024-05-16 [1] RSPM
 callr                   3.7.6     2024-03-25 [1] RSPM
 checkmate               2.3.2     2024-07-29 [1] RSPM
 chromote                0.5.1     2025-04-24 [1] RSPM
 cli                     3.6.5     2025-04-23 [1] RSPM
 coda                    0.19-4.1  2024-01-31 [1] CRAN (R 4.5.0)
 codetools               0.2-20    2024-03-31 [2] CRAN (R 4.5.0)
 curl                    6.3.0     2025-06-06 [1] RSPM
 dichromat               2.0-0.1   2022-05-02 [1] CRAN (R 4.5.0)
 digest                  0.6.37    2024-08-19 [1] RSPM
 dplyr                 * 1.1.4     2023-11-17 [1] RSPM
 emmeans                 1.11.1    2025-05-04 [1] RSPM
 estimability            1.5.1     2024-05-12 [1] RSPM
 evaluate                1.0.3     2025-01-10 [1] RSPM
 farver                  2.1.2     2024-05-13 [1] RSPM
 fastmap                 1.2.0     2024-05-15 [1] RSPM
 fontawesome             0.5.3     2024-11-16 [1] RSPM
 forcats                 1.0.0     2023-01-29 [1] RSPM
 formatR                 1.14      2023-01-17 [1] CRAN (R 4.5.0)
 formatters            * 0.5.11    2025-04-09 [1] RSPM
 geepack                 1.3.12    2024-09-23 [1] RSPM
 generics                0.1.4     2025-05-09 [1] RSPM
 ggplot2                 3.5.2     2025-04-09 [1] RSPM
 glue                    1.8.0     2024-09-30 [1] RSPM
 gtable                  0.3.6     2024-10-25 [1] RSPM
 htmltools               0.5.8.1   2024-04-04 [1] RSPM
 htmlwidgets             1.6.4     2023-12-06 [1] RSPM
 httpuv                  1.6.16    2025-04-16 [1] RSPM
 jquerylib               0.1.4     2021-04-26 [1] RSPM
 jsonlite                2.0.0     2025-03-27 [1] RSPM
 knitr                   1.50      2025-03-16 [1] RSPM
 later                   1.4.2     2025-04-08 [1] RSPM
 lattice                 0.22-7    2025-04-02 [2] CRAN (R 4.5.0)
 lifecycle               1.0.4     2023-11-07 [1] RSPM
 logger                  0.4.0     2024-10-22 [1] RSPM
 magrittr              * 2.0.3     2022-03-30 [1] RSPM
 MASS                    7.3-65    2025-02-28 [2] CRAN (R 4.5.0)
 Matrix                  1.7-3     2025-03-11 [1] CRAN (R 4.5.0)
 memoise                 2.0.1     2021-11-26 [1] RSPM
 mime                    0.13      2025-03-17 [1] RSPM
 multcomp                1.4-28    2025-01-29 [1] RSPM
 mvtnorm                 1.3-3     2025-01-10 [1] RSPM
 nestcolor               0.1.3     2025-01-21 [1] RSPM
 nlme                    3.1-168   2025-03-31 [2] CRAN (R 4.5.0)
 pillar                  1.10.2    2025-04-05 [1] RSPM
 pkgcache                2.2.4     2025-05-26 [1] RSPM
 pkgconfig               2.0.3     2019-09-22 [1] RSPM
 processx                3.8.6     2025-02-21 [1] RSPM
 promises                1.3.3     2025-05-29 [1] RSPM
 ps                      1.9.1     2025-04-12 [1] RSPM
 purrr                   1.0.4     2025-02-05 [1] RSPM
 R6                      2.6.1     2025-02-15 [1] RSPM
 random.cdisc.data       0.3.16    2024-10-10 [1] RSPM
 rbibutils               2.3       2024-10-04 [1] RSPM
 RColorBrewer            1.1-3     2022-04-03 [1] RSPM
 Rcpp                    1.0.14    2025-01-12 [1] RSPM
 Rdpack                  2.6.4     2025-04-09 [1] RSPM
 rlang                   1.1.6     2025-04-11 [1] RSPM
 rmarkdown               2.29      2024-11-04 [1] RSPM
 rtables               * 0.6.12    2025-04-11 [1] RSPM
 sandwich                3.1-1     2024-09-15 [1] RSPM
 sass                    0.4.10    2025-04-11 [1] RSPM
 scales                  1.4.0     2025-04-24 [1] RSPM
 sessioninfo             1.2.3     2025-02-05 [1] any (@1.2.3)
 shiny                 * 1.10.0    2024-12-14 [1] RSPM
 shinycssloaders         1.1.0     2024-07-30 [1] RSPM
 shinyjs                 2.1.0     2021-12-23 [1] RSPM
 shinyvalidate           0.1.3     2023-10-04 [1] RSPM
 shinyWidgets            0.9.0     2025-02-21 [1] RSPM
 stringi                 1.8.7     2025-03-27 [1] RSPM
 stringr               * 1.5.1     2023-11-14 [1] RSPM
 survival                3.8-3     2024-12-17 [2] CRAN (R 4.5.0)
 teal                  * 0.16.0    2025-02-23 [1] RSPM
 teal.code             * 0.6.1     2025-02-14 [1] RSPM
 teal.data             * 0.7.0     2025-01-28 [1] RSPM
 teal.logger             0.3.2     2025-02-14 [1] RSPM
 teal.modules.clinical * 0.10.0    2025-02-28 [1] RSPM
 teal.reporter           0.4.0     2025-01-24 [1] RSPM
 teal.slice            * 0.6.0     2025-02-03 [1] RSPM
 teal.transform        * 0.6.0     2025-02-12 [1] RSPM
 teal.widgets            0.4.3     2025-01-31 [1] RSPM
 tern                  * 0.9.8.433 2025-06-11 [1] https://i~
 tern.gee                0.1.5     2024-08-23 [1] RSPM
 testthat                3.2.3     2025-01-13 [1] RSPM
 TH.data                 1.1-3     2025-01-17 [1] RSPM
 tibble                  3.3.0     2025-06-08 [1] RSPM
 tidyr                   1.3.1     2024-01-24 [1] RSPM
 tidyselect              1.2.1     2024-03-11 [1] RSPM
 vctrs                   0.6.5     2023-12-01 [1] RSPM
 webshot                 0.5.5     2023-06-26 [1] CRAN (R 4.5.0)
 webshot2                0.1.2     2025-04-23 [1] RSPM
 websocket               1.4.4     2025-04-10 [1] RSPM
 withr                   3.0.2     2024-10-28 [1] RSPM
 xfun                    0.52      2025-04-02 [1] RSPM
 xtable                  1.8-4     2019-04-21 [1] RSPM
 yaml                    2.3.10    2024-07-26 [1] RSPM
 zoo                     1.8-14    2025-04-10 [1] RSPM

 [1] /usr/local/lib/R/site-library
 [2] /usr/local/lib/R/library
 [3] /github/home/R/x86_64-pc-linux-gnu-library/4.5
 * ── Packages attached to the search path.

──────────────────────────────────────────────────────────────────────────────

.lock file

Download the .lock file and use renv::restore() on it to recreate environment used to generate this website.

Download

AET06
AET07
Source Code
---
title: AET06_SMQ
subtitle: Adverse Events by Baseline Characteristic, by SMQ and Preferred Term
---

------------------------------------------------------------------------

{{< include ../../_utils/envir_hook.qmd >}}

```{r setup, echo = FALSE, warning = FALSE, message = FALSE}
library(dplyr)
library(tern)
library(stringr)

adsl <- random.cdisc.data::cadsl
adae <- random.cdisc.data::cadae

adsl_labels <- var_labels(adsl)
adae_labels <- var_labels(adae)

adsl <- adsl %>%
  mutate(
    AGE65 = case_when(
      AGE >= 65 ~ ">= 65",
      TRUE ~ "< 65"
    ),
    AGE65 = factor(
      AGE65,
      levels = c("< 65", ">= 65")
    )
  )

adae <- adae %>%
  mutate(
    AGE65 = case_when(
      AGE >= 65 ~ ">= 65",
      TRUE ~ "< 65"
    ),
    AGE65 = factor(
      AGE65,
      levels = c("< 65", ">= 65")
    )
  )

# Ensure character variables are converted to factors and empty strings and NAs are explicit missing
# levels. For details, refer to Teal and Study Data article.
adsl <- df_explicit_na(adsl)
adae <- df_explicit_na(adae)

# Simulate a random AAG dataset.
aag <- data.frame(
  NAMVAR = c("SMQ01NAM", "SMQ01NAM", "SMQ02NAM", "CQ01NAM", "CQ01NAM"),
  SRCVAR = rep("AEDECOD", 5),
  GRPTYPE = c("SMQ", "SMQ", "SMQ", "CUSTOM", "CUSTOM"),
  REFID = c(1, 1, 2, 3, 3),
  REFNAME = c(rep("C.1.1.1.3/B.2.2.3.1 AESI", 2), "non observed SMQ02NAM level", rep("D.2.1.5.3/A.1.1.1.1 AESI", 2)),
  SCOPE = c("BROAD", "BROAD", "BROAD", "", ""),
  REFTERM = c("C.1.1.1.3", "B.2.2.3.1", "Z.9.9.9.9", "D.2.1.5.3", "A.1.1.1.1"),
  stringsAsFactors = FALSE
)

# Create summary AAG dataset (used for labelling).
# Note it's important to incorporate SCOPE into the basket names for SMQs so as to
# match the flags present in ADAE.
aag_summary <- aag %>%
  select(NAMVAR, REFNAME, SCOPE) %>%
  unique() %>%
  mutate(
    REFNAME_SCOPE = ifelse(SCOPE == "", REFNAME, paste0(REFNAME, "(", SCOPE, ")"))
  ) %>%
  rename(basket = NAMVAR, basket_name = REFNAME_SCOPE) %>%
  select(basket, basket_name)

# Make a summary of the full ADAE based on AAG by using h_stack_by_baskets helper function
adae_smq_all <- h_stack_by_baskets(
  df = adae,
  aag_summary = aag_summary,
  keys = c("STUDYID", "USUBJID", "ACTARMCD", "AEDECOD", "SEX", "AGE65")
)
# Post-process adae_smq_all to keep only certain baskets of interest for variant 1
# Not need to drop baskets for variant 2 as all baskets will be considered.
baskets_to_drop_variant <- aag_summary$basket_name[!aag_summary$basket %in% c("SMQ01NAM")]

# Adjust the analysis dataset based on basket subset for variant 1.
adae_smq_1 <- adae_smq_all %>%
  filter(!adae_smq_all$SMQ %in% baskets_to_drop_variant) %>%
  mutate(
    SMQ = tern::fct_discard(SMQ, discard = baskets_to_drop_variant)
  )
```

```{r include = FALSE}
webr_code_labels <- c("setup")
```

{{< include ../../_utils/webr_no_include.qmd >}}

## Output

:::::: panel-tabset
## Adverse Events by Sex, <br/> by SMQ and Preferred Term

::: {.panel-tabset .nav-justified group="webr"}
## {{< fa regular file-lines sm fw >}} Preview

```{r variant1, test = list(result_v1 = "result")}
criteria_fun <- function(tr) !is(tr, "ContentRow") && all_zero_or_na(tr)

lyt <- basic_table(show_colcounts = TRUE) %>%
  split_cols_by("ACTARMCD") %>%
  split_cols_by("SEX") %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique"),
    .labels = c(unique = "Total number of patients with at least one adverse event")
  ) %>%
  split_rows_by(
    "SMQ",
    child_labels = "visible",
    nested = FALSE,
    split_fun = trim_levels_in_group("AEDECOD", drop_outlevs = FALSE),
    label_pos = "topleft",
    split_label = "Standardized MedDRA Query"
  ) %>%
  summarize_num_patients(
    var = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique = "Total number of patients with at least one adverse event",
      nonunique = "Total number of events"
    )
  ) %>%
  count_occurrences(vars = "AEDECOD", drop = FALSE) %>%
  append_varlabels(adae_smq_1, "AEDECOD", indent = 1L)

result <- build_table(
  lyt = lyt,
  df = adae_smq_1,
  alt_counts_df = adsl
) %>%
  sort_at_path(path = c("SMQ"), scorefun = cont_n_allcols) %>%
  sort_at_path(path = c("SMQ", "*", "AEDECOD"), scorefun = score_occurrences, na.pos = "last") %>%
  trim_rows(criteria = criteria_fun)

result
```

```{r include = FALSE}
webr_code_labels <- c("variant1")
```

{{< include ../../_utils/webr.qmd >}}
:::

## Adverse Events by Sex, by SMQ and <br/> Preferred Term (with Customized Queries)

::: {.panel-tabset .nav-justified group="webr"}
## {{< fa regular file-lines sm fw >}} Preview

```{r variant2, test = list(result_v2 = "result")}
criteria_fun <- function(tr) {
  !is(tr, "ContentRow") && all_zero_or_na(tr) && !grepl("Total number of", obj_label(tr))
}

lyt <- basic_table(show_colcounts = TRUE) %>%
  split_cols_by("ACTARMCD") %>%
  split_cols_by("SEX") %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique"),
    .labels = c(unique = "Total number of patients with at least one adverse event")
  ) %>%
  split_rows_by(
    "SMQ",
    child_labels = "visible",
    nested = FALSE,
    split_fun = trim_levels_in_group("AEDECOD", drop_outlevs = FALSE),
    label_pos = "topleft",
    split_label = "Standardized MedDRA Query"
  ) %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique = "Total number of patients with at least one adverse event",
      nonunique = "Total number of events"
    ),
    show_labels = "hidden"
  ) %>%
  count_occurrences(vars = "AEDECOD", drop = FALSE) %>%
  append_varlabels(adae_smq_all, "AEDECOD", indent = 1L)

score_ae_pts <- function(tt) {
  count_vals <- cell_values(tree_children(tt)[["USUBJID"]])[[1]]
  sum(matrix(unlist(count_vals), nrow = length(count_vals), byrow = TRUE)[, 1])
}

result <- build_table(
  lyt = lyt,
  df = adae_smq_all,
  alt_counts_df = adsl
) %>%
  sort_at_path(path = c("SMQ"), scorefun = score_ae_pts) %>%
  sort_at_path(path = c("SMQ", "*", "AEDECOD"), scorefun = score_occurrences, na.pos = "last") %>%
  trim_rows(criteria = criteria_fun)

result
```

```{r include = FALSE}
webr_code_labels <- c("variant2")
```

{{< include ../../_utils/webr.qmd >}}
:::

## Adverse Events by Other Baseline <br/> Characteristics (e.g. Age Group)

::: {.panel-tabset .nav-justified group="webr"}
## {{< fa regular file-lines sm fw >}} Preview

```{r variant3, test = list(result_v3 = "result")}
criteria_fun <- function(tr) !is(tr, "ContentRow") && all_zero_or_na(tr)

lyt <- basic_table(show_colcounts = TRUE) %>%
  split_cols_by("ACTARMCD") %>%
  split_cols_by("AGE65") %>%
  analyze_num_patients(
    vars = "USUBJID",
    .stats = c("unique"),
    .labels = c(unique = "Total number of patients with at least one adverse event")
  ) %>%
  split_rows_by(
    "SMQ",
    child_labels = "visible",
    nested = FALSE,
    split_fun = trim_levels_in_group("AEDECOD", drop_outlevs = FALSE),
    label_pos = "topleft",
    split_label = "Standardized MedDRA Query"
  ) %>%
  summarize_num_patients(
    var = "USUBJID",
    .stats = c("unique", "nonunique"),
    .labels = c(
      unique = "Total number of patients with at least one adverse event",
      nonunique = "Total number of events"
    )
  ) %>%
  count_occurrences(vars = "AEDECOD", drop = FALSE) %>%
  append_varlabels(adae_smq_1, "AEDECOD", indent = 1L)

result <- build_table(
  lyt = lyt,
  df = adae_smq_1,
  alt_counts_df = adsl
) %>%
  sort_at_path(path = c("SMQ"), scorefun = cont_n_allcols) %>%
  sort_at_path(path = c("SMQ", "*", "AEDECOD"), scorefun = score_occurrences, na.pos = "last") %>%
  trim_rows(criteria = criteria_fun)

result
```

```{r include = FALSE}
webr_code_labels <- c("variant3")
```

{{< include ../../_utils/webr.qmd >}}
:::

## Data Setup

```{r setup}
#| code-fold: show
```
::::::

{{< include ../../_utils/save_results.qmd >}}

## `teal` App

::: {.panel-tabset .nav-justified}
## {{< fa regular file-lines fa-sm fa-fw >}} Preview

```{r teal, opts.label = c("skip_if_testing", "app")}
library(teal.modules.clinical)

## Data reproducible code
data <- teal_data()
data <- within(data, {
  library(dplyr)

  ADSL <- random.cdisc.data::cadsl %>%
    mutate(
      AGE65 = case_when(
        AGE >= 65 ~ ">= 65",
        TRUE ~ "< 65"
      ),
      AGE65 = factor(
        AGE65,
        levels = c("< 65", ">= 65")
      )
    )
  ADAE <- random.cdisc.data::cadae %>%
    mutate(
      AGE65 = case_when(
        AGE >= 65 ~ ">= 65",
        TRUE ~ "< 65"
      ),
      AGE65 = factor(
        AGE65,
        levels = c("< 65", ">= 65")
      )
    )
})
datanames <- c("ADSL", "ADAE")
datanames(data) <- datanames
join_keys(data) <- default_cdisc_join_keys[datanames]

## Reusable Configuration For Modules
ADSL <- data[["ADSL"]]
ADAE <- data[["ADAE"]]

names_baskets <- grep("^(SMQ|CQ).*NAM$", names(ADAE), value = TRUE)
names_scopes <- grep("^SMQ.*SC$", names(ADAE), value = TRUE)

cs_baskets <- choices_selected(
  choices = variable_choices(ADAE, subset = names_baskets),
  selected = names_baskets
)
cs_scopes <- choices_selected(
  choices = variable_choices(ADAE, subset = names_scopes),
  selected = names_scopes,
  fixed = TRUE
)

## Setup App
app <- init(
  data = data,
  modules = modules(
    tm_t_smq(
      label = "Adverse events by `SMQ` Table",
      dataname = "ADAE",
      arm_var = choices_selected(
        choices = variable_choices(ADSL, subset = c("ARM", "SEX", "AGE65")),
        selected = c("ARM", "SEX")
      ),
      add_total = FALSE,
      baskets = cs_baskets,
      scopes = cs_scopes,
      llt = choices_selected(
        choices = variable_choices(ADAE, subset = c("AEDECOD")),
        selected = "AEDECOD"
      )
    )
  )
)

shinyApp(app$ui, app$server)
```

{{< include ../../_utils/shinylive.qmd >}}
:::

{{< include ../../repro.qmd >}}

Made with ❤️ by the NEST Team

  • Edit this page
  • Report an issue
Cookie Preferences