SDTM Case Study: Raw to Submission-Ready Domain
An end-to-end build tying together the SDTM module.
Learning objectives
- 1Assemble a complete domain from raw data through to a valid .xpt file.
- 2Combine dplyr, admiral, and xportr in a single, reviewable script.
- 3Apply controlled terminology, proper sequencing, and metadata.
- 4Perform automated conformance-style checks and double-programming validation before export.
Key SAS® → R comparison
| Task | SAS® | R |
|---|---|---|
| Domain pipeline | Large macro library per domain | One clear dplyr + admiral + xportr script |
| Metadata handling | PROC CONTENTS + manual labels | xportr_label() + xportr_type() from spec |
| Conformance | OpenCDISC / Pinnacle 21 | sdtmchecks + custom rules + xportr |
| Dataset compare | PROC COMPARE | diffdf (structured report) |
Code examples
/* %build_dm; %qc_dm; %export_dm; */dm <- raw_dm |>
transmute(
STUDYID = "ABC",
DOMAIN = "DM",
USUBJID = str_c("ABC", siteid, subjid, sep = "-"),
...
) |>
derive_vars_dt(new_vars_prefix = "RF", ...) |>
arrange(USUBJID, SITEID, SUBJID) |>
mutate(DMSEQ = row_number())
dm |>
xportr_type(spec, domain = "DM") |>
xportr_label(spec, domain = "DM") |>
xportr_write("dm.xpt", label = "Demographics")Note: xportr enforces the exact data types and labels from your specification document, dramatically reducing define.xml mismatches.
sex = put(rawsex, $sex.);
race = put(rawrace, $race.);dm <- dm |>
mutate(
SEX = recode(SEX, "1"="M", "2"="F", .default = "U"),
RACE = case_when(
RACE == "WHITE" ~ "WHITE",
RACE == "BLACK" ~ "BLACK OR AFRICAN AMERICAN",
...
)
)Note: Store your controlled terminology in a small metadata table or YAML and join/recode — far easier to maintain and audit than dozens of formats.
proc compare base=dm_prod compare=dm_qc;
id usubjid;
run;# In production script
dm |> xportr_write(...)
# In independent QC script (different programmer)
qc_dm <- ... # same logic, independently coded
diffdf(dm, qc_dm, keys = "USUBJID") # zero differences = passNote: diffdf gives you a clean, machine-readable discrepancy list that can be automatically checked in CI.
Explanation
The real power of the modern open-source stack appears when you assemble the full pipeline in one script: raw data → mapping + CT → date derivations (admiral) → sequencing → metadata application (xportr) → transport file. The result is dramatically shorter and more transparent than the equivalent SAS macro suite.
Validation remains essential. You still double-program key domains, but the comparison tool (diffdf) is more structured than PROC COMPARE output, and you can add automated sdtmchecks-style rules directly in the same R session.
The combination of readable code + automated metadata + repeatable environment (renv) + structured QC gives you a credible, regulator-friendly SDTM process without the traditional SAS macro maintenance burden.