Collapses element-level source records (exposure, disposition, or a stack of both) into one SE record per subject per element, resolves the element label from Trial Elements (TE), orders the elements chronologically, and numbers them with SESEQ.

derive_se(dm, exposure, te, ta = NULL)

Arguments

dm

Demographics (DM). Supplies the subject universe, STUDYID, and the arm (ACTARMCD if present, else ARMCD) used to join ta. Subjects absent from dm are dropped; subjects with no source records get no SE records.

exposure

Element source records: one or more rows per subject and element, with USUBJID, a --STDTC (and optionally --ENDTC) date variable, and ETCD. Build it from EX, DS, or both. When ETCD is absent it is derived from ta — see the ETCD section below.

te

Trial Elements (TE), with ETCD, ELEMENT and optionally TEDUR.

ta

Optional Trial Arms (TA), with ARMCD, ETCD and optionally TAETORD and EPOCH. Supplying it adds TAETORD and EPOCH to the result, and EPOCH is what derive_epoch() needs. EPOCH is joined on arm and element; where the subject's arm is not in ta at all — screen failures usually are not — it falls back to the element alone, but only when every arm containing that element agrees on its EPOCH. Elements ta does not place (CDISCPILOT01 defines no follow-up element in TA) keep EPOCH missing rather than being assigned a plausible one.

Value

An sdtmgap_domain tibble (a plain data frame downstream) with STUDYID, DOMAIN, USUBJID, SESEQ, ETCD, ELEMENT, SESTDTC, SEENDTC, plus TAETORD and EPOCH when ta is supplied.

Details

SDTMIG treats subject elements as contiguous: the end of one element is the start of the next. SEENDTC is therefore filled, in this order:

  1. the latest observed end date among that subject's source records for the element;

  2. failing that, the SESTDTC of the subject's next element;

  3. failing that, SESTDTC plus TEDUR from te (inclusive of the start day, so P14D from 2026-02-18 ends 2026-03-03);

  4. failing that, NA — an open-ended final element, which derive_epoch() treats as running to the end of time rather than dropping the records after it.

Partial dates are ordered by their earliest possible instant, and are written through to SESTDTC/SEENDTC exactly as collected (a "2026-02" end stays "2026-02"); see derive_epoch() for how they are then matched.

ETCD

If exposure has no ETCD column it is derived from ta by derive_etcd(), which places each dosing record on the arm's treatment elements in order. That requires ta; without it, or where the design does not determine the assignment, derive_se() aborts naming what is missing rather than guessing. Read derive_etcd() before relying on it — the rule is ordering-based, and a design that distinguishes its treatment elements by dose or branch instead needs ETCD attached by hand.

How TEDUR is resolved

Step 3 above adds an ISO 8601 duration to SESTDTC. Components are applied largest first — years, then months, then weeks and days — and the start date counts as day 1, so one day is subtracted at the end. Years and months are calendar arithmetic, not a fixed number of days: P1M from 2026-01-15 ends 2026-02-14, and P1M from 2026-01-31 ends 2026-02-27, because the month step is clamped to the last day of the target month (2026-02-28) rather than overflowing into March. Weeks and days are fixed at 7 and 1 day. Composite durations work: P1M15D from 2026-01-20 is one month to 2026-02-20, then 15 days to 2026-03-07, inclusive end 2026-03-06.

A duration with a time component (PT12H, P1DT12H), the alternate P0003-06-04 form, an all-zero duration, or a partial SESTDTC yields NAsdtmgap leaves the element open-ended rather than inventing an end date.

Limit. An element start that the source records do give is never moved. A study that snaps each element back to start on the day the previous one ended will therefore differ from sdtmgap by the gap between the two — one day, wherever dosing resumes the day after the previous element's last dose. Records landing in that gap are the main source of element-level disagreement; see the README for the rate measured against CDISCPILOT01.

SAS idiom replaced. The hand-written PROC SORT + BY USUBJID ETCD + RETAIN/LAG DATA step that every study writes to stitch element start and stop dates together and carry the next element's start backwards.

Examples

se <- derive_se(
  dm       = sdtmgap_example("edge_dm"),
  exposure = sdtmgap_example("edge_elements"),
  te       = sdtmgap_example("edge_te"),
  ta       = sdtmgap_example("edge_ta")
)
se[se$USUBJID == "SDTMGAP01-001", c("SESEQ", "ETCD", "SESTDTC", "SEENDTC", "EPOCH")]
#> 
#> ── SDTM SE (3 records) ─────────────────────────────────────────────────────────
#>  6 subject(s) with elements, 17 element record(s).
#>  1 subject(s) in DM contributed no elements.
#>  1 source record(s) had a partial start date.
#> # A tibble: 3 × 5
#>   SESEQ ETCD  SESTDTC    SEENDTC    EPOCH    
#>   <int> <chr> <chr>      <chr>      <chr>    
#> 1     1 SCRN  2026-01-04 2026-01-19 SCREENING
#> 2     2 TRTA  2026-01-19 2026-02-15 TREATMENT
#> 3     3 FUP   2026-02-16 2026-03-01 FOLLOW-UP

# ETCD derived from Trial Arms rather than supplied: the CDISCPILOT01
# exposure records carry no element code.
derive_se(
  dm       = sdtmgap_example("lzzt_dm"),
  exposure = sdtmgap_example("lzzt_ex"),
  te       = sdtmgap_example("lzzt_te"),
  ta       = sdtmgap_example("lzzt_ta")
)
#> 
#> ── SDTM SE (354 records) ───────────────────────────────────────────────────────
#>  254 subject(s) with elements, 354 element record(s).
#>  52 subject(s) in DM contributed no elements.
#> # A tibble: 354 × 10
#>    STUDYID      DOMAIN USUBJID SESEQ ETCD  ELEMENT SESTDTC SEENDTC TAETORD EPOCH
#>  * <chr>        <chr>  <chr>   <int> <chr> <chr>   <chr>   <chr>     <dbl> <chr>
#>  1 CDISCPILOT01 SE     01-701…     1 PBO   Placebo 2014-0… 2014-0…       2 Trea…
#>  2 CDISCPILOT01 SE     01-701…     1 PBO   Placebo 2012-0… 2012-0…       2 Trea…
#>  3 CDISCPILOT01 SE     01-701…     1 HIS   High_S… 2013-0… 2013-0…       2 Trea…
#>  4 CDISCPILOT01 SE     01-701…     2 HIM   High_M… 2013-0… 2014-0…       3 Trea…
#>  5 CDISCPILOT01 SE     01-701…     3 HIE   High_E… 2014-0… 2014-0…       4 Trea…
#>  6 CDISCPILOT01 SE     01-701…     1 LO    Low     2014-0… 2014-0…       2 Trea…
#>  7 CDISCPILOT01 SE     01-701…     1 HIS   High_S… 2014-0… 2014-0…       2 Trea…
#>  8 CDISCPILOT01 SE     01-701…     2 HIM   High_M… 2014-0… 2014-1…       3 Trea…
#>  9 CDISCPILOT01 SE     01-701…     3 HIE   High_E… 2014-1… 2014-1…       4 Trea…
#> 10 CDISCPILOT01 SE     01-701…     1 PBO   Placebo 2013-0… 2013-0…       2 Trea…
#> # ℹ 344 more rows