12  TLFs: Table, Figure, Listing

library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(gt)
library(ggplot2)
library(survival)

We reuse adsl from the previous chapter (or synthesize if missing).

if (!exists("adsl")) {
  set.seed(123)
  adsl <- tibble::tibble(
    USUBJID = sprintf("XYZ-%03d", 1:60),
    TRT01P = sample(c("Placebo","Active"), 60, replace=TRUE),
    AGE = round(rnorm(60, 60, 8)),
    SEX = sample(c("M","F"), 60, replace=TRUE)
  )
}

12.1 Table 1: Baseline Characteristics by Treatment

tbl1 <- adsl |>
  group_by(TRT01P) |>
  summarise(
    N = dplyr::n(),
    mean_age = mean(AGE, na.rm=TRUE),
    sd_age = sd(AGE, na.rm=TRUE),
    pct_female = mean(SEX == "F")*100
  )

gt(tbl1) |>
  tab_header(title = "Table 1. Baseline Characteristics by Treatment") |>
  fmt_number(columns = c(mean_age, sd_age, pct_female), decimals = 1)
Table 1. Baseline Characteristics by Treatment
TRT01P N mean_age sd_age pct_female
Active 24 59.4 8.1 50.0
Placebo 36 61.7 5.6 61.1

12.2 Figure: (Toy) Survival Curve

We simulate time-to-event data for illustration only.

set.seed(42)
n <- nrow(adsl)
adsl$time <- rexp(n, rate = ifelse(adsl$TRT01P=="Active", 0.08, 0.1))
adsl$status <- rbinom(n, 1, 0.7)
fit <- survival::survfit(survival::Surv(time, status) ~ TRT01P, data = adsl)

# Quick GGplot
ggsurv <- function(fit) {
  # rebuild data for plotting
  ss <- summary(fit)
  dd <- data.frame(
    time = ss$time,
    surv = ss$surv,
    strata = rep(names(fit$strata), fit$strata)
  )
  ggplot(dd, aes(x=time, y=surv, linetype=strata)) +
    geom_step() +
    labs(title="Kaplan–Meier (Toy Data)", x="Time", y="Survival Probability", linetype="Treatment") +
    theme_minimal()
}
#ggsurv(fit)

12.3 Listing: Subject-Level Listing

lst <- adsl |>
  arrange(USUBJID) |>
  select(USUBJID, TRT01P, AGE, SEX) |>
  head(20)

gt(lst) |>
  tab_header(title = "Listing: First 20 Subjects")
Listing: First 20 Subjects
USUBJID TRT01P AGE SEX
XYZ-001 Placebo 63 F
XYZ-002 Placebo 58 M
XYZ-003 Placebo 67 M
XYZ-004 Active 67 M
XYZ-005 Placebo 67 M
XYZ-006 Active 66 F
XYZ-007 Active 64 F
XYZ-008 Active 60 M
XYZ-009 Placebo 58 M
XYZ-010 Placebo 57 F
XYZ-011 Active 54 F
XYZ-012 Active 58 M
XYZ-013 Active 50 M
XYZ-014 Placebo 77 F
XYZ-015 Active 70 F
XYZ-016 Placebo 51 M
XYZ-017 Active 57 M
XYZ-018 Placebo 56 F
XYZ-019 Placebo 66 M
XYZ-020 Placebo 59 F

Exercises 1. Format Table 1 to N (mean ± SD) for age. 2. Add risk table to the KM plot (use an extension like survminer outside of this minimal example). 3. Create a listing that includes population flags once you derive them.