R TLF Pseudocode Library 217 TLF SCRIPTS

ADaM-based R pseudocode for every Table, Listing, and Figure in an early-phase clinical-pharmacology study — a complete separate set per study design, on the pharmaverse stack (dplyr/tidyr/rtables/ggplot2/emmeans/lme4).

Status — illustrative pseudocode / skeleton, NOT validated production software. These scripts show derivation and layout logic for spec writing and double-programming. Every reported number comes only from validated tools (Phoenix WinNonlin, Pinnacle 21, EDC) per SOP — nothing here originates a reported value, and no number is ever produced by an LLM.

R twin of ../sas_tlf_pseudocode/. The higher-level pharmaverse path (admiral for ADaM, tern for pre-built TLF layouts) is the production route; this library shows the underlying logic explicitly. R runs laptop-only per SOP.

▶ Run it 📦 Stack 🗂 Designs 📋 TLF inventory 🆘 Troubleshooting 💻 Browse the code
Before you start, you'll need: R 4.2+ (with Rscript on your PATH)  ·  the pharmaverse-style packages each script library()s — dplyr, tidyr, stringr, purrr, tibble, rtables, ggplot2, emmeans, lme4, broom, broom.mixed (plus haven to read .xpt/.sas7bdat ADaM)  ·  read access to your study's ADaM datasets (ADSL, ADAE, ADLB, ADVS, ADEG, ADPC, ADPP, ADPD, ADIS, ADEX), CDISC-conformant  ·  about 5 minutes to point one script at your data. No internet needed once the packages are installed. These are illustrative pseudocode skeletons: setup() defaults to placeholder paths (/data/adam) and assumes ADaM frames are loaded as tibbles — you wire in your real read step before a script produces output.

Run it

Each script source()s 00_setup_helpers.R (it expects to be run with the design folder as the working directory, e.g. parallel/), calls setup(), gets dv <- design_vars("<DESIGN>"), then builds one TLF. Install the packages once, then Rscript a program.

① install packages (once)  ·  ② point setup() / your read step at your ADaM  ·  ③ Rscript the program from inside its design folder.

① Install the packages once (CRAN; needs internet for this step only):

Rscript -e 'install.packages(c("dplyr","tidyr","stringr","purrr","tibble","rtables","ggplot2","emmeans","lme4","broom","broom.mixed","haven"))'

② Run a program — cd into the design folder first so the relative source("../00_setup_helpers.R") resolves, then:

cd parallel
Rscript t_demographics.R

Quick-start checklist — click each as you finish it:

Parse-only sanity check. Every script in the library parses under Rscript as shipped. To confirm a script is syntactically intact without touching data, parse it without executing:
Rscript -e 'invisible(parse("parallel/t_demographics.R")); cat("parses OK\n")'

Stack

dplyr/tidyr/purrr (derivation) · rtables (regulatory table layout) · ggplot2 (figures) · emmeans + lme4/nlme (mixed models / BE) · broom/broom.mixed (tidy model output).

Designs (one folder each — full set per design)

FolderDesignKey analysis differences
parallel/Parallel-groupBetween-group; treatment = TRT01A/TRT01AN
crossover/Crossover (2×2, Williams)Within-participant; by APERIOD/TRTSEQP; BE/ANOVA = lmer + emmeans, GMR + 90% CI
single_sequence/Single-/fixed-sequence (e.g. DDI)By APERIOD; fixed order, no randomized sequence; ratio + 90% CI vs reference period
sad/Single Ascending DoseParallel cohorts (dose = treatment); dose-proportionality (power model, lm)
mad/Multiple Ascending DoseParallel cohorts, repeated dosing; accumulation ratio (Rac), steady-state assessment

Design is wired via design_vars("...") in 00_setup_helpers.R (returns trtvar/trtnvar, seqvar/seqvarn, byperiod). Programs differ only where the statistics genuinely differ (PK comparison, by-period/by-sequence safety, dose escalation); safety Tables/Listings are structurally identical, re-pointed to the design's variables.

Per-script flow

source 00_setup_helpers.R→ setup()→ dv <- design_vars("<DESIGN>")→ read ADaM→ derive (dplyr/tidyr)→ layout (rtables / ggplot2)→ one TLF output

TLF inventory (each = one script per design)

Disposition / Demographics / Exposure (ADSL, ADEX, ADCM, ADMH)

t_disposition · t_demographics · t_baseline_characteristics · t_exposure · t_protocol_deviations · t_prior_con_meds · t_medical_history · l_disposition

Safety — AE (ADAE)

t_ae_overview · t_ae_by_soc_pt · t_ae_by_severity · t_ae_by_relationship · t_ae_sae_death_withdrawal · l_ae · l_sae_death

Safety — Labs (ADLB)

t_lab_summary · t_lab_shift · t_lab_marked_abnormal · f_lab_lft_scatter · f_lab_change · l_lab_abnormal

Safety — Vitals / ECG (ADVS, ADEG)

t_vitals_summary · f_vitals_change · l_vitals · t_ecg_summary · t_ecg_qtc_categorical · f_qtc_change · l_ecg

PK (ADPC, ADPP)

t_pk_conc_summary · l_pk_conc · f_pk_conc_mean (linear+semilog) · f_pk_conc_individual · t_pk_param_summary · l_pk_param · f_pk_param_boxplot · design-specific t_dose_proportionality (SAD) · t_accumulation (MAD) · t_be_anova (crossover / single-seq)

PD / Biomarkers (ADPD)

t_pd_summary · f_pd_change · f_pkpd_overlay · l_pd

Immunogenicity (ADIS)

t_ada_summary · t_ada_impact_pk · l_ada

Naming & conventions

t_ table · l_ listing · f_ figure. One script → one output. Each script source()s 00_setup_helpers.R, calls setup(), gets dv <- design_vars("<DESIGN>"), then the TLF body.

House rules baked in (the same correctness lessons as the SAS twin)

ADaM variables relied on (no re-derivation)

Treatment TRT01A/TRT01AN (parallel) · TRTA/TRTAN+APERIOD/APERIODC+TRTSEQP/TRTSEQPN (crossover/seq); populations SAFFL/PKFL/ITTFL; analysis PARAMCD/AVAL/CHG/PCHG/BASE/AVISIT/ATPTN/ANRIND/BNRIND/A1HI/A1LO/R2ULN; AE TRTEMFL/AESOC/AEDECOD/AESEVN/AREL/AESER/AESDTH/AEACN; PK ADPC AVAL=conc, ADPP PARAMCD=Cmax/Tmax/AUClast/AUCinf/CL/Vz/Rac.

Status

Complete: 217 TLF scripts across all five design folders + 00_setup_helpers.R. Built mirroring the SAS twin with all its QC fixes pre-applied, then re-QC'd (6 family reviewers → per-finding verification: 24 findings, 14 confirmed P0/P1 fixed, 0 false-positives), then a deterministic sweep. All 218 scripts parse under Rscript, design_vars() matches every folder, geometric PK stats compute on the log scale, AE/safety counts are distinct participants, single-sequence participant-level tables key on dv$seqvar, period tables denominate from a period-bearing source, exposure uses ADaM AVAL/TRTDURD/NDOSES (no *DTC arithmetic), and the library is free of eDISH/Hy's Law terminology.

Pseudocode for spec / double-programming. Reported numbers still come from validated tools per SOP; an LLM never sources a number.

Troubleshooting

SymptomWhyFix
Error in library(rtables) : there is no package called 'rtables' (or dplyr, emmeans, lme4…) The pharmaverse packages each script library()s aren't installed in this R library. Run the install.packages(...) line in Run it once. Needs internet for that step. Confirm with Rscript -e 'library(rtables)'.
cannot open file '../00_setup_helpers.R': No such file or directory You ran the script from the wrong directory; the source("../00_setup_helpers.R") path is relative to the design folder. cd into the design folder (parallel/, crossover/, etc.) first, then Rscript <script>.R — so ../ points back at the shared helper.
object 'adam' not found / object 'adsl' not found These are pseudocode skeletons: setup() uses placeholder paths and assumes the ADaM frames are already loaded as tibbles — nothing actually reads your data yet. Wire in your real read step (e.g. adsl <- haven::read_xpt(file.path(env$adam,"adsl.xpt"))) and point adam/out at your study's ADaM and output paths before expecting output.
Table runs but every % / count looks wrong or empty Population flag or treatment variable doesn't match your ADaM, so bign() denominators or SAFFL=="Y" filters drop everything. Confirm your ADaM carries the variables the library relies on (SAFFL/PKFL/ITTFL, TRT01A/TRTA, USUBJID); for crossover/single-seq check design_vars() matches your APERIOD/TRTSEQP naming.
Wrong design_vars() branch (e.g. crossover script keyed on TRT01A) The design_vars("<DESIGN>") argument in the script header doesn't match the folder you copied it from. Use the script from the matching design folder; the argument must be PARALLEL/CROSSOVER/SINGLESEQ/SAD/MAD for that folder.
Geometric mean / CV% looks identical to the arithmetic mean A modified copy is computing exp(mean(raw)) instead of working on the log scale. Keep pkstats(): geomean = exp(mean(log(x))), geocv = 100*sqrt(exp(var(log(x)))-1); filter AVAL > 0 first. Tmax stays Median (Min, Max).
boundary (singular) fit warning from t_be_anova (crossover / single-seq) lmer can't estimate a random effect (too few sequences/periods or a near-degenerate design) — expected on small or illustrative data. It's a warning, not a failure. On real BE data confirm the model spec against the SAP; the reported GMR + 90% CI still comes from the validated tool, not this skeleton.
Numbers from a script land in a regulatory deliverable This library is illustrative pseudocode for spec writing / double-programming — it is not validated and never the reported source. Reported and regulated numbers come only from validated tools (Phoenix WinNonlin, Pinnacle 21, EDC) per SOP. An LLM never sources a number.

Browse the code

Shared setup

00_setup_helpers.Rsetup() · design_vars() · bign() helpers

parallel/ — Parallel-group 42 SCRIPTS

t_disposition.R t_demographics.R t_baseline_characteristics.R t_exposure.R t_protocol_deviations.R t_prior_con_meds.R t_medical_history.R l_disposition.R t_ae_overview.R t_ae_by_soc_pt.R t_ae_by_severity.R t_ae_by_relationship.R t_ae_sae_death_withdrawal.R l_ae.R l_sae_death.R t_lab_summary.R t_lab_shift.R t_lab_marked_abnormal.R f_lab_lft_scatter.R f_lab_change.R l_lab_abnormal.R t_vitals_summary.R f_vitals_change.R l_vitals.R t_ecg_summary.R t_ecg_qtc_categorical.R f_qtc_change.R l_ecg.R t_pk_conc_summary.R l_pk_conc.R f_pk_conc_mean.R f_pk_conc_individual.R t_pk_param_summary.R l_pk_param.R f_pk_param_boxplot.R t_pd_summary.R f_pd_change.R f_pkpd_overlay.R l_pd.R t_ada_summary.R t_ada_impact_pk.R l_ada.R

crossover/ — Crossover (2×2, Williams) 44 SCRIPTS

t_disposition.R t_demographics.R t_baseline_characteristics.R t_exposure.R t_protocol_deviations.R t_prior_con_meds.R t_medical_history.R l_disposition.R t_ae_overview.R t_ae_by_soc_pt.R t_ae_by_severity.R t_ae_by_relationship.R t_ae_sae_death_withdrawal.R l_ae.R l_sae_death.R t_lab_summary.R t_lab_shift.R t_lab_marked_abnormal.R f_lab_lft_scatter.R f_lab_change.R l_lab_abnormal.R t_vitals_summary.R f_vitals_change.R l_vitals.R t_ecg_summary.R t_ecg_qtc_categorical.R f_qtc_change.R l_ecg.R t_pk_conc_summary.R l_pk_conc.R f_pk_conc_mean.R f_pk_conc_individual.R t_pk_param_summary.R t_pk_param_by_period.R l_pk_param.R f_pk_param_boxplot.R t_be_anova.Rlmer + emmeans, GMR + 90% CI t_pd_summary.R f_pd_change.R f_pkpd_overlay.R l_pd.R t_ada_summary.R t_ada_impact_pk.R l_ada.R

single_sequence/ — Single-/fixed-sequence (DDI) 44 SCRIPTS

t_disposition.R t_demographics.R t_baseline_characteristics.R t_exposure.R t_protocol_deviations.R t_prior_con_meds.R t_medical_history.R l_disposition.R t_ae_overview.R t_ae_by_soc_pt.R t_ae_by_severity.R t_ae_by_relationship.R t_ae_sae_death_withdrawal.R l_ae.R l_sae_death.R t_lab_summary.R t_lab_shift.R t_lab_marked_abnormal.R f_lab_lft_scatter.R f_lab_change.R l_lab_abnormal.R t_vitals_summary.R f_vitals_change.R l_vitals.R t_ecg_summary.R t_ecg_qtc_categorical.R f_qtc_change.R l_ecg.R t_pk_conc_summary.R l_pk_conc.R f_pk_conc_mean.R f_pk_conc_individual.R t_pk_param_summary.R t_pk_param_by_period.R l_pk_param.R f_pk_param_boxplot.R t_be_anova.Rratio + 90% CI vs reference period t_pd_summary.R f_pd_change.R f_pkpd_overlay.R l_pd.R t_ada_summary.R t_ada_impact_pk.R l_ada.R

sad/ — Single Ascending Dose 43 SCRIPTS

t_disposition.R t_demographics.R t_baseline_characteristics.R t_exposure.R t_protocol_deviations.R t_prior_con_meds.R t_medical_history.R l_disposition.R t_ae_overview.R t_ae_by_soc_pt.R t_ae_by_severity.R t_ae_by_relationship.R t_ae_sae_death_withdrawal.R l_ae.R l_sae_death.R t_lab_summary.R t_lab_shift.R t_lab_marked_abnormal.R f_lab_lft_scatter.R f_lab_change.R l_lab_abnormal.R t_vitals_summary.R f_vitals_change.R l_vitals.R t_ecg_summary.R t_ecg_qtc_categorical.R f_qtc_change.R l_ecg.R t_pk_conc_summary.R l_pk_conc.R f_pk_conc_mean.R f_pk_conc_individual.R t_pk_param_summary.R l_pk_param.R f_pk_param_boxplot.R t_dose_proportionality.Rpower model, lm t_pd_summary.R f_pd_change.R f_pkpd_overlay.R l_pd.R t_ada_summary.R t_ada_impact_pk.R l_ada.R

mad/ — Multiple Ascending Dose 44 SCRIPTS

t_disposition.R t_demographics.R t_baseline_characteristics.R t_exposure.R t_protocol_deviations.R t_prior_con_meds.R t_medical_history.R l_disposition.R t_ae_overview.R t_ae_by_soc_pt.R t_ae_by_severity.R t_ae_by_relationship.R t_ae_sae_death_withdrawal.R l_ae.R l_sae_death.R t_lab_summary.R t_lab_shift.R t_lab_marked_abnormal.R f_lab_lft_scatter.R f_lab_change.R l_lab_abnormal.R t_vitals_summary.R f_vitals_change.R l_vitals.R t_ecg_summary.R t_ecg_qtc_categorical.R f_qtc_change.R l_ecg.R t_pk_conc_summary.R l_pk_conc.R f_pk_conc_mean.R f_pk_conc_individual.R t_pk_param_summary.R l_pk_param.R f_pk_param_boxplot.R t_accumulation.Raccumulation ratio (Rac), steady state t_dose_proportionality_ss.Rsteady-state dose proportionality t_pd_summary.R f_pd_change.R f_pkpd_overlay.R l_pd.R t_ada_summary.R t_ada_impact_pk.R l_ada.R