BookmarkSubscribeRSS Feed
Kirito1
Quartz | Level 8

I was just trying to learn SAS more thoroughly, I came across the below code can anyone explain what does this code is performing. (much appreciated if you could do a line by line explanation except data and set. xD)

%macro mymacro(k);
data want;
set temp;
%do i = 1 %to &k;
if _N_ = &i then y = %eval(&i.* 10);
%end;
run;
%mend;
data _null_;
call execute ('%mymacro(6)');run;

 Kindly, help. 

Thanks in advance. Much Appreciated. 🙂

7 REPLIES 7
yabwon
Onyx | Level 15

Hi,

 

First of of all, this code is just an ugly version of:

data want; 
  set temp; 
  if _N_ <= 6 then y = _N_*10; 
run;

but lets got through it.

 

 

Starting from the end:

 

data _null_;
call execute ('%mymacro(6)');run;

 

this part is running call execute() function to run macro "mymacro" defined just before that data step. Such code can be justified in a situation when you want to use data from some dataset as input parameters for some macro e.g., a dataset contains variable "date" and you want to run a macro for each value of "date" from that dataset. In case of the code we are looking at it could be just replaced with simple:

 

%mymacro(6)

 

The part:

 

%macro mymacro(k);
...
%mend;

 

defines a macro named "mymacro". Macros, in short words, are the way to make (or rather "add to") static 4GL language more dynamics. Or, another "short", macros are "code which writes code".

Inside the macro definition you can see both 4GL code and macro code. 4GL code is for example:

 

data want;
set temp;

if _N_ =  then y = ;

run;

 

and macro code is:

 

%do i = 1 %to &k;
         &i       %eval(&i.* 10);
%end;

 

The %do-loop is a macro loop which will repeat and generate text which is in its body i.e.,

 

if _N_ = &i then y = %eval(&i.* 10);

 

(so mix of 4GL and macro language) into pure 4GL. For example the first iteration will give you:

 

if _N_ = 1 then y = 10;

 

and the 5th iteration will give:

 

if _N_ = 5 then y = 50;

 

so the "final code generated by the macro", and executed for value 6 will be:

data want; 
set temp; 
if _N_ = 1 then y = 10; 
if _N_ = 2 then y = 20; 
if _N_ = 3 then y = 30; 
if _N_ = 4 then y = 40; 
if _N_ = 5 then y = 50; 
if _N_ = 6 then y = 60; 
run;

 

But the best way to check it out would be to generate a test dataset temp:

data temp;
do x=1 to 10;
output;
end;
run;

and run the code.

 

 

All the best

Bart

 

 

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



PaigeMiller
Diamond | Level 26

Great explanation from @yabwon !

 

I add that if you see macro code, and don't understand what it is doing, you can turn on macro debugging options before you run the code.

 

options mprint;

 

So now if you run the code

 

%macro mymacro(k);
data want;
set temp;
%do i = 1 %to &k;
if _N_ = &i then y = %eval(&i.* 10);
%end;
run;
%mend;
%mymacro(6)

 

 

then you see this in the log, this is the code generated by the macro

 

MPRINT(MYMACRO):   data want;
MPRINT(MYMACRO):   set temp;
MPRINT(MYMACRO):   if _N_ = 1 then y = 10;
MPRINT(MYMACRO):   if _N_ = 2 then y = 20;
MPRINT(MYMACRO):   if _N_ = 3 then y = 30;
MPRINT(MYMACRO):   if _N_ = 4 then y = 40;
MPRINT(MYMACRO):   if _N_ = 5 then y = 50;
MPRINT(MYMACRO):   if _N_ = 6 then y = 60;
MPRINT(MYMACRO):   run;

 

So this is yet another way to see what macro code is doing.

--
Paige Miller
SASKiwi
PROC Star

Also it is worth pointing out that this code is overly complicated. You can easily get the same result without a macro at all:

%let k = 6;

data want;
set temp;
if _N_ <= &k then y = _N_* 10;
run;

:

 

Kurt_Bremser
Super User

This code is a textbook example for the abuse of the macro language. It illustrates the inefficiency of repeatedly creating code vs. using repeating code.

Let's make a small dataset:

data have;
do x = 1 to 10000;
  output;
end;
run;
 69         data have;
 70         do x = 1 to 10000;
 71           output;
 72         end;
 73         run;
 
 NOTE: The data set WORK.HAVE has 10000 observations and 1 variables.
 NOTE:  Verwendet wurde: DATA statement - (Gesamtverarbeitungszeit):
       real time           0.00 seconds
       user cpu time       0.00 seconds
       system cpu time     0.00 seconds

As you can see, this step runs in almost no time.

Now, let's apply your macro to half of the dataset:

%macro mymacro(k);
data want;
set have;
%do i = 1 %to &k.;
if _n_ = &i. then y = %eval(&i. * 10);
%end;
run;
%mend;
%mymacro(5000)
 69         %macro mymacro(k);
 70         data want;
 71         set have;
 72         %do i = 1 %to &k.;
 73         if _n_ = &i. then y = %eval(&i. * 10);
 74         %end;
 75         run;
 76         %mend;
 77         %mymacro(5000)
 
 NOTE: There were 10000 observations read from the data set WORK.HAVE.
 NOTE: The data set WORK.WANT has 10000 observations and 2 variables.
 NOTE:  Verwendet wurde: DATA statement - (Gesamtverarbeitungszeit):
       real time           0.79 seconds
       user cpu time       0.78 seconds
       system cpu time     0.01 seconds

Now, use the simple code which uses the internal loop of the data step itself and only data step language:

%let k = 5000;

data want2;
set have;
if _n_ le &k. then y = _n_ * 10;
run;
 69         %let k = 5000;
 70         
 71         data want2;
 72         set have;
 73         if _n_ le &k. then y = _n_ * 10;
 74         run;
 
 NOTE: There were 10000 observations read from the data set WORK.HAVE.
 NOTE: The data set WORK.WANT2 has 10000 observations and 2 variables.
 NOTE:  Verwendet wurde: DATA statement - (Gesamtverarbeitungszeit):
       real time           0.00 seconds
       user cpu time       0.00 seconds
       system cpu time     0.00 seconds

Wow! While the use of data step language runs in next to no time (like the creation of the dataset itself), the macro loop consumes a whopping 0.8 seconds! Why is this?

The macro loop creates 5000 IF statements and runs them 10000 times, leading to the execution of 50 million logical evaluations during the data step. Such evaluations are the most time-consuming operations for any CPU (except in quantum computers). And you also must consider that compiling 5000 lines of code itself takes much longer than compiling a single line.

The data step solution runs just 10000 IFs, so it stays well within the limits set by simply reading and writing data from/to storage.

 

To me, this piece of code is an example for what the premature teaching of macro coding to SAS newbies causes. Before they have even an inkling of what the SAS language (procedures and data steps) is really capable of, the seemingly simple tool of the macro language misguides them, leading to code which is hard to understand and is inefficient on top.

Heed Maxim 11. First exploit the capabilities of the Base SAS language and tools, and resort to macro programming only once this is exhausted. The most important skill of a macro programmer is to know when to not use the macro language.

 

PS just to make sure that the codes result in the same outcome, run this:

proc compare base=want compare=want2;
var x y;
with x y;
run;
yabwon
Onyx | Level 15

Kurt ( @Kurt_Bremser ),

 

Thank you for your comment, you gave a beautiful explanation to my oversimplified comment about the original code, which was:

 

First of of all, this code is just an ugly version of:

data want; 
  set temp; 
  if _N_ <= 6 then y = _N_*10; 
run;

 

😀

 

All the best

Bart 

_______________
Polish SAS Users Group: www.polsug.com and communities.sas.com/polsug

"SAS Packages: the way to share" at SGF2020 Proceedings (the latest version), GitHub Repository, and YouTube Video.
Hands-on-Workshop: "Share your code with SAS Packages"
"My First SAS Package: A How-To" at SGF2021 Proceedings

SAS Ballot Ideas: one: SPF in SAS, two, and three
SAS Documentation



SASKiwi
PROC Star

@yabwon @Kurt_Bremser It's also a classic example of someone else's code leading a SAS beginner astray...when maybe that someone else should know better.

ballardw
Super User

I could wish that some of my inherited code was only this poor...

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
Mastering the WHERE Clause in PROC SQL

SAS' Charu Shankar shares her PROC SQL expertise by showing you how to master the WHERE clause using real winter weather data.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 7 replies
  • 769 views
  • 18 likes
  • 6 in conversation