Numeric Functions: ROUND, INT, SUM and the Missing-Value Trap
Numeric functions translate cleanly — but R handles missing values differently, and that difference matters for every derivation.
Learning objectives
- 1Map common SAS numeric functions (ROUND, INT, CEIL, MEAN, SUM) to R.
- 2Understand how NA propagates and why na.rm = TRUE is essential.
- 3Contrast SAS's MEAN-of-nonmissing default with R's strict NA behavior.
- 4Perform safe rounding and integer arithmetic for clinical derivations.
Key SAS® → R comparison
| Task | SAS® | R |
|---|---|---|
| Round | ROUND(x, 0.1) | round(x, 1) |
| Truncate to integer | INT(x) | trunc(x) |
| Ceiling / floor | CEIL(x) / FLOOR(x) | ceiling(x) / floor(x) |
| Absolute value | ABS(x) | abs(x) |
| Square root | SQRT(x) | sqrt(x) |
| Sum across values | SUM(a, b, c) (skips missing) | sum(a, b, c, na.rm = TRUE) |
| Mean | MEAN(of x1-x5) (skips missing) | mean(x, na.rm = TRUE) |
| Max / Min | MAX(a,b) / MIN(a,b) | max(a,b, na.rm=TRUE) / min(...) |
| Is missing? | if x = . then ... | if (is.na(x)) ... |
Code examples
data vs;
set vs;
bmi = round(weight / (height/100)**2, 0.1);
run;vs <- vs |>
mutate(bmi = round(weight / (height/100)^2, 1))Note: round(x, 1) rounds to one decimal place. Note R uses ^ for exponentiation where SAS uses **.
data lb;
set lb;
/* SAS SUM/MEAN automatically skip missing */
total = sum(of result1-result3);
avg = mean(of result1-result3);
run;lb <- lb |>
mutate(
total = rowSums(across(result1:result3), na.rm = TRUE),
avg = rowMeans(across(result1:result3), na.rm = TRUE)
)
# Plain sum() WITHOUT na.rm returns NA if any value is missing:
sum(c(1, 2, NA)) # NA
sum(c(1, 2, NA), na.rm = TRUE) # 3Note: This is the #1 gotcha for SAS programmers. SAS's SUM()/MEAN() silently ignore missing values; R's sum()/mean() return NA unless you add na.rm = TRUE.
data lb;
set lb;
if aval = . then flag = 'Missing';
else if aval > uln then flag = 'High';
else flag = 'Normal';
run;lb <- lb |>
mutate(flag = case_when(
is.na(aval) ~ "Missing",
aval > uln ~ "High",
TRUE ~ "Normal"
))Note: Always test is.na() FIRST. In R, NA > uln evaluates to NA (not FALSE), so an untested missing value can slip through your conditions.
Explanation
Numeric functions are largely a vocabulary swap — round, abs, sqrt, ceiling, floor all behave as you'd expect. The one conceptual difference that will bite every new R programmer is missing-value handling. SAS treats the missing value as a special low value and its aggregate functions (SUM, MEAN, MIN, MAX) quietly drop missings. R treats NA as 'unknown', and the philosophy is that any computation involving an unknown is itself unknown — so mean(c(1,2,NA)) is NA, not 1.5.
The fix is the na.rm = TRUE argument, available on virtually every aggregation function. Get in the habit of adding it deliberately, and document when you do, because in clinical derivations the decision to include or exclude missing values is a scientific one, not a coding afterthought.
Be equally careful in conditional logic. Because comparisons against NA return NA rather than FALSE, an if/case_when that doesn't test is.na() first can misclassify records. The idiom is always to handle the missing case explicitly and first.