I have written two different SAS EG programs under the same project. The first one is to estimate a parameter, say A, which will be used in the second program. At the moment, once first program finish running, I manually set the parameter
%let A = 0.1;
in the second program. I just wonder is there a way to declare A as a global variable once it is calculated in the first program and then my second program can use it without the manual input?
Thanks.
I'll post a data step, and use CALL SYMPUTX so you can assign the variable a global scope as well.
data _null_; *does not generate a data set;
set have;
call symputx('A', variable_name, 'G');
run;
Documentation for call symputx is here:
http://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a002295697.htm
Global macro variables persist for an entire SAS session (unless you delete them). This means that in one EG session, if you run your %LET statement in the first program, it will be created in the session's global macro symbol table. It will then be available to be used in any other program that runs during that same SAS session.
A SAS session persists in EG until you close EG, or manually disconnect from the SAS server.
As a separate option beyond the scope of EG, if you were to run 2 separate programs in non-interactive mode a good way of sharing variables (or parameters of any kind) is to store them in a file and read them in at the start of the next program.
Using call symputx to assign a global variable will then allow these to persist throughout your new program.
Related
With PROC SQL it's possible to connect to a Database (Db2 in my case) and execute Inserts, Deletes, etc.
If one such process causes no modifications to the target Table you will see a note like this in the Log:
NOTE: No data found/modified.
So it's clear that SAS checks for this after every such step.
Can I access this Information during the execution of a Program other than by parsing the Log on the fly?
Perhaps some sort of automatic Macrovariable/Dataset that stores the Status of the last step?
EDIT: I'm using Pass Thru SQL with EXECUTE-Statements.
Check the automatic macro variables PROC SQL creates after remote execution.
SQLXMSG contains descriptive information and the DBMS-specific return
code for the error that is returned by the pass-through facility.
Note: Because the value of the SQLXMSG macro variable can contain
special characters (such as &, %, /, *, and ;), use the %SUPERQ macro
function when printing the following value: %put %superq(sqlxmsg); For
information about the %SUPERQ function, see SAS Macro Language:
Reference.
SQLXRC contains the DBMS-specific return code that is returned by the
pass-through facility.
I have a SAS program . I need to call the SAS program multiple times, each time passing a different date parameter.
Am I correct that first I need to wrap the entire .sas file into some kind of macro and then I need to call that macro repeatedly? Or is there a way to do it without wrapping it in a macro ?
In short: maybe, yes.
Maybe:
If you have specific program you wish to launch each time with certain parameter that can be done from command line. There is sysparm-variable, which is imported to the program, like following:
> <path>SASHome\SASFoundation\9.4\sas.exe -sysparm "21537"
which in SAS code is equivalent to:
%let sysparm = 21537;
This enables you to restrict, label data wit your input as much need be. Also you can run your program as many times with any parameter you wish. What we do is parse the Sysparm to allow multiple parameter to be passed.
For more on Sysparm, see documentation
Yes:
If you want to run your code mulitple times in a session you ideally want something like:
%macro do_stuff(your_date):
%put Processing date &your_date.;
data data_&your_date.;
set someLib.begin;
if your_date < data_date < (your_date-20) ;
run;
/*And so forth....*/
%mend do_stuff;
%do_stuff(date_1);
%do_stuff(date_2);
%do_stuff(date_3);
This code executes fine when Run as a SAS program:
%MyMacro(foo_val, bar_val, bat_val);
I have created a table using:
DATA analyses;
input title : $32. weight : $32. response : $32.;
datalines;
foo1 bar1 bat1
foo2 bar2 bat2
;
I want to execute MyMacro once for each row of the analyses table.
The following code appears to only pass the string values title, weight and response (rather than the data values foo1 etc.) to my macro (tested with calls to the %put command) :
DATA _NULL_ ;
set analyses;
%MyMacro(title, weight, response);
RUN;
How can I invoke the macro once per record of the analyses table whilst passing data values as arguments to the macro? The intention is to actually run this for a very large number of analyses so the solution must scale appropriately to many more records in the analyses table.
This in part depends on what your macro is doing. If we assume that your macro is doing something that is intended to be run outside of a data step (ie, it's not just assigning a data step variable), then you have several options.
CALL EXECUTE
PROC SQL: SELECT INTO macro variable
Write macro calls into an %INCLUDE file
DOSUBL
CALL EXECUTE has already been explained, and is a good option for some cases. It has some downsides, however, particularly with macro timing, that requires some extra care to protect in some cases - particularly when you are creating macro variables inside your macro. Quentin in his comments shows a way to get around this (adding %NRSTR to the call), but I find that I prefer to only use CALL EXECUTE when there's an advantage to doing so over the other methods - particularly, if I want to use SAS data step techniques (such as FIRST or LAST, for example, or some form of looping) in creating my macro calls, or when I have to do things in a data step anyway and can avoid the overhead of reading the file another time. If I'm just writing a data step like yours above - data something, set something, call execute, run - I wouldn't use it.
PROC SQL SELECT INTO is typically what I use for list processing (which is largely what this is). I like SQL's simplicity a bit better when doing things that aren't too complicated; for example, you can get just one version of each macro call easily with DISTINCT without having to explicitly write a proc sort nodupkey or use first/last processing. It also has the advantage for debugging that you can write all of your macro calls to your results window (if you don't add noprint), which is a bit easier to read than the log for me if I'm trying to see why my calls didn't get generated properly (and doesn't take any extra PUT statements).
proc sql;
select catx(',','%macro(',arg1,arg2,arg3)||')'
into :mvarlist separated by ' '
from dataset;
quit;
&mvarlist.
That runs them quite simply, and has no timing issues (As you're just writing a bunch of macro calls out).
The main downside to this method is that you have a maximum of 64k characters in a macro variable, so if you're writing a huge number of these you'll run into that. In that case use CALL EXECUTE or %INCLUDE files.
%INCLUDE files are largely useful either as replacement for SELECT INTO when the call is over the character limit, or if you find it useful to have a text file to look at with your calls (if you're running this in batch mode for example, this could be easier to get to and/or parse than log or listing output). You just write your calls out to a file, and then %INCLUDE that file.
filename myfile temp; *or a real file if you want to look at it.;
data _null_;
set dataset;
file myfile;
length str $200;
str=catx(',','%macro(',arg1,arg2,arg3)||')';
put str;
run;
%include myfile;
I don't really use this much anymore, but it's a common technique used particularly by older SAS programmers so good to know.
DOSUBL is a relatively new method, and to some extent can be used to replace CALL EXECUTE as its default behavior is typically closer to what you expect intuitively than CALL EXECUTE's. The doc page has really the best example for how this works differently; basically, it fixes the timing issue by letting each separate call look import and export the macro variables from/to the calling environment, meaning that each iteration of DOSUBL is run at a distinct time versus CALL EXECUTE where everything is run in one bunch and the macro environment is 'fixed' (ie, any reference to a macro variable is fixed at run time, unless you escape it messily with %NRSTR).
One more thing worth mentioning is RUN_MACRO, a part of the FCMP language. That allows you to completely run a macro and import its contents back to the data step, which is an interesting option in some cases (for example, you could wrap a call around a PROC SQL that selected a count of something, and then import that to the dataset as a variable, all in one datastep). It's applicable if you're doing this for the purpose of calling a macro to assign a data step variable, not to run a process that does things that don't need to be imported into the data step, but it's something worth considering if you do want that data back all in the dataset that called the process.
You could use CALL EXECUTE:
data _null_;
set analyses;
call execute('%nrstr(%MyMacro('||title||','||weight||','||response||'))');
run;
You can put the variables values into macrovariables and then call your %MyMacro many times (the number of obs in your dataset) with the macrovariables as argument:
Data :
DATA analyses;
input title : $32. weight : $32. response : $32.;
datalines;
foo1 bar1 bat1
foo2 bar2 bat2
;
run;
Code to run macro :
data _NULL_;
set analyses end=fine;
call symput("ARGUMENT"||compress(_N_),catx(",",title,weight,response));
if fine then call symput("NLOOPS",compress(_N_));
run;
%*PUT &ARGUMENT1;
%*PUT &ARGUMENT2;
%MACRO MAIN;
%DO L=1 %TO &NLOOPS;
%MyMacro(&&ARGUMENT&L);
%END;
%MEND;
%MAIN;
This code executes fine when Run as a SAS program:
%MyMacro(foo_val, bar_val, bat_val);
I have created a table using:
DATA analyses;
input title : $32. weight : $32. response : $32.;
datalines;
foo1 bar1 bat1
foo2 bar2 bat2
;
I want to execute MyMacro once for each row of the analyses table.
The following code appears to only pass the string values title, weight and response (rather than the data values foo1 etc.) to my macro (tested with calls to the %put command) :
DATA _NULL_ ;
set analyses;
%MyMacro(title, weight, response);
RUN;
How can I invoke the macro once per record of the analyses table whilst passing data values as arguments to the macro? The intention is to actually run this for a very large number of analyses so the solution must scale appropriately to many more records in the analyses table.
This in part depends on what your macro is doing. If we assume that your macro is doing something that is intended to be run outside of a data step (ie, it's not just assigning a data step variable), then you have several options.
CALL EXECUTE
PROC SQL: SELECT INTO macro variable
Write macro calls into an %INCLUDE file
DOSUBL
CALL EXECUTE has already been explained, and is a good option for some cases. It has some downsides, however, particularly with macro timing, that requires some extra care to protect in some cases - particularly when you are creating macro variables inside your macro. Quentin in his comments shows a way to get around this (adding %NRSTR to the call), but I find that I prefer to only use CALL EXECUTE when there's an advantage to doing so over the other methods - particularly, if I want to use SAS data step techniques (such as FIRST or LAST, for example, or some form of looping) in creating my macro calls, or when I have to do things in a data step anyway and can avoid the overhead of reading the file another time. If I'm just writing a data step like yours above - data something, set something, call execute, run - I wouldn't use it.
PROC SQL SELECT INTO is typically what I use for list processing (which is largely what this is). I like SQL's simplicity a bit better when doing things that aren't too complicated; for example, you can get just one version of each macro call easily with DISTINCT without having to explicitly write a proc sort nodupkey or use first/last processing. It also has the advantage for debugging that you can write all of your macro calls to your results window (if you don't add noprint), which is a bit easier to read than the log for me if I'm trying to see why my calls didn't get generated properly (and doesn't take any extra PUT statements).
proc sql;
select catx(',','%macro(',arg1,arg2,arg3)||')'
into :mvarlist separated by ' '
from dataset;
quit;
&mvarlist.
That runs them quite simply, and has no timing issues (As you're just writing a bunch of macro calls out).
The main downside to this method is that you have a maximum of 64k characters in a macro variable, so if you're writing a huge number of these you'll run into that. In that case use CALL EXECUTE or %INCLUDE files.
%INCLUDE files are largely useful either as replacement for SELECT INTO when the call is over the character limit, or if you find it useful to have a text file to look at with your calls (if you're running this in batch mode for example, this could be easier to get to and/or parse than log or listing output). You just write your calls out to a file, and then %INCLUDE that file.
filename myfile temp; *or a real file if you want to look at it.;
data _null_;
set dataset;
file myfile;
length str $200;
str=catx(',','%macro(',arg1,arg2,arg3)||')';
put str;
run;
%include myfile;
I don't really use this much anymore, but it's a common technique used particularly by older SAS programmers so good to know.
DOSUBL is a relatively new method, and to some extent can be used to replace CALL EXECUTE as its default behavior is typically closer to what you expect intuitively than CALL EXECUTE's. The doc page has really the best example for how this works differently; basically, it fixes the timing issue by letting each separate call look import and export the macro variables from/to the calling environment, meaning that each iteration of DOSUBL is run at a distinct time versus CALL EXECUTE where everything is run in one bunch and the macro environment is 'fixed' (ie, any reference to a macro variable is fixed at run time, unless you escape it messily with %NRSTR).
One more thing worth mentioning is RUN_MACRO, a part of the FCMP language. That allows you to completely run a macro and import its contents back to the data step, which is an interesting option in some cases (for example, you could wrap a call around a PROC SQL that selected a count of something, and then import that to the dataset as a variable, all in one datastep). It's applicable if you're doing this for the purpose of calling a macro to assign a data step variable, not to run a process that does things that don't need to be imported into the data step, but it's something worth considering if you do want that data back all in the dataset that called the process.
You could use CALL EXECUTE:
data _null_;
set analyses;
call execute('%nrstr(%MyMacro('||title||','||weight||','||response||'))');
run;
You can put the variables values into macrovariables and then call your %MyMacro many times (the number of obs in your dataset) with the macrovariables as argument:
Data :
DATA analyses;
input title : $32. weight : $32. response : $32.;
datalines;
foo1 bar1 bat1
foo2 bar2 bat2
;
run;
Code to run macro :
data _NULL_;
set analyses end=fine;
call symput("ARGUMENT"||compress(_N_),catx(",",title,weight,response));
if fine then call symput("NLOOPS",compress(_N_));
run;
%*PUT &ARGUMENT1;
%*PUT &ARGUMENT2;
%MACRO MAIN;
%DO L=1 %TO &NLOOPS;
%MyMacro(&&ARGUMENT&L);
%END;
%MEND;
%MAIN;
I am trying to breakdown data file into small files, with one of the variables as a part of the name for those files. To be specific, I have a bunch of Census tracts, plus other variables. I am reading them into the matrix, perform some operations and now would like to export the data out of the loop and save it as external data file, with census tract as a part of the name; this has to be done without breaking the loop or quitting IML as I am moving onto the next tract:
read i = first census tract;
append data from other matrix;
save out file as "rld_'census_tract' value";
read next census tract;
repeat;
I tried symput function but it requires using data null inside the IML which breaks the flow.
I don't know the solution in IML (or even if there is), but I'd suggest a different solution.
Write all of your matrices out to a single dataset (either by appending them all together or by appending to a single dataset as the loop progresses, whichever is easier), and append 'census tract' as one variable in that dataset. Then use a sas datastep to write them out to separate files afterwards. If you're talking about writing out to separate sas datasets, you can either use conditional logic or you can create a macro call to do it; if you are writing external files such as CSV or text file, you can use a filename variable (filevar option on the file statement) and write it out that way.
This will be fairly efficient (in particular for the external file method) and doesn't require staying in IML.
In SAS/IML 12.1 you can use the USE and CREATE statements with arguments that are evaluated at run time.
If you have not yet upgraded to SAS/IML 12.1, you can use CALL SYMPUT and the SYMGET function to fill and retrieve a macro variable. These functions work in the IML language: no need to use DATA NULL. See my article on macros and loops in IML.