sas programming: loop over a macro call - sas

I am new to sas and have the following macro call:
%test (input1=&lib._2015, input2=&lib.base, out=out_data_2016)
Now, I want a loop that in the next step the output_data_2016 are used as input1 and create a new out variable out_data_2017. The call then should look like:
%test (input1=out_data_2016, input2=&lib.base, out_data_2017).
Thanks in advance!
edit:
I have several years to observe (say 2010 till 2018). Then I need 9 macro calls:
%test (input1=&lib._2014, input2=&lib.base, out=out_data_2015)
%test (input1=&lib._2015, input2=&lib.base, out=out_data_2016)
%test (input1=out_data_2016, input2=&lib.base, out_data_2017) etc.
My hope is to create a loop, so that I need only one step, but I do not know why...

Please read through the SAS documentation, write some code and post a new question around what you tried.
%DO, Iterative Statement
Executes a section of a macro repetitively based on the value of an index variable.
Type:
Macro statement
Restriction:
Allowed in macro definitions only (my emphasis)
See also:
%END Statement
…
Syntax
%DO macro-variable=start %TO stop <%BY increment>;
text and macro language statements
%END;

Related

% in beginning of codes in SAS

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;

Checking to see if a dataset exists

I've just finished the main macro for a project that I'm on. It generates a line to enter into another table. So, my next step is to write another macro that calls this one. One of the arguments for this next macro is the name of the dataset in which to insert this new observation, which leads to my question...
I'd like for my next macro to check and see if the named dataset exists. If so, it will insert the new calculated line into the dataset. If it dose not yet exist, I'd like to save the new line as a dataset with this name.
To get a little bit more concrete, let's suppose I have the macro
%calculate_for(ARG1, ARG2, ARG3) that creates a single-observation dataset NEXT_LINE. I want to write a macro that does something like:
%macro do_for(ARG1, ARG2, ARG3, DATASET_NAME);
%calculate_for(&ARG1, &ARG2, &ARG3)
{if DATASET_NAME exists then do:}
data &DATASET_NAME;
set &DATASET_NAME
NEXT_LINE;
run;
{if DATASET_NAME doesn't exist yet then do:}
data &DATASET_NAME;
set NEXT_LINE;
run;
%mend;
How might I go about doing this in SAS?
The macro function %SYSFUNC can be used to invoke almost any DATA step function.
For example
%macro …;
data &out;
set
%if %sysfunc (EXIST(&OUT,DATA)) %then %do;
&OUT
%end;
NEXT_LINE;
;
run;
%mend;
Likewise, the %SYSCALL routine can be used to invoke almost any CALL routine.
As #Reeza comments, for the specific coding case in your question, Proc APPEND could be the better choice. The pattern shown in your sample code would cause an entire rewrite of the base table.
Other coding patterns that do not rewrite the entire data set include
DATA Step : MODIFY statement with subsequent OUTPUT, REPLACE or REMOVE statements
Proc SQL : INSERT INTO … SELECT … FROM
If you are doing a lot of development, perhaps don't recreate the wheel at every step. Look around for SAS macro libraries that have common utility features, one example Roland's SAS® Macros

%let %put variables - what it does to your sas program

I'm new in SAS. I'd like to know what the lines below do. Couldn't figure out what it does to the program because I didn't encounter any of the defined variables in the succeeding parts after declaration.
%let cutofftime =%sysfunc(time());
%let currdt = %sysfunc(putn(&cutofftime.,time5.)) ;
%put &cutofftime. &currdt.;
The %let statement is used to create a macro variable.
The first statement:
%let cutofftime =%sysfunc(time());
uses the time() function to determine the current time. It returns the current time as a numeric value which is number of seconds since midnight.
The second statement:
%let currdt = %sysfunc(putn(&cutofftime.,time5.)) ;
uses the PUTN() to convert the numeric time value (which Is now stored in the macro variable CUTOFFTIME) to a pretty formatted value like 22:30.
So after the two %let statements have run, you created two macro variables. Then the %PUT statement is used to write the values of the two macro variables to the log:
%put &cutofftime. &currdt.;
Using the %PUT statement to write the value of macro variables to the log is a useful way to debug macro code, in the same way that the PUT statement can be used to write the value of data step variables to the log as a data step debugging tool. When I run the code at 9:32 PM, the log shows:
3 %put &cutofftime. &currdt.;
77537.809 21:32
That said, if you're new to SAS, you should probably avoid the trying to learn the macro language at the same time as you are learning the SAS language.

Why does my macro behave differently with call execute()?

Using SAS, I often want to perform an action on each row of a dataset. To do so, I use a command I found on a tutorial : call execute(). As I'm not very familiar with SAS environment, I tend to use macro-functions to do anything I don't know how to and execute them with call execute().
However I have difficulties understanding how macro-language works exactly in SAS. I know that all macro references are resolved first, which provides a base-SAS code which is then executed (or am I already wrong ?). But I don't understand how it applies with call execute().
Let's consider the following code :
%macro prog1; /* %prog1 defines a macrovariable mv1 */
%global mv1;
data _null_;
call symputx("mv1","plop");
run;
%mend;
%macro prog2(var); /* prog2 puts it on the log */
%put PUT &var;
%mend;
%macro prog_glob; /* prog_glob executes prog 1 then prog2 */
%prog1;
%prog2(&mv1);
%mend;
I know there is no need for three macros here but this is a minimal version of my real code, which has this structure.
Now if I execute prog_glob :
%prog_glob;
I get PUT plop on the log, as expected.
But if I use it with call execute() (even if there is no need for loop here) :
data _null_;
mac=%NRSTR("%prog_glob");
call execute(mac);
run;
I get only PUT.
There is no error suggesting that the macrovariable is not defined so the %global statement worked. But somehow, prog2 was executed before prog1 base part (at least I think so) and mv1 is not defined yet.
My questions are :
Is my interpretation correct ?
Why does the result change when I use call execute ?
Depending on the precedent question answer, how should I fix it or is there a more convenient way to loop trough a column values ?
EDIT : My original code intends to rename the variables of several tables, which I have listed in a dataset. For each listed table, I want the following algorithm executed :
prog1 : store a list with all variables in a macrovariable (this is where I define mv equivalent)
prog2 : add a common suffix to these variables names
There is probably a more clever way to do this. Again, I'm not so familiar with SAS and I tend to over-use macros. If you want to show me a better way to do this, I'd be happy to chat but I don't expect you guys to rewrite all my code so an alternative to call execute would be enough for me to be grateful ! :)
Let's have a look at the documentation
http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a000543697.htm
If an EXECUTE routine argument is a macro invocation or resolves to one, the macro executes immediately. However, any SAS statements produced by the EXECUTE routine do not execute until after the step boundary has been passed.
Note: Because macro references execute immediately and SAS statements do not execute until after a step boundary, you cannot use CALL EXECUTE to invoke a macro that contains references for macro variables that are created by CALL SYMPUT in that macro. See Interfaces with the Macro Facility, for an example.
This means, if you call it via call execute:
macro statements are executed immediately - those are:
1.1. first in %prog1: %global mv1; - so mv1 is defined but empty, no Apparent... warning
1.2. SAS statements from %prog1 are still deferred
Now %prog2 - here's only macro statement %PUT which puts (still empty) &mv1 variable. That what you see in the log
Now all the what gets executed immediately has been done. The data step which contains call execute ends.
SAS statements deferred from call execute are now being executed:
4.1. the dataset from prog1 sets the value for mv1.
And that's all :-)
EDIT: regarding your edit: try looking at this http://support.sas.com/kb/48/674.html
data _null_;
mac='%nrstr(%prog_glob)';
call execute(mac);
run;
or, more plainly, as you would see in the documentation...
data _null_;
call execute('%nrstr(%prog_glob)');
run;
or
%let prog=%nrstr(%prog_glob);
data _null_;
mac="&prog.";
call execute(mac);
run;
or, and I wouldn't really recommend this one, but you could also manually concatenate the macro quotes
data _null_;
mac=cats('01'x,'%prog_glob','02'x);
call execute(mac);
run;
The way you are running it, the macro statements get execute at run time and the data step is execute after the calling data step completes. You're not properly using %NRSTR for this context, as described by the documentation. You need to pass the macro, along with the quoting as text to the call routine.

Using a dynamic macro variable in a call symput statement

I posted a question a while back about trimming a macro variable down that I am using to download a CSV from Yahoo Finance that contains variable information on each pass to the site. The code that was suggested to me to achieve this was as follows:
data _null_;
a = "&testvar.";
call symputx('svar',trim(input(a,$8.)));
run;
That worked great, however I have since needed to redesign the code so that I am declaring multiple macro variables and submitting multiple ones at the same time.
To declare multiple macros at the same time I have used the following lines of code:
%let svar&e. = &svar.;
%put stock_ticker = &&svar&e.;
The varible &e. is an iterative variable that goes up by one everytime. This declares what looks to be an identical macro to the one called &svar. everytime they are put into the log, however the new dynamic macro is now throwing up the original warning message of:
WARNING: The quoted string currently being processed has become more than 262 characters long. You
may have unbalanced quotation marks.
That i was getting before i started using the symputx option suggested in my original problem.
The full code for this particular nested macro is listed below:
%macro symbol_var;
/*here the start row and end row created in the macro above are passed to this nested macro and then passed through the*/
/*source dataset. at the end of the loop each ticker macro variable is defined in turn for use in the following nested*/
/*macro, symbol by metric.*/
%do e = &beg_point. %to &end_point. %by 1;
%put stock row in dataset nasdaq ticker = &e.;
%global svar&e;
proc sql noprint;
select symbol
into :testvar
from nasdaq_ticker
where monotonic() = &e.;
quit;
/*convert value to string here*/
data _null_;
a = "&testvar.";
call symputx('svar',trim(input(a,$8.)));
run;
%let svar&e. = &svar.;
%put stock_ticker = &&svar&e.;
%end;
%mend;
%symbol_var;
Anyone have any suggestions how I could declare the macro &&svar&e. directly into the call synputx step? It currently throws up an error saying that the macro variable being created cannot contain any special characters. Ive tried using &QUOTE, %NRQUOTE and %NRBQUOTE but either I have used the function in an invalid context or I haven't got the syntax exactly right.
Thanks
Isn't this as simple as the following two line data step?
%macro symbol_var;
/*here the start row and end row created in the macro above are passed to this nested macro and then passed through the*/
/*source dataset. at the end of the loop each ticker macro variable is defined in turn for use in the following nested*/
/*macro, symbol by metric.*/
data _null_;
set nasdaq_ticker(firstobs=&beg_point. obs=&end_point.);
call symputx('svar' || strip(_n_), symbol);
run;
%mend;
%symbol_var;
Or the following (which includes debugging output)
%macro symbol_var;
/*here the start row and end row created in the macro above are passed to this nested macro and then passed through the*/
/*source dataset. at the end of the loop each ticker macro variable is defined in turn for use in the following nested*/
/*macro, symbol by metric.*/
data _null_;
set nasdaq_ticker(firstobs=&beg_point. obs=&end_point.);
length varname $ 32;
varname = 'svar' || strip(_n_);
call symputx(varname, symbol);
put varname '= ' symbol;
run;
%mend;
%symbol_var;
When manipulating macro variables and desiring bullet-proof code I often find myself reverting to using a data null step. The original post included the problem about a quoted string warning. This happens because the SAS macro parser does not hide the value of your macro variables from the syntax scanner. This means that your data (stored in macro vars) can create syntax errors in your program because SAS attempts to interpret it as code (shudder!). It really makes the hair on the back of my neck stand up to risk my program at the hands of what might be in the data. Using the data step and functions protects you from this completely. You will note that my code never uses an ampersand character other than the observation window points. This makes my code bullet proof regarding what dirty data there may be in the nasdaq_ticker data set.
Also, it is important to point out that both Dom and I wrote code that makes one pass over the nasdaq_ticker data set. Not to bash the original posted code, but looping in that way causes a proc sql invocation for every observation in the result set. This will create very poor performance for large result sets. I recommend developing an awareness of how many times a macro loop is going to cause you to read a data set. I have been bitten by this many times in my own code.
Try
call symputx("svar&e",trim(input(a,$8.)));
You need double quotes ("") to resolve the e macro.
As an aside, I am not sure you need the input statement if $testvar is a string and not a number.
I would have written this as
%macro whatever();
proc sql noprint;
select count(*)
into :n
from nasdaq_ticker;
select strip(symbol)
into :svar1 - :svar%left(&n)
from nasdaq_ticker;
quit;
%do i=1 %to &n;
%put stock_ticker = &&svar&i;
%end;
%mend;