SAS SUBSTR takes a length. R substr() takes a stop position. The three-argument shape is the same; the last argument is not. A SAS length passed as stop can return "" with no error.
Quick reference
# SAS: SUBSTR(string, start, length)# R: substr(string, start, stop) stop is an end position, not a lengthsubstr(usubjid, 5, 3) # "" (stop < start: empty range)substr(usubjid, 5, 7) # positions 5 through 7substr(usubjid, 5, 5+3-1) # SAS length 3, written as start+len-1substring(usubjid, 5, 7) # same idea; arguments recycle; can assign# stringr::str_sub() also takes end positions, not a length
R
SAS
substr(x, start, stop)
SUBSTR(x, start, length)
substring(x, start, stop)
SUBSTR with recycled bounds
stringr::str_sub(x, start, end)
SUBSTR (end, not length)
Three traps: the third argument is a stop position, not a length. substr(x, 5, 3) is not "three characters from 5"; it is the empty range 5-to-3. Write start+len-1, or count the end position. substring() and str_sub() use end positions too.
Worked examples below.
The example values
USUBJID values of the form study-site-subject, and visit codes of the form CYCnDn.
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 is 12 characters. Positions 5-7 are the site, 001. CYC1D1: cycle at position 4, day from position 6.
The empty-range mistake
SAS SUBSTR(usubjid, 5, 3) means start at 5, length 3: the site.
site = substr(usubjid, 5, 3);
R substr(usubjid, 5, 3) means start at 5, stop at 3. Stop is before start, so the range is empty:
substr(dm$USUBJID, 5, 3)
[1] "" "" ""
Three empty strings. No warning.
Use the end position, or write the SAS length as start + len - 1:
substr(dm$USUBJID, 5, 7)
[1] "001" "001" "002"
substr(dm$USUBJID, 5, 5+3-1)
[1] "001" "001" "002"
ImportantThe third argument is a stop position, not a length
substr(x, start, stop) keeps characters from start through stop inclusive. A SAS length with stop < start returns "" on every row. Count the end position, or write start + len - 1.
Visit codes: CYC1D1
Cycle is one character at position 4. Day starts at position 6 and continues to the end of the string, so CYC1D8 and CYC1D15 both work with nchar().
SAS for the cycle is SUBSTR(visit, 4, 1). In R that is substr(visit, 4, 4), not substr(visit, 4, 1).
substring() and str_sub()
Base R substring() uses the same start-and-stop model as substr(). Arguments recycle when lengths differ. Assignment is supported (substring(x, 5, 7) <- "000"). For extraction, substr() is sufficient.
stringr::str_sub() also takes end positions, not a length. str_sub(x, 5, 7) matches substr(x, 5, 7). Negative indices count from the end. It is not SAS SUBSTR(x, 5, 3). For a length API, compute the stop position.