derive_sv.RdStacks the visit-bearing collected domains and reduces them to one record per
subject per visit, with SVSTDTC/SVENDTC set to the earliest and latest
date observed for that subject at that visit across all the domains
supplied. A visit that spans two days in the lab but one in vitals therefore
gets the full span, which is the point of building SV from the collected data
rather than from any single domain.
derive_sv(domains, dm)An sdtmgap_domain tibble with STUDYID, DOMAIN, USUBJID,
VISITNUM, VISIT, SVSTDTC, SVENDTC.
Partial dates are ordered by their earliest possible instant for the start and their latest for the end, and the winning value is written through as collected — SV never invents a precision the CRF did not have.
Records with a missing VISITNUM (unscheduled or unvisited data) or with no
usable date are excluded, as are subjects absent from dm. SV has no
--SEQ: USUBJID + VISITNUM is the key.
Limit. The window is the range of collected dates only. Planned visit
windows from Trial Visits (TV) are not read, so derive_sv() will not flag a
visit that fell outside its protocol window, nor emit a row for a visit at
which nothing was collected.
SAS idiom replaced. The PROC SQL union of every raw domain followed by
PROC MEANS/PROC SUMMARY with MIN(--DTC)/MAX(--DTC) BY USUBJID VISITNUM — with the character-date min/max trap that comes with it.
sv <- derive_sv(
domains = list(vs = sdtmgap_example("edge_vs"), lb = sdtmgap_example("edge_lb")),
dm = sdtmgap_example("edge_dm")
)
sv[sv$USUBJID == "SDTMGAP01-001", ]
#>
#> ── SDTM SV (6 records) ─────────────────────────────────────────────────────────
#> ℹ 31 visit record(s) for 7 subject(s), from 38 input record(s) across 2 domain(s).
#> ℹ 1 input record(s) had no VISITNUM, no usable date, or no matching subject.
#> # A tibble: 6 × 7
#> STUDYID DOMAIN USUBJID VISITNUM VISIT SVSTDTC SVENDTC
#> <chr> <chr> <chr> <dbl> <chr> <chr> <chr>
#> 1 SDTMGAP01 SV SDTMGAP01-001 1 SCREENING 2026-01-04 2026-01-05
#> 2 SDTMGAP01 SV SDTMGAP01-001 2 DAY 1 2026-01-19 2026-01-19
#> 3 SDTMGAP01 SV SDTMGAP01-001 3 WEEK 2 2026-02-02 2026-02-03
#> 4 SDTMGAP01 SV SDTMGAP01-001 4 WEEK 4 2026-02-15 2026-02-15
#> 5 SDTMGAP01 SV SDTMGAP01-001 5 FOLLOW-UP 2026-03-01 2026-03-01
#> 6 SDTMGAP01 SV SDTMGAP01-001 99 UNSCHEDULED 2025-12-20 2025-12-20