BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
stataq
Quartz | Level 8
 

Hello,

 

I am trying to get a clear picture of when can anyone use %do %end and do end in macros.

What kind of situation demands the need of %do loops in a macros and do loops in macros?

 

The reason I ask this question I saw people are using both %do and do within the same marco. I am not sure why. whether it required two types of do loops, or it was written by two person with different writing habit.  🤣 I am new to macro, please guide me on this.

 

Please share your thoughts. If you know why we sometime we NEED % within marco, please also share your knowledge.

 

Thanks!

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

DO is a SAS statement.  You use it when that is the SAS code you want to run.

%DO is a MACRO statement.  You use it when that is the macro logic you need so that the macro can generate the SAS code you want.

 

Let's make a trivial example where you might have both.  Say you need adjust a variable's value in some way.  For example we could decide we need to convert the value to the absolute value.

If you just have one variable you could do this:

data want;
  set have;
  x= abs(x);
run;

If you have multiple variables you might want to do this:

data want;
  set have;
  array list x y z;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
run;

Now what if want to make it easy to change the dataset names and the list of variable names.  So you might create a macro to generate the SAS code for you.

%macro abs(dsin,dsout,varlist);
data &dsout;
  set &dsin;
%if %sysfunc(countw(&varlist,%str( )))=1 %then %do;
  &varlist = abs(&varlist);
%end;
%else %do;
  array list &varlist;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
%end;
run;
%mend abs;

So to generate the first program the macro call would be:

%abs(dsin=have,dsout=want,varlist=x)

And to generate the second program the macro call would be:

%abs(dsin=have,dsout=want,varlist=x y z)

View solution in original post

6 REPLIES 6
PaigeMiller
Diamond | Level 26

DO END is useful within a data step, and within PROC IML. It is used whenever the code in the data step or code in IML needs this type of structure. It can be useful when you need to perform operations using DATA step or IML variables.

 

%DO %END works only on macro variables within a macro. It works on macro variables and cannot access DATA step variables or IML variables.

 

I have seen people write incorrect code like this:

 

%if year>2004 %then %do;

 

In this case, the value of YEAR is a data set variable, and %IF cannot access the values of YEAR and therefor it cannot test whether or not YEAR>2004. %IF can't do this test here, but IF works fine:

 

if year>2004 then do;

 

The two are not generally interchangeable, although I'm sure that there are rare examples where either would get the job done. However, my philosophy is if you don't need a macro, then don't use it. If you can get by with DO END, then definitely use that.

--
Paige Miller
ballardw
Super User

Without a specific example the "why" may be questionable. We not uncommonly see incorrect use of %do/%end in code where it should not be.

 

Do/End is the typical block code inside a data step and a very few other procedures. This is the executable code.

 

The macro language is designed to create executable code. So a simple %do/%end isn't very interesting. Conditionally , as it with an %if then it means to use the code inside the block as the created code (or not based on the result of the comparison). If the %do/%end is a LOOP such as %do i=1 %to <some value>;   Then it means the code is generated a number of times and typically changes something based on the value of the &i value, such as to process a list of values.

 

Personally if I ever see a %do/%end without either an %if or a loop it probably isn't needed.

 

Here is one idea of why a %do LOOP would be needed. Consider I have a Proc report table and a graph that are related. If I use a BY statement, such as for a location, in each procedure I get sequential report tables and sequential graphs in the output. But I might want to have the graph follow the report table for the same location. So I would create a %do / %end loop to run the proc report and graph code in sequence and filter the data for each step. An example in dummy code:

 

%do loc = 1 %to 5;
   Proc report data=mydata;
   Where Location=&loc. ;
  <rest of report code>
  run;
  
  proc sgplot data=mydata;
  where Location=&loc. ;
  <rest of sgplot code>
  run;
%end;

Other uses might be to use different data sets referenced by a loop variable.

Or both if nest %do / %end loops.

 

There are lots of style choices and typically many more than one way to solve a problem. The macro language is sometimes abused to work with poor data designs that would not require macro language at all.

Tom
Super User Tom
Super User

DO is a SAS statement.  You use it when that is the SAS code you want to run.

%DO is a MACRO statement.  You use it when that is the macro logic you need so that the macro can generate the SAS code you want.

 

Let's make a trivial example where you might have both.  Say you need adjust a variable's value in some way.  For example we could decide we need to convert the value to the absolute value.

If you just have one variable you could do this:

data want;
  set have;
  x= abs(x);
run;

If you have multiple variables you might want to do this:

data want;
  set have;
  array list x y z;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
run;

Now what if want to make it easy to change the dataset names and the list of variable names.  So you might create a macro to generate the SAS code for you.

%macro abs(dsin,dsout,varlist);
data &dsout;
  set &dsin;
%if %sysfunc(countw(&varlist,%str( )))=1 %then %do;
  &varlist = abs(&varlist);
%end;
%else %do;
  array list &varlist;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
%end;
run;
%mend abs;

So to generate the first program the macro call would be:

%abs(dsin=have,dsout=want,varlist=x)

And to generate the second program the macro call would be:

%abs(dsin=have,dsout=want,varlist=x y z)
stataq
Quartz | Level 8

Thanks so much for the detailed explanation. 

Will it still work if we remove % and update the codes as following:

%macro abs(dsin,dsout,varlist);
data &dsout;
  set &dsin;
if countw(&varlist,' '))=1 then do;
  &varlist = abs(&varlist);
end;
else do;
  array list &varlist;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
end;
run;
%mend abs;

IF not, would you share more thoughts on why we need to use % here? Many thanks.

Tom
Super User Tom
Super User

No.  To see for yourself run the call with MPRINT turned on.

Your macro will generate this code when called with VARLIST=x y z

data want;
  set have;
if countw(x y z,' ')=1 then do;
  x y z= abs(x y z);
end;
else do;
  array list x y z;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
end;
run;

Which includes these two invalid SAS statements:

if countw(x y z,' '))=1 then do;
  x y z= abs(x y z);

 

Before you start writing a macro you need to know what SAS code you want the macro to generate.

PaigeMiller
Diamond | Level 26

@stataq wrote:

Thanks so much for the detailed explanation. 

Will it still work if we remove % and update the codes as following:

%macro abs(dsin,dsout,varlist);
data &dsout;
  set &dsin;
if countw(&varlist,' '))=1 then do;
  &varlist = abs(&varlist);
end;
else do;
  array list &varlist;
  do index=1 to dim(list);
    list[index] = abs(list[index]);
  end;
  drop index;
end;
run;
%mend abs;

IF not, would you share more thoughts on why we need to use % here? Many thanks.


You could certainly try it and find out if it works.

 

Without the macro having arguments, &dsin, &dsout and &varlist don't exist and can't be used. You could assign values to these variables outside of the macro, such as %let &dsin=sashelp.class; and then these macro variables can be used. However, the code will still fail as written, as you will find out. Why? The macro generates SAS code; the generated SAS code must be working legal valid SAS code, that does what you want. And this will not produce working legal valid SAS code, there will be errors in the log.

 

To quote @Tom "Before you start writing a macro you need to know what SAS code you want the macro to generate." This is incredibly important and probably the main reason that people come here with macros that are not working, they don't know what SAS code they want to generate. And so they write a macro that doesn't produce the working legal valid SAS code, and it doesn't work. It's like shooting blindfolded at a target. First you have to know where (or what) the target is.

--
Paige Miller

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 6 replies
  • 1227 views
  • 4 likes
  • 4 in conversation