Lesson 11, Part 3: Use of %DO Loops¶

Why do we need macros?

"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¶
In [6]:
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;
SAS Output

sashelp.retail

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:

In [ ]:
%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¶
In [ ]:
%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);
In [15]:
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;
SAS Output

Dataset: class

Obs Name Sex Age Height Weight
1 Alfred M 14 69 112.5

Dataset: iris

Obs Species SepalLength SepalWidth PetalLength PetalWidth
1 Setosa 50 33 14 2

Dataset: retail

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

In [2]:
*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
SAS Output

sashelp.class

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

sashelp.iris

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

sashelp.retail

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.

In [ ]:
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.

In [ ]:
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.

In [ ]:
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