How to use a SAS Macro inside data step - sas

I'm running a code similare to the following one and the data step is not working and I can't seem to understand why
%macro macro_1(variable);
rsubmit;
data want_&variable. (keep = a b c);
set have;
run;
endrsubmit;
%mend macro_1;
%macro testing;
%do i=1 %to 3;
%macro_1(&i.); /* My loop here*/
%end;
%mend testing;
%testing;
Here's the error I keep on getting :
Syntax error, expecting one of the following: a name, a quoted string, (, /, ;, DATA, LAST, NULL.
I have tried using the double ampersand or using more periods at the end, unsuccessfully however
Much thanks for any help !

You have defined the macro variable VARIABLE on the local machine, but the code that is using the macro variable is running on the remote machine. Try pushing the value to the remote machine before trying to use it.
%macro macro_1(variable);
%syslput variable=&variable;
rsubmit;
data want_&variable. (keep = a b c);
set have;
run;
endrsubmit;
%mend macro_1;

Related

How to batch modify SAS programs with a SAS program?

I know we can use data steps to infile "*.sas" programs as a dataset, each line of code is one record of the dataset. And then I can make changes to the dataset with SAS.
Let's say I have 100 programs already exist in c:\pgm, all I need to do is minor modifications, such as changing flag1 to flag2 for all these 100 program. If I don't want to open each program and substitute the flag one by one. Is there a way to get all program names in c:\pgm, so that I can loop over these names and do the substitution.
I'm using SAS 9.4 and EG. Thanks!
I might have overcomplicated it a bit, but the idea is the following:
extract the list of all the sas files in folder
create a macro variable array of these files
make a loop and call editFile macro on each single extracted file.
%let dir=C:\prg;
%let var_prefix=vars_;
%macro batchEdit;
data _null_;
pfad="&dir.";
rc=filename("fileref",pfad);
did=dopen("fileref");
if did eq 0 then do;
putlog "Dir does not exist";
return;
end;
num=dnum(did);
j=1;
do i=1 to dnum(did);
name=dread(did,i);
if UPCASE(substrn(name,max(1,length(name)-3),4)) eq ".SAS" then do;
call symput(CATS("&var_prefix.",j),name);
j=j+1;
end;
end;
call symput("varcnt",j);
rc=dclose(did);
rc=filename("fileref");
run;
%DO j=1 %TO &varcnt.;
%editFile(file=&&&var_prefix&j);
%END;
%mend;
%macro editFile(file=);
%put In this Macro you should define what modification should be done on each single file;
%put Current File: &file.;
%mend;
%batchEdit;
P.S. I have tested it on unix SAS server, so it might require some modifications on PC SAS.

how to display a sentence in the SAS result output window

I wrote a macro using sas. I saved the log in an external txt file, using the following code.
proc printto log="path\log.txt"; run;
%macro loop(num);
for i = 1 : #
data a;
set a;
display &i;
run;
%mend;
% loop(100)
proc printto; run;
My program works like a loop. for i = 1 : 100, do something then i = i +1. I want to display i at the end of each loop, so I know where the program is. I do not want it displayed in log /(the log txt file) because it will be mixed with the log and hard to read. so I can not use put "&i" function.
Please let me know if there is any better place to display i (maybe the result output window?) If so, how to do it please.
The log is the correct place to put this. Many tricks exist to help you make this visible.
The way the log highlights things in different colors is driven by the keywords NOTE:, WARNING:, and ERROR:. You can use those to your advantage if you don't mind slightly misleading log notes.
Turn off excess log messages, or redirect them to another location, during the bulk of the processing. This is very common to be done inside a macro.
Use things like this:
Code:
%put ********************************************************;
%put * ITERATION &i BEGINNING *;
%put ********************************************************;
All that said, it's certainly possible to do what you're asking; the question is, where is it useful for you to see it.
Two suggestions.
First: ods text will output to the results window, but it requires you to have something else printing (it won't just show up on its own). It's not dissimilar to using title, really.
%macro test;
%do i=1 %to 10;
ods text="Running &i. Iteration";
proc print data=sashelp.class;
run;
%end;
%mend test;
%test;
Similarly, you can use ods proclabel to get the Results Explorer window to show the iteration number.
%macro test;
%do i=1 %to 10;
ods proclabel="&i. Iteration";
proc print data=sashelp.class;
run;
%end;
%mend test;
%test;
I like the latter more; it makes it easier to see what's going on.
Both, however, have a major limitation: they're not going to let you see what's going on while the macro is executing, generally. You'll need something for this purpose I suspect that will update a file outside of the SAS environment; anything other than the log will often be sort of 'frozen' while the IDE thinks about doing SAS things.
If you're aiming to use this as sort of a 'how far along are we' monitor, you have several options.
First, a SAS/AF window could possibly do the trick - see Aster, NESUG '92 for an example.
Second, write to a file.
filename monitor "c:\temp\monitor.dat";
%macro test;
%do i=1 %to 100;
data _null_;
file monitor mod;
put "Iteration &i started";
run;
*... do stuff ...;
%end;
%mend test;
%test;
This is basically just making your own secondary log, which seems like perhaps the best compromise here. It does also give you the same information in the log if you have SYMBOLGEN on.
Since you are using PC SAS, in an interactive session, you can use data step PUT statements to write to the log, or the output window. In PC sas the log and the output window but update in real time (different than in EG).
%macro testmsg
(nloop=5
,file=log /* log | print */
)
;
%local i ;
%do i=1 %to &nloop ;
data _null_ ;
file &file ;
x=sleep(3) ;
put "&i" ;
run ;
%end ;
%mend testmsg ;
%testmsg(file=log)
%testmsg(file=print)

How to mask an & in a SAS macro variable call via an indirect reference

I'm using the following macro code to generate a list of clients from a table:
%macro listclient;
proc sql noprint;
select unique(client)
into :cli1 -
from books; quit;
%put Total Number of Clients: &sqlobs..;
%do i=1 %to &sqlobs;
%put Client &i &&cli&i;
%end;
%mend listclient;
%listclient
My problem is that some of the clients have names such as Smith & Jones, so I need to use some sort of masking function to get a correct resolution. I've tried a few things, my best guess being to use %nrbquote(&&cli&i) but can't seem to null the problem out. I imagine that I am making a syntax error, or that there may be an issue with the indirect macro variable referencing.
The code runs but with a warning every time an & is encountered in the client name.
I would prefer not to go down the route of replacing all of the &s with "and"s and then changing them back again!
Can anybody be of assistance?
A useful macro function for this is %SUPERQ(). You just need to provide it with the name (not the value) of the macro variable to be quoted.
%do i=1 %to &sqlobs;
%put Client &i %superq(cli&i);
%end;
You could also build your macro variable to already be formatted as quoted strings. If you use single quotes then the macro triggers inside will be ignored.
proc sql noprint;
select distinct cats("'",tranwrd(name,"'","''"),"'")
into :name1 -
from sashelp.class
;
quit;
%put &=NAME1;
NAME1='Alfred'

Why does SAS warn that these variables have never been referenced

I am running a SAS Script that used to work.
When I run this part of the script
data PosteriorProbabilities (keep=Site VarStrg2(_,&MinGrp,&MaxGrp));
set TestOut;
run;
I get the following warning
WARNING: The variable _1 in the DROP, KEEP, or RENAME list has never
been referenced.
The macro for VarStrg2 is below.
%macro VarStrg2(Pref,V_Beg,V_End) ;
%do n = &V_Beg %to &V_End ; &Pref&n %end ;
%mend VarStrg2 ;
I need this step to work so that the rest of the program can run. Any help or suggestions would be most welcome.
The warning means that the variable _1 does not exist on the input data set.
I also assume you mean:
data PosteriorProbabilities (keep=Site %VarStrg2(_,&MinGrp,&MaxGrp));
set TestOut;
run;
With the % in front of VarStrg2(...).

what is wrong with the following sas code?

I am new to sas. I have written a basic code but it is not working. Can somebody help me figure out what is wrong with the code. I wish to append the datasets.
options mprint mlogic symbolgen;
%macro temp();
%let count = 0;
%if &count = 0 %then %do;
data temp;
set survey_201106;
%let count = %eval(&count +1);
%end;
%else %do;
%do i = 201107 %to 201108;
data temp;
set temp survey_&i;
%end;
%end;
run;
%mend;
%temp;
You are setting &count to 0 at the beginning of the macro, so the %else clause will never be executed.
I'm not sure what your aim is, but it looks like you just want to concatenate 3 datasets and store in a new dataset. If so, will this not suffice:
data temp;
set survey_201106-survey_201108;
run;
This creates a dataset called temp and populates it with the the contents of survey_201106, survey_201107 and survey_201108 in order. The - tells SAS that you want the all the datasets named survey_20110* between survey_201106 and survey_201108 inclusive.
Details of the syntax.
options mprint mlogic symbolgen;
%macro temp();
proc sql noprint;
create table table_list as
select monotonic() as num,memname
from dictionary.tables
where libname = 'WORK' and memname contains 'SURVEY_';
quit;
proc sql noprint;
select count(*) into :cnt
from table_list;
quit;
%do i = 1 %to &cnt.;
%if &i eq 1 and %sysfunc(exist(work.temp)) %then %do;
proc sql;
drop table work.temp;
quit;
%end;
proc sql noprint;
select memname into :memname
from table_list
where num = &i.;
quit;
proc append base = temp data = &memname. force;
run;
%end;
%mend;
%temp;
Working: Above code will append all the work data sets whose names starting with
'SURVEY_' to temp data set.
dictionary.tables is used to create a data set which will contain the list of data sets whose names starts with 'survey_'.
table_list data set:
num memname
1 survey_201106
2. survey_201107
3. survey_201108
cnt macro variable is created to hold number of such data sets
Within loop, each data set present the list of data set names (in table table_list) will be appended to work.temp data set
What you're probably trying to do is create a macro that appends (without using proc append for some reason) when a dataset exists, or creates it new when it does.
SAS is not like r or other similar languages, where you have to control largely everything that happens. SAS's strength is that you can ask it to do common things with only a line or two of code. SAS is what's commonly called a 4th Generation Language for this reason: you're not supposed to control all of the little bits. That's a waste of time. Use the Procedures (PROC...) and constructs SAS provides you.
In this case, PROC APPEND does exactly what this whole macro does. It creates a dataset or appends new rows to it if it already exists.
proc append base=temp data=in_data;
run;
Now, if you're trying to learn the macro language and using this concept as a learning tool only, it is possible to do this in a macro that's not all that different from yours.
Note: This is not a good way to do this. It might be useful for learning macro concepts, but it should not be used as an example of good code. Despite my improvements, it is still not the way you should do this; proc append or SRSwift's example are better.
One thing I'm going to introduce here: a macro parameter. A good rule of macro programming is that all macros should have parameters. If there's no possible parameter, it should usually be possible to do without needing a macro. Parameters are what make macros useful, most of the time. In this case I'm going to rewrite your macro to take one append dataset as a parameter and one 'base' dataset. In your example, temp was the base dataset and survey_1106 etc. are the append datasets.
Also, &count needs to be a global macro variable. In SAS, variables created inside a macro are by default local in scope - ie, they only are defined inside one run of the macro and then disappear. This is nearly identical to functions in c/etc. languages (a bit different from r, which uses lexical scoping, and you might be expecting given how you wrote this). There are some funny rules, though, but for now we'll just go with this. global macro variables, which include any variable that has already been defined in the global scope, are available in all macro iterations (and outside of macros).
So:
%macro append_dataset(base=,append=);
%if &count=0 %then %do;
data &base.;
set &append.;
run;
%end;
%else %do;
data &base.;
set &base. &append.;
run;
%end;
%let count=%eval(&count.+1);
%mend append_dataset;
%let count=0;
%append_dataset(base=temp,append=survey_1106);
%append_dataset(base=temp,append=survey_1107);
%append_dataset(base=temp,append=survey_1108);
Now, you could generate those calls through an external method (such as dictionary.tables as in Harshad's example). You also could add another element to the macro, which is to iterate over all of the elements in a list provided as append. You could also hardcode the list in a %do loop, as you did in the initial example (but I think that's bad practice to get into). You could literally do that in my macro:
%macro append_dataset(base=,append=);
%do survey=201106 to 201108;
%if &count=0 %then %do;
data &base.;
set survey_&survey.;
run;
%end;
%else %do;
data &base.;
set &base. survey_&survey.s;
run;
%end;
%let count=%eval(&count.+1);
%end;
%mend append_dataset;
Notice the count increment is inside the do loop - that's one of the places you went wrong here. Otherwise this is just adding an outer loop and changing the append mentions to the calculated values from the loop. But again, this is fairly poor coding practice - the loop should at minimum be constructed from a macro parameter.