I've been struggling with this problem for a while now. I have written a macro lets call it macro1 and I want to run it on a list of years lets say 1989 1990 1995 using a do or %do loop in open code but I cannot get it to work. I have been trying things along the lines of
%let years = 1989 1990 1995
%local i next_year
%do i=1 %to %sysfunc(%countw(&years));
next_year = %scan(&years, &i);
%macro1(&next_year);
%end
to no avail. Does anyone have a convenient solution to this problem? It seems like something that another programming language would be able to do in 3 lines (I have to use SAS though as its the only available language). Any help would be great.
Your code could work inside a macro definition if you include the required %LET needed to create a macro variable. And terminate your statements with the required semi-colons. And also leave off the semi-colons you don't need.
%macro test(years);
%local i next_year;
%do i=1 %to %sysfunc(%countw(&years));
%let next_year = %scan(&years, &i);
%macro1(&next_year)
%end;
%mend test;
%test(1989 1990 1995)
You could also eliminate the unneeded NEXT_YEAR variable completely.
%macro1(%scan(&years, &i))
Really you should just avoid using macro coding. Stick to learning how to write actual SAS code first. Wait to learn how to use macros to generate code until you know what code you want to generate. Complex DO loops are much easier to create using the real language of a data step instead of trying to code in a macro processor.
data _null_;
do year=1989, 1990, 1995 ;
call execute(cats('%nrstr(%macro1)(',year,')'));
end;
run;
But sometimes wallpaper code is easier than over complicating things. Here is the three line program.
%macro1(1989)
%macro1(1990)
%macro1(1995)
Related
As part of my work, I am going through this SAS code. I have never worked on SAS before. Could anybody explain the usage of '%' in front of the SAS lines in the code below?
%if &i>0 %then %do;
or
%put ##### calling formula;
%formula();
% signs indicate macros or macro code. Macros in SAS are similar to functions in other programming languages, but not quite. You can treat them like functions though. They deal exclusively with text and only text.
The macro facility exists to handle more generalized problems. Things in SAS are done in PROCs and the DATA Step. Each PROC and DATA Step is like its own little self-contained environment - stuff that happens in there stays in there. The Macro facility gives you tools to do things like conditionally call PROCs, the DATA Step, or system options.
I highly recommend taking one of the free training courses on SAS programming. If you want to get a jump start on Macro language, start with this free course on Coursera.
To help get you started, here's what this code is doing, line-by-line.
%if &i>0 %then %do;
If the macro variable, i, is greater than 1, run some code. All macro variables start with &. The value of the macro variable i can be reviewed by typing %put &i.
%put ##### calling formula;
%put writes a line to the log in open code. This is likely being used to help debug things.
%formula();
This is a macro function that holds some code and runs it. It has no arguments. Macro functions are created with the following syntax:
%macro myMacro();
<macro or SAS code here>;
%mend;
% is the key symbol to invoke a macro. If we wanted to invoke myMacro, we can do so by prefixing it with a %:
%myMacro;
This question already has an answer here:
Output multiple dataset
(1 answer)
Closed 5 years ago.
In SAS, is there a way to reference a macro in declaring an explicit output? It gives me an error when I use the code below, which tries to use the macro "&fname" as the output name declaring at the beginning of the data step. (As a bonus, is there a way to declare the names of the output data sets as some sort of loop, so I don't have to type cd2002, cd2003, etc..).
data cd2002 cd2003 cd2004 cd2005;
set cd;
do i = 2002 to 2005;
%let fname=cats(cd,i);
if year=i then do; output &fname; end;
end
run;
Write a macro routine. This is a way to have SAS "write" code for you. There is a macro processor that expands the code and then submits it to the main interpreter.
%macro split_cd(to,from);
%local i;
data
%do i=&to %to &from;
cd&i
%end;
;
set cd;
%do i=&to %to &from;
if year=&i then output cd&i;
%end;
run;
%mend;
Use the mprint option to see what code was generated and sumbitted:
options mprint;
%split_cd(2002,2005)
To create a complex analysis, I have a program that will %include some different other programs, each one with a specific purpose (mainly one for each analysis, one for macros, one for formats and so on..). All the analysis run smoothly with one click on the final program.
Basically what I want now is to implement a system that will be able to rewrite all the code in one document: exploding the macros with the correct code, exploding the %do loops with the correct datastep code, deleting the %let statements but resolving the macro variables, and so on (example at the bottom of the post).
The only idea I had was to save the log, with the MPRINT option to write everything, delete the notes after the datastep executions and other things not datastep related. But this method is really dirty, therefore I would kindly ask if you have better ideas on how to proceed.
This is an example of 2 nested programs:
main.sas
-----------------------------------------------------
/* My MAIN PROGRAM */
%let PGM=MAIN;
%put This is my &pgm. program;
%let LIB=MYLIB;
%include &LIB.(FIRST.sas);
-----------------------------------------------------
first.sas
-----------------------------------------------------
%let ind=2;
%macro ABC;
%do rk=1 %to &ind.;
data A&rk.;
MYVAR=&rk.; output;
run;
%end;
%mend; %ABC;
-----------------------------------------------------
The document that I would get is something like:
/* My MAIN PROGRAM */
%put This is my MAIN program;
data A1;
MYVAR=1; output;
run;
data A2;
MYVAR=2; output;
run;
Any suggestion? Did you ever find out this? How you solved it? Many thanks.
See Example 2 here:
http://support.sas.com/documentation/cdl/en/mcrolref/69726/HTML/default/viewer.htm#p1dhqw0i5yj2m8n15opapnwteqra.htm
You can add these options to your program:
options mfile mprint;
filename mprint 'debugmac';
And all the generated code will appear in the specified output file. This will not include %PUT statements though - in your example, your desired output includes a %PUT statement. The options above just redirect all the normal MPRINT lines to a file, so no open code or macro statements are include. You could probably find a way to append a header to the MPRINT file though, depending on your exact needs.
Having said all that, I wouldn't recommend your approach. Why would you want to flatten the program in this way? It's extremely unlikely to have any noticeable performance benefit, and will make it much harder to debug or modify the program in future.
Please consider this sample SAS macro code:
%MACRO reports;
%IF &SYSDAY = Monday %THEN %DO;
%END;
%MEND reports;
Does every single word inside the macro need to be prefixed with a %? What exactly does the % sign mean?
% is a macro trigger, along with &. It identifies the next symbol(s) as part of a macro language element. This might be a macro call (%reports();), a macro statement (%if), a macro comment (%*), or other macro language elements.
Understanding how the SAS macro language works is pretty important to understanding the difference here. %IF for example is instructing the SAS macro processor to do something. IF is regular SAS code that will be put into the SAS data step (or whatever). Spend some time understanding what the macro language is doing - what the point of it is entirely - to fully understand that.
And, as with many things in SAS, Ian Whitlock can explain it better than I can.
The % symbol indicates that it is macro logic, no datastep logic.
Macro logic is executed before compilation, just like pre-compiler logic in C++. Fro instance
%MACRO reports ;
data lastWorkingDayData;
set allData;
%IF &SYSDAY = Monday
%THEN %DO ;
if transactionDate ge "&SYSDATE."d -3 then output;
%END ;
%ELSE %DO ;
if transactionDate ge "&SYSDATE."d -1 then output;
%END ;
RUN ;
/* your printing logic comes here */
%MEND reports ;
%reports;
will, if you run it today be converted before it is even compiled to
data lastWorkingDayData;
set allData;
if transactionDate ge "&SYSDATE."d -3 then output;
RUN ;
/* your printing logic comes here */
before it is even compiled. To understand it better, start your code with option mprint; and inspect your log
I am new to SAS and having a hard time figuring out when should the simple If-Then-else and when should %IF-%THEN-%ELSE should be used. As an example code below:
%let inFile = %scan(&sysparm, 1, " ");
%macro read_data(infile);
data want;
infile "&infile" LRECL=1000;
retain fdate;
if _n_ = 1 then do;
input Agency $ Status $ Num $ fdate sdate;
end;
else do;
%if fdate < 20130428 %then
input
#1 poolno $6.
#7 factor 9.8 #;
%else
input
#1 rectype $1
#3 poolno $6.
#9 factor 9.8 #;
#18 pfactor 9.8;
output;
end;
drop Agency Status Num sdate;
run;
proc print data=want;
run;
%mend read_data;
%read_data(&inFile);
I am trying to get the first line(header) and taking the parameter fdate. Based on the value of this parameter, I am parsing the subsequent input lines differently.
But this does not seem to work, as only the second input part runs (always getting parameter 'rectype' in output).
Any suggestions as what i might be doing wrong?
I see you have C++ as one of your tags, and that you are just getting started with SAS. So I'll try to provide an answer specific to your background.
The easiest way to understand the distinction between SAS Macro commands and the commands in the DATA step or in several procs with the same name, like %if vs. if, is to think of SAS Macro commands as equivalent to C/C++ pre-processor (CPP) directives. CPP and SAS MAcro are both macro languages, and although they are not exactly the same kind of language, they share two important and initially confusing characteristics: they are text processors; and they are executed as a separate step before the main code is processed.
There are places where this approximation breaks down, but as a beginner to SAS with background in C/C++, it is a good place to start.
The macro statement %if is compiled before any data step statement. That means you are generally not able to use a data step variable in your logic expression. When the macro processor is compiling the macro statement, data step variable doesn't exist yet.
In the example above, the %IF condition is based on a datastep variable/value. This should indicate it can be achieved using a datastep 'if' rather than a %IF.
You have already received an answer to this in your previous question, https://stackoverflow.com/a/15341502/108797
If you do %if fdate < 20130428 SAS compares literals fdate and 20130428 not
the value od fdate and 20130428.
If you had a macro variable named fdate you would do %if &fdate < 20130428.
In your case fdate is a variable in a dataset, so use if not %if, but it seems like you are trying to create a data step with a macro so just using if will probably not work in this case (depends on what are you trying to get).