30-Day Course
Clinical SAS → R
Day 28 of 30·ADaM, TLFs & Advanced Topics

Tables, Listings & Figures with rtables, tern & gtsummary

Producing regulated TLF outputs from analysis datasets.

Learning objectives

  • 1Build demographic and AE summary tables using industry-standard frameworks.
  • 2Compare gtsummary (rapid) vs rtables/tern (submission-grade control).
  • 3Create publication-quality figures with ggplot2 + themes.
  • 4Export tables and figures to RTF/PDF suitable for eCTD appendices.

Key SAS® → R comparison

TaskSAS®R
Demographics tablePROC REPORT + ODSgtsummary or tern::demog_tbl
AE incidencePROC FREQ + custom macrotern::count_occurrences
Shift / lab tablesComplex PROC REPORTtern + rtables layout
FiguresPROC SGPLOT / GTLggplot2 + ggsurvfit / ggpubr
Regulatory exportODS RTF + stylesrtables::export_as_rtf + r2rtf

Code examples

Demographics with gtsummary (fast & beautiful)
SAS®
proc report data=adsl;
  columns arm age sex;
  define arm / group;
  ...
run;
R
library(gtsummary)
adsl |>
  select(ARM, AGE, SEX, RACE, BMIBL) |>
  tbl_summary(
    by = ARM,
    statistic = list(all_continuous() ~ "{mean} ({sd})"),
    digits = all_continuous() ~ 1
  ) |>
  add_p() |>
  add_overall() |>
  modify_header(label ~ "**Characteristic**")

Note: gtsummary is perfect for internal reports and many standard tables. For formal CSR TLFs many sponsors still prefer rtables for pixel-perfect control.

AE summary table with tern (submission style)
SAS®
/* Custom %ae_table macro with 20+ parameters */
%ae_table(data=adae, by=trt01a, ...);
R
adae |>
  count_occurrences(
    variables = exprs(AEBODSYS, AEDECOD),
    by = ARM
  ) |>
  add_overall(across = "col") |>
  build_table()

Note: tern's count_occurrences + risk tables give you exactly the structure regulators expect with far less custom code than traditional SAS macros.

Mean change plot (ggplot2)
SAS®
proc sgplot data=adlb;
  series x=avisitn y=meanchg / group=arm;
  ...
run;
R
ggplot(adlb_mean, aes(x = AVISITN, y = mean_chg, color = ARM)) +
  geom_line(linewidth = 1) +
  geom_point(size = 2.5) +
  labs(title = "Mean Change in Systolic BP",
       x = "Visit", y = "Change from Baseline (mmHg)") +
  theme_classic() +
  scale_color_brewer(palette = "Set1")

Note: ggplot2 figures can be saved at 300 dpi for submission or converted to editable EMF via the officer or rvg packages.

Explanation

The TLF ecosystem in R is now mature enough for real regulatory submissions. gtsummary excels at speed and attractive defaults for exploratory work and many standard outputs. For the tightly controlled, highly formatted tables required in a CSR or ISS/ISE, rtables + tern give you the same level of layout precision that experienced SAS programmers achieve with PROC REPORT and ODS.

Figures are almost universally moving to ggplot2 because the grammar is vastly more powerful and the theming system produces consistent branding across a submission with almost no per-figure tweaking.

Export tooling (r2rtf, rtables exporters, officer for Word) closes the loop so your final deliverables can be dropped straight into the eCTD without manual reformatting.

Practice exercise

Produce (1) a demographics table with gtsummary, (2) an AE body-system / preferred-term incidence table with tern, and (3) a line plot of mean lab parameter over time. Export the AE table to RTF and the figure to high-resolution PNG.