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

Building ADSL with admiral

The subject-level analysis dataset, derived end to end.

Learning objectives

  • 1Derive treatment variables and population flags (SAFFL, ITTFL, FASFL).
  • 2Compute treatment start/end dates and durations using admiral helpers.
  • 3Create demographic groupings (AGEGR1, AGEGR2, RACE, etc.).
  • 4Assemble a submission-ready ADSL with proper variable ordering and labels.

Key SAS® → R comparison

TaskSAS®R
Population flagsMultiple if/else or macros on DM+EXadmiral::derive_var_merged() + case_when
Treatment datesPROC SUMMARY + retain/mergederive_vars_merged(derivation = date_source(...))
Duration (days)TRTEDT - TRTSDT + 1derive_var_trtdurd()
Baseline characteristicsCustom formats + PROC FREQadmiral + dplyr summaries

Code examples

Population flags from DM + EX
SAS®
if first.usubjid;
  saffl = 'Y' if ex exists and not screen failure;
  ittfl = 'Y';
run;
R
adsl <- dm |>
  derive_vars_merged(
    dataset_add = ex,
    filter_add = !is.na(EXSTDTC),
    new_vars = exprs(TRTSDT = convert_dtc_to_dt(EXSTDTC))
  ) |>
  mutate(
    SAFFL = if_else(!is.na(TRTSDT) & !is.na(RFENDT), "Y", "N"),
    ITTFL = "Y"
  )

Note: admiral's date conversion functions handle ISO dates robustly and are heavily tested for regulatory use.

Treatment duration and actual arm
SAS®
trtdurd = trtedt - trtsdt + 1;
if trt01a = '' then trt01a = trt01p;
R
adsl <- adsl |>
  derive_var_trtdurd() |>
  mutate(TRT01A = if_else(is.na(TRT01A), TRT01P, TRT01A))

Note: derive_var_trtdurd() is a one-liner that respects the exact FDA/PMDA conventions for duration calculation.

Age groups + BMI derivation
SAS®
if age < 65 then agegr1 = '<65';
else if age < 75 then agegr1 = '65-74';
else agegr1 = '>=75';

bmi = weight / (height/100)**2;
R
adsl <- adsl |>
  mutate(
    AGEGR1 = case_when(
      AGE < 65 ~ "<65",
      AGE < 75 ~ "65-74",
      TRUE ~ ">=75"
    ),
    BMIBL = round(WEIGHTBL / (HEIGHTBL / 100)^2, 1)
  )

Note: All derived variables in ADSL should carry clear labels and be documented in the ADaM spec (define.xml).

Explanation

ADSL is the single most important analysis dataset you will create. Every other ADaM (ADLB, ADVS, ADAE, ADTTE, etc.) left-joins to it. Because of this, errors or missing flags in ADSL cascade into every table and figure.

admiral shines here by giving you battle-tested functions for the repetitive parts (treatment date derivation, population flags, duration) while still letting you use ordinary dplyr for study-specific logic. This combination produces code that is both concise and auditable.

In a real submission you will also add sponsor-specific variables (e.g. custom stratification factors, region groupings, biomarker flags). The discipline of keeping ADSL derivation in one well-commented script pays enormous dividends during validation and regulatory review.

Practice exercise

Using DM, EX and a small custom metadata table, build a complete ADSL containing TRT01P/TRT01A, TRTSDT/TRTEDT/TRTDURD, SAFFL/ITTFL/FASFL, AGE/AGEGR1, SEX, RACE, and at least two study-specific flags. Validate that every subject in DM appears exactly once in the final ADSL.