# Setup: run this first, before any exercise ----------------------------
library(cards)
library(gtsummary)
library(tidyverse)
adsl <- pharmaverseadam::adsl |>
filter(SAFFL == "Y") |>
mutate(ARM2 = word(ARM))
adae <- pharmaverseadam::adae |>
filter(SAFFL == "Y") |>
filter(AESOC %in% unique(AESOC)[1:3]) |>
group_by(AESOC) |>
filter(AEDECOD %in% unique(AEDECOD)[1:3]) |>
ungroup()Exercises
Setup
Run this once, before any exercise.
Exercise 1: ARDs with {cards}
# Exercise 1: ARDs with {cards}
#
# Run `00-setup.R` first.
# A. Calculate the number and percentage of *unique* subjects with at least
# one AE:
# - By each SOC (AESOC)
# - By each Preferred term (AEDECOD) within SOC (AESOC)
# By every combination of treatment group (ARM)
ard_ae <-
ard_stack_hierarchical(
data = ,
variables = ,
by = ,
id = ,
denominator =
)
ard_ae
# B. Using the ARD you just built, report the rate of subjects who
# experienced fatigue in each treatment group.
# HINT: `filter()` the ARD down to the rows you need, and
# `apply_fmt_fun()` formats the statistics for reportingExercise 2: a theme for your organization
# Exercise 2: a theme for your organization
#
# Run `00-setup.R` first.
# A. Finish the theme below. It should:
# - be named "R/Pharma 2026"
# - print every table with {flextable}
# - round percentages to one decimal place
# - round p-values to three decimal places
# HINT: the element names are catalogued in the themes vignette,
# https://www.danieldsjoberg.com/gtsummary/articles/themes.html
theme_gtsummary_rpharma <- function(set_theme = TRUE) {
lst_theme <-
utils::modifyList(
# start from the shipped compact theme, then layer ours on top
theme_gtsummary_compact(set_theme = FALSE),
list(
"pkgwide-str:theme_name" = ,
"pkgwide-str:print_engine" = ,
"tbl_summary-fn:percent_fun" = ,
"pkgwide-fn:pvalue_fun" =
)
)
if (set_theme == TRUE) set_gtsummary_theme(lst_theme)
invisible(lst_theme)
}
# B. Check the theme, then set it and build a table to see it applied.
check_gtsummary_theme()
# C. Clear the theme again before moving on.
reset_gtsummary_theme()Exercise 3: tbl_pharma_summary()
# Exercise 3: tbl_pharma_summary()
#
# Run `00-setup.R` first.
# A. Finish the wrapper below. Continuous variables should default to
# multi-line summaries showing mean (sd), median, Q1/Q3 and min/max;
# categorical variables to n (%).
#
# Note `type` stays NULL, exactly as in tbl_summary() — the continuous2
# default comes from the theme element instead, so an explicit `type =`
# from the caller still wins.
tbl_pharma_summary <- function(
data,
by = NULL,
type = NULL,
# TODO: replace NULL with the default statistics described above
statistic = NULL,
...) {
with_gtsummary_theme(
# TODO: the element that makes continuous variables multi-line
list("tbl_summary-str:default_con_type" = ),
tbl_summary(
data = data,
by = {{ by }},
type = type,
statistic = statistic,
...
)
)
}
# B. Use it to summarize AGE and ETHNIC in adsl by ARM2.Solutions
Exercise 1 solution
# Exercise 1 solution: ARDs with {cards}
#
# Run `00-setup.R` first.
# A. Calculate the number and percentage of *unique* subjects with at least
# one AE:
# - By each SOC (AESOC)
# - By each Preferred term (AEDECOD) within SOC (AESOC)
# By every combination of treatment group (ARM)
ard_ae <-
ard_stack_hierarchical(
data = adae,
variables = c(AESOC, AEDECOD),
by = ARM,
id = USUBJID,
denominator = adsl
)
ard_ae
# B. Report the rate of subjects who experienced fatigue in each treatment
# group. We used `filter()` to keep the fatigue rows, and `apply_fmt_fun()`
# to format the statistics.
ard_ae |>
filter(variable == "AEDECOD", variable_level == "FATIGUE") |>
apply_fmt_fun()Exercise 2 solution
# Exercise 2 solution: a theme for your organization
#
# Run `00-setup.R` first.
# A. Finish the theme below.
theme_gtsummary_rpharma <- function(set_theme = TRUE) {
lst_theme <-
utils::modifyList(
# start from the shipped compact theme, then layer ours on top
theme_gtsummary_compact(set_theme = FALSE),
list(
"pkgwide-str:theme_name" = "R/Pharma 2026",
"pkgwide-str:print_engine" = "flextable",
"tbl_summary-fn:percent_fun" = scales::label_number(scale = 100, accuracy = 0.1),
"pkgwide-fn:pvalue_fun" = scales::label_pvalue()
)
)
if (set_theme == TRUE) set_gtsummary_theme(lst_theme)
invisible(lst_theme)
}
# B. Check the theme, then set it and build a table to see it applied.
check_gtsummary_theme(theme_gtsummary_rpharma(set_theme = FALSE))
theme_gtsummary_rpharma()
adsl |>
tbl_summary(by = ARM2, include = c(AGE, ETHNIC)) |>
add_p()
# C. Clear the theme again before moving on.
reset_gtsummary_theme()Exercise 3 solution
# Exercise 3 solution: tbl_pharma_summary()
#
# Run `00-setup.R` first.
# A. Finish the wrapper below.
tbl_pharma_summary <- function(
data,
by = NULL,
type = NULL,
statistic = list(
all_continuous() ~ c("{mean} ({sd})", "{median}",
"{p25}, {p75}", "{min}, {max}"),
all_categorical() ~ "{n} ({p}%)"
),
...) {
with_gtsummary_theme(
list("tbl_summary-str:default_con_type" = "continuous2"),
tbl_summary(
data = data,
by = {{ by }},
type = type,
statistic = statistic,
...
)
)
}
# B. Use it to summarize AGE and ETHNIC in adsl by ARM2.
adsl |>
tbl_pharma_summary(
by = ARM2,
include = c(AGE, ETHNIC)
)