DO Loops¶
DO loops can be used to execute a set of code over a series of items. These can be used in both DATA steps and open code inside of a %macro, however, we tend to use them most often in open code.
The basic DO loop syntax is
do i = /* start */ to /* end */ (by /* by value */);
/* SAS statements */
end;
The default value of the counter is 1 but you can modify this with the by option. Also, the index value i can be called whatever you want (i.e. opportunity would also work). This is similar to a forval loop in Stata.
data out.A;
do i = 1 to 5;
y = i*2;
output;
end;
run;
| i | y | |
|---|---|---|
| 0 | 1.0 | 2.0 |
| 1 | 2.0 | 4.0 |
| 2 | 3.0 | 6.0 |
| 3 | 4.0 | 8.0 |
| 4 | 5.0 | 10.0 |
data out.A;
do i = 1 to 100 by 20;
y = i*2;
output;
end;
run;
| i | y | |
|---|---|---|
| 0 | 1.0 | 2.0 |
| 1 | 21.0 | 42.0 |
| 2 | 41.0 | 82.0 |
| 3 | 61.0 | 122.0 |
| 4 | 81.0 | 162.0 |
Or in open code
%macro doloop;
%do i = 1 %to 5;
%put &i;
%end;
%mend doloop;
%doloop;
686 %doloop;
1
2
3
4
5
%macro doloop;
%do i = 1 %to 100 %by 20;
%put &i;
%end;
%mend doloop;
%doloop;
699 %doloop;
1
21
41
61
81
DO-WHILE Loop¶
Sometimes it is useful to execute a set of commands while a condition is true. In these cases, we can use a DO-WHILE loop.
do while (cond);
/* SAS statements */
end;
The condition is evaluated at the top of the loop before statements in the DO loop are executed. This can be used in DATA steps or open code in a %macro. For the remainder of this page, we will only show examples in open code but syntax is very similar for DATA step.
%macro dowhile(end);
%let i = 1;
%do %while(&i < &end);
%put &i;
%let i = %sysfunc(sum(&i, 1));
%end;
%mend dowhile;
%dowhile(2);
%dowhile(5);
762 %dowhile(2);
1
763 %dowhile(5);
1
2
3
4
Iterate Over a List with DO Loop¶
If we want to execute some code over a list of values then we need to be a little clever. To do this, we’re going to use a few helpful SAS functions. The code to loop over a list of items is below. The basic idea is we use a DO-WHILE loop to iteratively retrieve each element of our list until we reach the end of the list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | %macro listloop(list);
%let i = 1;
%do %while (%scan(&list, &i, " ") ~= );
* Get the value in the ith spot;
%let value = %scan(&list, &i, " ");
* Print to log;
%if %length(&value) ~= 0 %then %put &value;
*Increment counter;
%let i = %eval(&i + 1);
%end;
%mend listloop;
|
Let’s discuss the helper functions we use in this code block. The first new function here is %scan(list, i, delim) which returns the ith item in the specified list that is delimited by delim (specifying the delimiter is optional). We use this in line 5 and 7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | %macro listloop(list);
%let i = 1;
%do %while (%scan(&list, &i, " ") ~= );
* Get the value in the ith spot;
%let value = %scan(&list, &i, " ");
* Print to log;
%if %length(&value) ~= 0 %then %put &value;
*Increment counter;
%let i = %eval(&i + 1);
%end;
%mend listloop;
|
We use %scan to parse through the elements of the list we are trying to loop over. We use this with a DO-WHILE loop to continue scanning elements from our list until the element we scan is empty i.e. we reached the end of the list. We use %scan again in line 7 to retrieve the ith element in our list.
Another new function we use is %length which returns the length of the given argument. We use this in line 10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | %macro listloop(list);
%let i = 1;
%do %while (%scan(&list, &i, " ") ~= );
* Get the value in the ith spot;
%let value = %scan(&list, &i, " ");
* Print to log;
%if %length(&value) ~= 0 %then %put &value;
*Increment counter;
%let i = %eval(&i + 1);
%end;
%mend listloop;
|
Here we use %length to check that the element we retrieved from our list is not empty i.e. has non-zero length. This is overkill since we already checked in line 5 that the element is not empty.
The last new function we use here is %eval which allows us to evaluate expressions in open code. We use this in line 13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | %macro listloop(list);
%let i = 1;
%do %while (%scan(&list, &i, " ") ~= );
* Get the value in the ith spot;
%let value = %scan(&list, &i, " ");
* Print to log;
%if %length(&value) ~= 0 %then %put &value;
*Increment counter;
%let i = %eval(&i + 1);
%end;
%mend listloop;
|
We use %eval to increment our counter. Recall that in a previous section we used %sysfunc combined with sum to increment our counter.