R (tidyverse)+
#==============================================================================
# programs/01_make_dm_xpt.R β build and write DM.xpt
#==============================================================================
library(tidyverse)
library(haven)
out_dir <- file.path("output", "xpt")
dir.create(out_dir, recursive = TRUE, showWarnings = FALSE)
dm <- tribble(
~studyid, ~domain, ~usubjid, ~subjid, ~sex, ~age,
"CSG001", "DM", "CSG001-0001", "0001", "M", 35,
"CSG001", "DM", "CSG001-0002", "0002", "F", 29
) %>%
mutate(across(where(is.character), ~ str_sub(.x, 1, 200)))
write_xpt(dm, file.path(out_dir, "DM.xpt"))
#==============================================================================
# programs/02_make_ae_xpt.R β build and write AE.xpt
#==============================================================================
library(tidyverse)
library(haven)
out_dir <- file.path("output", "xpt")
dir.create(out_dir, recursive = TRUE, showWarnings = FALSE)
ae <- tribble(
~studyid, ~domain, ~usubjid, ~aeseq, ~aeterm, ~aeser,
"CSG001", "AE", "CSG001-0001", 1, "Headache", "N",
"CSG001", "AE", "CSG001-0002", 1, "Nausea", "N"
) %>%
mutate(across(where(is.character), ~ str_sub(.x, 1, 200)))
write_xpt(ae, file.path(out_dir, "AE.xpt"))
#==============================================================================
# programs/03_make_lb_xpt.R β build and write LB.xpt
#==============================================================================
library(tidyverse)
library(haven)
out_dir <- file.path("output", "xpt")
dir.create(out_dir, recursive = TRUE, showWarnings = FALSE)
lb <- tribble(
~studyid, ~domain, ~usubjid, ~lbtestcd, ~lbtest, ~lbstresn, ~lbstresu,
"CSG001", "LB", "CSG001-0001", "HGB", "Hemoglobin", 13.2, "g/dL"
) %>%
mutate(across(where(is.character), ~ str_sub(.x, 1, 200)))
write_xpt(lb, file.path(out_dir, "LB.xpt"))
#==============================================================================
# run_all.R β batch driver (sits at the project root)
#==============================================================================
library(tidyverse)
setwd("C:/path/to/r_batch_xpt_example")
scripts <- c(
file.path("programs", "01_make_dm_xpt.R"),
file.path("programs", "02_make_ae_xpt.R"),
file.path("programs", "03_make_lb_xpt.R")
)
log_dir <- file.path("output", "logs")
dir.create(log_dir, recursive = TRUE, showWarnings = FALSE)
walk(scripts, function(s) {
log_file <- file.path(
log_dir,
paste0(tools::file_path_sans_ext(basename(s)), ".log")
)
system2(
command = "Rscript",
args = s,
stdout = log_file,
stderr = log_file
)
})
cat("Batch run completed\n")- To run several R programs from a single driver file in tidyverse style, we list the programs in order and iterate with
walk(). scriptsis an ordered character vector — the same idea as listing SAS programs in a batch submit file. The execution order is the vector order.- Inside each individual program, we include
library(tidyverse)andlibrary(haven). That is critical for batch mode:Rscriptdoes not auto-load any packages, so each program has to be self-sufficient. - We use
across(where(is.character), ~ str_sub(.x, 1, 200))to cap every character column at 200 bytes. This matches XPT v5 limits and keeps the write step safe for SDTM-style data. - In the driver,
walk()is thepurrrequivalent of a for loop — we iterate overscriptsand execute a block for each one. Usingwalk()instead ofmap()signals “we care about the side effect, not a return value”. - Inside the walk body, we build a log file name by stripping the directory and extension from the script name, and redirect
stdoutandstderrinto it withsystem2(). system2(command = "Rscript", args = s, ...)launches a brand-new R session for each program. That gives us clean isolation — a stray object or loaded package in one program cannot leak into the next.- After the loop finishes, we print “Batch run completed” so the console shows a clear end-of-batch marker.
- After running
Rscript run_all.R, we check thatoutput/xpt/contains DM.xpt, AE.xpt and LB.xpt, andoutput/logs/contains one .log file per program. - If any program fails, we open its specific log file, fix the issue inside that program, and rerun
Rscript run_all.R— the driver is safe to re-run end-to-end.