SAS (Base SAS)+
*==============================================================================;
* Sample data: 3 subjects, 3 visits each, with numeric AVAL scores
*==============================================================================;
data advs;
infile datalines dlm='|' dsd missover;
input USUBJID : $7. VISITNUM PARAMCD : $6. AVAL;
datalines4;
101-001|1|SYSBP|130
101-001|2|SYSBP|125
101-001|3|SYSBP|120
101-002|1|SYSBP|145
101-002|2|SYSBP|140
101-002|3|SYSBP|138
101-003|1|SYSBP|118
101-003|2|SYSBP|122
101-003|3|SYSBP|119
;;;;
run;
proc sort data=advs;
by USUBJID VISITNUM;
run;
*==============================================================================;
* WRONG WAY: LAG inside a conditional IF
*==============================================================================;
* The LAG queue advances every time the DATA step executes the LAG() call.
* Placing LAG inside IF means it only advances on rows where the IF is true,
* so the "previous" value is not actually the previous row.
*------------------------------------------------------------------------------;
data wrong;
set advs;
by USUBJID VISITNUM;
if VISITNUM > 1 then do;
prev_aval = lag(AVAL);
change = AVAL - prev_aval;
end;
run;
*==============================================================================;
* RIGHT WAY: call LAG before any conditional logic
*==============================================================================;
* We call LAG unconditionally so the queue advances on every row.
* Then we use the stored value only when we need it.
*------------------------------------------------------------------------------;
data correct;
set advs;
by USUBJID VISITNUM;
prev_aval = lag(AVAL);
if first.USUBJID then do;
prev_aval = .;
change = .;
end;
else change = AVAL - prev_aval;
run;
proc print data=correct noobs;
run;- We start with ADVS sorted by USUBJID and VISITNUM so each subject's visits appear in order.
LAG(AVAL)returns the value of AVAL from the previous time the LAG function was called — not necessarily the previous row in the dataset.- In the WRONG example,
lag(AVAL)sits insideif VISITNUM > 1. On VISITNUM = 1 the LAG call never executes, so the internal queue does not advance. On VISITNUM = 2, LAG returns the value from the last time it ran — which was the previous subject's last visit, not the current subject's baseline. - In the RIGHT example, we call
prev_aval = lag(AVAL)unconditionally on every row, so the queue always moves forward one step. - We then use
first.USUBJIDto blank out prev_aval and change on the first row of each subject, because lagging across subjects is meaningless. - The
elsebranch computeschange = AVAL - prev_avalonly for visits after baseline. - This unconditional-LAG-then-guard pattern is the standard safe approach in clinical SAS programming.