Lesson 11, Part 3: Use of %DO Loops¶
"Macro variables are useful for simple text substitution. They cannot perform conditional operations, DO loops, and other more complex tasks. For this type of work, you must define a macro". (SAS Documentation)
Iterative %DO Loops¶
In the code snippet below, the iterative %DO loop generates SAS code eight times.
%DO Loop with Sequential Values - Example 1¶
ods html close;
options nocenter nodate nonumber nosymbolgen;
%macro runit (first=, last=);
%local yr;
%do yr=&first %to &last;
data have_20%sysfunc(putn(&yr,z2.));
exp=20%sysfunc(putn(&yr,z2.))/4;
year=20%sysfunc(putn(&yr,z2.));
run;
%end ;
%mend runit;
%runit(first=8, last=15)
data all_yrs;
retain year;
set Have:;
run;
proc print data=all_yrs noobs split='*';
label year= 'Survey Year'
exp= 'Mean expenses*for treatment*on influenza*per person per year';
run;
| Survey Year | Mean expenses for treatment on influenza per person per year |
|---|---|
| 2008 | 502.00 |
| 2009 | 502.25 |
| 2010 | 502.50 |
| 2011 | 502.75 |
| 2012 | 503.00 |
| 2013 | 503.25 |
| 2014 | 503.50 |
| 2015 | 503.75 |
%DO %Until with Sequential Values - Example 2¶
PharmaSUG 2024 - Paper AP- 361 by Chary Akmyrado provides various examples, including the following:
%macro do_until(start, finish);
%local i;
%let i = &start;
%do %until(&i = &finish);
%put Value of i = &i;
%let i = %eval(&i + 1);
%end;
%mend do_until;
%do_until(2,6)
%DO %While Loop with Sequential Values - Example 3¶
%macro do_while(start, finish);
%local i;
%let i = &start;
%do %while(&i < &finish);
%put Value of i = &i;
%let i = %eval(&i + 1);
%end;http://localhost:8889/lab/tree/Week11_P3_various_macros.ipynb?#%DO-%While-Loop-with-non-equential-Values---Example-4
%mend do_while;
%do_while(2,6)
%DO %While Loop with non-equential Values - Example 4¶
In the code snippet below, the %DO %WHILE loop over non-sequential values, but with an important nuance:
- The loop itself is sequential in the index (i)
- The values being processed (class, iris, retail) are non-numeric, non- sequential labels
The macro loops through a list of dataset names and creates a PROC PRINT code block for each data set.
- Initialize counter -> %let i=1;
- i is a loop index, starting at 1
- Define dataset list -> %let dslist=class iris retail;
- Extract the first dataset -> %let ds=%scan(&dslist, &i, %str( ));
- %scan() picks the i-th word from the list.
- The macro starts a loop -> %do %while(&ds ne );
- ... and keeps looping while ds is not empty.
- Move the increment counter -> %let i=%eval(&i+1);
- Get next dataset -> %let ds=%scan(&dslist, &i);
ods html close;
options nocenter nodate nonumber nonotes nosymbolgen;
%macro process_ds;
%let i=1;
%let dslist=class iris retail;
%let ds=%scan(&dslist, &i, %str( ));
%do %while(%length(&ds) > 0);
title "Dataset: &ds";
proc print data=sashelp.&ds (obs=1);
run;
%let i=%eval(&i+1);
%let ds=%scan(&dslist, &i, %str( ));
%end;
%mend process_ds;
%process_ds;
| Obs | Name | Sex | Age | Height | Weight |
|---|---|---|---|---|---|
| 1 | Alfred | M | 14 | 69 | 112.5 |
| Obs | Species | SepalLength | SepalWidth | PetalLength | PetalWidth |
|---|---|---|---|---|---|
| 1 | Setosa | 50 | 33 | 14 | 2 |
| Obs | SALES | DATE | YEAR | MONTH | DAY |
|---|---|---|---|---|---|
| 1 | $220 | 80Q1 | 1980 | 1 | 1 |
%DO Loop using a nonsequential list of values Code in this link - Example 1¶
%DO Loop using a nonsequential list of values (code below) - Example 2¶
The macro variable, list, holds a pipe-delimited list of datasets.
The macro variable, count, correctly counts how many items are in the list.
The macro %loop iterates from 1 → &count.
%SCAN() extracts each dataset name.
After the macro processor writes the SAS code, the generated SAS code, that is, the PROC PRINT, runs for each dataset.
Remember: Macro processor → writes the program SAS compiler/executor → runs the program 1
*Ex7_%DO_Nonsequential1.sas;
options nonumber nocenter nodate symbolgen;
%let list = %str(sashelp.class|
sashelp.iris|
sashelp.retail);
%let count=%sysfunc(countw(&list, %str(|)));
%macro loop;
%local i dsname;
%do i = 1 %to &count;
%let dsname = %scan(&list, &i, %str(|));
title "%left(&dsname)";
proc print data=&dsname (obs=5) noobs;
run;
%end;
%mend loop;
%loop
| Name | Sex | Age | Height | Weight |
|---|---|---|---|---|
| Alfred | M | 14 | 69.0 | 112.5 |
| Alice | F | 13 | 56.5 | 84.0 |
| Barbara | F | 13 | 65.3 | 98.0 |
| Carol | F | 14 | 62.8 | 102.5 |
| Henry | M | 14 | 63.5 | 102.5 |
| Species | SepalLength | SepalWidth | PetalLength | PetalWidth |
|---|---|---|---|---|
| Setosa | 50 | 33 | 14 | 2 |
| Setosa | 46 | 34 | 14 | 3 |
| Setosa | 46 | 36 | 10 | 2 |
| Setosa | 51 | 33 | 17 | 5 |
| Setosa | 55 | 35 | 13 | 2 |
| SALES | DATE | YEAR | MONTH | DAY |
|---|---|---|---|---|
| $220 | 80Q1 | 1980 | 1 | 1 |
| $257 | 80Q2 | 1980 | 4 | 1 |
| $258 | 80Q3 | 1980 | 7 | 1 |
| $295 | 80Q4 | 1980 | 10 | 1 |
| $247 | 81Q1 | 1981 | 1 | 1 |
Macro with a list of values of a macro Variable (code below) - Example 3¶
The macro variable DSLIST contains the dataset names.
The %DO loop is used to step through the list, with %DO loop index (&i) serving as the word counter.
The %SCAN is used to retrieve the &ith word (data set names).
The call to macro prints observations from each of the three data sets.
options nocenter nodate nonumber symbolgen;
%macro loop(dslist);
%local xcount i;
/* Count the # of values in the string */
%let xcount=%sysfunc(countw(&dslist, %STR(|)));
/* Loop through the total # of data sets */
%do i = 1 %to &xcount;
title "%left(%scan(&dslist,&i,%str(|)))";
proc print data=%scan(&dslist,&i,%str(|))
(obs=5) noobs;
run;
%end;
%mend loop;
%loop(%str(sashelp.class|sashelp.iris|sashelp.retail))
Macro with a list of macro Variables (Array of Macro Variables), code below - Example 4¶
There is an array of macro variables (DS1, DS2 and DS3).
Each macro variable has a common prefix (DS) and also an iterative integer counter (suffix) on the end.
The two ampersands (&&) in front of each of the macro variable name force the macro processor to scan the macro variable twice.
The 1st iteration resolves &&DS&i to &DS1 in the first scan and &DS1 to CLASS in the second scan.
The 2nd iteration resolves &&DS&i to &DS2 in the first scan and &DS2 to REVHUB2 in the second scan.
The 3rd iteration resolves &&DS&i to &DS3 in the first scan and &DS3 to IRIS in the second scan.
options nodate nonumber symbolgen;
%macro VList;
%local ds1 ds2 ds3 j;
%let ds1 = class;
%let ds2 = revhub2;
%let ds3 = iris;
%let dscount = 3;
%do j = 1 %to &dscount;
title1 "%upcase(sashelp.&&ds&j)";
proc print data=sashelp.&&ds&j (obs=3) noobs;
run;
%end;
%mend VList;
%VList
Macro with a single macro variable that contains an array of elements (code below)¶
The macro variable DSLIST contains an array of data set names.
The %DO loop is used to step through the list with %DO loop index (&J) serving as the word counter.
The %SCAN is used to retrieve the &jth data set of interest from the macro variable array (DSLIST).
The call to macro prints observations from each of the three data sets.
options nodate nonumber symbolgen;
%macro HList;
%local dslist j;
%let dslist = class revhub2 iris;
%do j =1 %to %sysfunc(countw(&dslist));
title1 "sashelp.%scan(&dslist,&j)";
proc print data= sashelp.%scan(&dslist, &j)(obs=3) noobs;
run;
%end;
%mend HList;
%HList