--SEQ is a within-subject row number after a defined order. In R: arrange(), then group_by(USUBJID), then row_number(). Without arrange(), the numbers follow the current row order, not the IG order.
Three traps: row_number() follows the current row order, so skip arrange() and --SEQ is whatever the last PROC left behind. Equal dates need tie-breakers in arrange(). Integer SEQ is not how SAS XPT stores it; use as.double().
Worked examples below.
The example table
Tumor results in input order, which is not the TUSEQ order.
library(dplyr)
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
STD-001-0001 has investigator lesions T01 and T02, and one independent-assessor row. VISITNUM 3 is the first input row. IG order: evaluator, lesion, visit, study day.
Wrong: row_number() without arrange()
tu |>group_by(USUBJID) |>mutate(TUSEQ =row_number()) |>ungroup()
INDEPENDENT ASSESSOR sorts before INVESTIGATOR and is TUSEQ 1. Then investigator T01 visit 1, T01 visit 3, T02 visit 1. TULNKID precedes VISITNUM, so T01 visit 3 precedes T02 visit 1. STD-001-0002 restarts at 1.
Importantrow_number() numbers the current order
It does not infer a clinical order. group_by(USUBJID) then row_number() uses the frame's current order. Sort first. Include every IG key in arrange(), including tie-breakers.
Tie-breakers
Two rows can share a study day. arrange(USUBJID, TUDY) alone leaves their relative order undefined. Add keys that make a row unique, in IG order: TUEVAL, TULNKID, VISITNUM, TUDY. If a tie remains, add a source sequence or a datetime.
as.double() for XPT
SAS numeric is double. row_number() returns integer. haven / xportr write that as integer unless it is cast. SDTM --SEQ in a SAS XPT is double: