The problem I have is, I use the same program for reports on multiple sites. I have to run the code for each of them individually. I'm looking to automate my process to call the code and run for all sites in a go. This is the code I tried using but it is not working.
data _null_;
array sites {2} _temporary_ (SiteA SiteB);
do k = 1 to dim(sites);
%let site = sites(k);
%include '...path\SitesWait.sas';
end; run;
The code in SiteWait works perfectly on its own. I defined a macro variable called site, which sets the site for the code to run.
The error I keep getting is 'ERROR 117-185: There was 1 unclosed DO block.'
Thanks
You cannot include another multi-step program into the middle of a DATA step. %include just puts lines from the file into the program stream as if the lines had been typed into program. When SAS sees the first DATA or PROC statement in the included file it will stop compiling the DATA step in your main program and run it. That is why your DO loop is not seeing the END statement.
You could just use a data step to generate code that sets the parameter and includes the program.
data _null_;
length site $20 ;
do site='SiteA','SiteB' ;
call execute(cats('%nrstr(%let) site=',site,';'));
call execute("%include '...path\SitesWait.sas';");
end;
run;
What you have created is basically a macro with a single parameter named SITE. In fact you could possible just use %INCLUDE as the body of the macro definition. Then you could just code the calls you want instead of trying to process some list of values.
%macro siteswait(site);
%include '...path\SitesWait.sas';
%mend;
%siteswait(SiteA)
%siteswait(SiteB)
So you wish to include different files based on prior dataset. I suggest you create the file instead of reading it from array, but keeping with the spirit of your initial request:
%let basePath= c:\foo;
data _NULL_;
array sites {2} $5. ('SiteA' , 'SiteB');
do k = 1 to dim(sites);
site = sites(k);
call execute('%nrstr(%put &basePath.\'||site||';)');
end;
run;
This produces
c:\foo\SiteA
c:\foo\SiteB
You can easily replace the %put command with %include to accomplish what you wish.
Edit: What I suggest you try to do instead is something like this:
data includes;
format path $30. file $10.;
input path $ file $;
cards;
c:\foo\ file1.sas
c:\foo\ file2.sas
c:\Bar\ file1.sas
c:\Bar\ file2.sas
; run;
data _null_;
set includes;
call execute('%nrstr(%put '||strip(path)||''||strip(file)||';)');
run;
You should do like this :
main.sas
%macro ProcessList;
%let list_of_site=SiteA|SiteB|SiteC;
%let k=1;
%do %while (%qscan(&list_of_site, &k,|) ne );
%let site = %scan(&list_of_site, &k,|);
%include 'H:\desktop\SAS\test_inc.sas';
/*%put site=&site;*/
%let k = %eval(&k + 1);
%end;
%mend ProcessList;
%processList;
test_inc.sas
%put site=&site;
Result:
site=SiteA
site=SiteB
site=SiteC
With a macro fonction it is simple. You can replace %include with %put.
You can get the list of site from a dataset with this technic :
data test;
infile datalines dsd;
input site : $200. ;
datalines;
SiteA,
SiteB,
SiteC,
SiteD,
SiteE,
SiteF,
;
run;
proc sql noprint;
select quote(trim(site), "'") into : list_of_site separated by "|" from work.test;
quit;
Regards
If the sites don't change often, and if there are not too many of them, this may be the simplest approach:
%let site=siteA;
%include '...path\SitesWait.sas';
%let site=siteB;
%include '...path\SitesWait.sas';
%let site=siteC;
%include '...path\SitesWait.sas';
Be careful though. You may want to include code at the top of SitesWait.sas that 'resets' you SAS session. ie. clears any macro values you plan on using, deletes any datasets in your work folder etc. Otherwise the second call to SitesWait.sas may be affected by the prior run.
Related
I wanna set the code run at days: 5,11,16,22,28 monthly but it doesn't work.
%if %sysfunc(day(%sysfunc(today()))) in (5,11,16,22,28) %then %do
How can I fix it? Thanks much.
The IN operator works in a SAS 9.2 Macro but you need to define system options. The MINOPERATOR (Macro-In-Operator) must be turned on and in your case, the MINDELILITER too must be specified as you are using comma delimited values.
More information can be found in Getting the IN operator to FUNCTION inside a SASĀ® Macro
options minoperator mindelimiter=',';
%if %sysfunc(day(%sysfunc(today())-1)) in (5,11,16,22,28) %then %do;
%put do smth...;
%end;
do smth...
Notice the -1 in the above example to replicate March 22.
I am trying to use the following sas programs to convert multiple rtf files into pdf files by using the VB.NET (X command). It worked perfectly in PC sas. However, while using it in the SAS Enterprise, it gave no error message but didn't perform the pdf convertion.
Does anyone know what's happening in here? What should I do to make this work again? Thanks!
%macro vbs_pdf(rtfname=,pdfname=);
data _null_;
length vbscmd $ 400;
file "temp.vbs";
put 'Dim ObjWord';
put 'set objWord = CreateObject("Word.Application")';
put 'objWord.Visible = True';
vbscmd='objWord.Documents.Open("'|| "&rtfname" ||'")';
put vbscmd;
vbscmd='objWord.ActiveDocument.SaveAs "'||"&pdfname"||'", 17';
put vbscmd;
put 'objWord.ActiveDocument.Close(False)';
put 'objWord.Application.Quit(False)';
run;options noxwait;
data _null_;
command="START /WAIT CScript temp.vbs //NoLogo";
call system(command);
command2="DEL temp.vbs ";
call system(command2);
run;
%mend;
options noxwait;
data &prefix._toc;
set &prefix._toc;
call symput('count',compress(put(_n_,best.)));
run;
proc sql noprint;
select filename
into : rtf1- :rtf&count
from &prefix._toc;
run;
%macro cvtpdfs;
%do i=1 %to &count;
%let mypdf=%scan(&&rtf&i,1,'.');
%vbs_pdf(rtfname=&file2path./&&rtf&i,pdfname=&curpath/documents/pdf/&mypdf..pdf);
%end;
%mend;
%cvtpdfs;
By default SAS EG has X commands disabled. In your SAS configuration files for EG you will see -NOXCMD. You will see more here: https://blogs.sas.com/content/sasdummy/2009/11/19/using-the-x-and-systask-commands-from-sas-enterprise-guide/
this is a toy example in order to help with a larger problem I have. It essentially involves using a %eval() macro when referencing a bigger macro variable name.
I have created a macro variable x_2, which uses the values of the loop, '&it', the variable is created successfully as can be seen by the final output, however I can only put it to the log without evaluating &it+1, which I will need to do when using a loop bigger than size 1.
It seems to resolve x_ first, giving a warning, before then evaluating x_2 as a whole and giving the output.
I realise this is just a problem about how to reference macros correctly, but I cannot find any examples where it uses an evaluation as part of a macro variable name.
Thanks.
%macro testing;
%DO it = 1 %TO 1;
data dataset;
s=100;
t=99;
run;
data _null_;
set dataset;
if s = 100 then do;
call symput("x_%eval(&it+1)",t);
end;
run;
%put "&x_%eval(&it+1)";
%put &x_2;
%END;
%mend testing;
%testing;
LOG OUTPUT
MLOGIC(TESTING): %PUT "&x_%eval(&it+1)"
WARNING: Apparent symbolic reference X_ not resolved.
SYMBOLGEN: Macro variable IT resolves to 1
SYMBOLGEN: Macro variable X_2 resolves to 99
" 99"
MLOGIC(TESTING): %PUT &x_2
SYMBOLGEN: Macro variable X_2 resolves to 99
99
MLOGIC(TESTING): %DO loop index variable IT is now 2; loop will not iterate again.
MLOGIC(TESTING): Ending execution.
Indeed, SAS does the variable substitution before calling the %eval function
The easiest solution is to call %eval in an earlier statement
%let xNr = %eval(&it+1);
%put "&&x_&xNr";
The double ampersand serves to delay the evaluation of x_&xNruntil &xNr is evaluated to 2 and not have the warning that x_ is undefined.
SYMBOLGEN: Macro variable IT resolves to 1
SYMBOLGEN: && resolves to &.
SYMBOLGEN: Macro variable XNR resolves to 2
SYMBOLGEN: Macro variable X_2 resolves to 99
" 99"
Agree with Dirk's answer. But as a thought exercise, it is possible to achieve the desired result with ugly use of quoting functions:
%let x_2=99;
%let it=1;
%put %unquote(%nrstr(&x_)%eval(&it+1));
So %nrstr hides the & until after the %eval has done its work (I think. :)
Update:
#Shenglin added a similar answer in a comment that I like better than my approach above:
%put %superq(x_%eval(&it+1));
This works because %superq() is unusual in taking a macro variable name as a parameter (no leading &), not a macro variable reference. So you can use %EVAL to generate part of the name of the macro variable. You could %unquote() if wanted, but it should not be necesssary.
It is much easier to build the macro variable name into another variable and then expand the value.
%let t2=found;
%let mvar=t%eval(1+1);
%put &&&mvar;
Could you try "&&x_%eval(&it+1)"?
I'm basically trying to make a program that can extract the relevant variables from a CSV file and perform stationarity tests on these timeseries variables without having to type out the variable names manually. I'm fairly new and maybe there's redundancies, but I've basically embeded the variable names into variable1, variable2, etc...
So when I've tested the ADF macro and input the variable as:
proc arima data=&y;
identify var= &variable1 stationarity =(adf = 3);
run;
it works, but I'm struggling to automate this process and would appreciate any help you could offer. Thanks.
Here is what I have to far:
proc import datafile=".....csv"
out=data
dbms=csv
replace;
getnames=yes;
run;
proc contents data=data out=contents noprint;
run;
data contents;
set contents(keep = name);
if Name = "Quarter" then delete;
run;
data _NULL_;
set contents;
call symputx(cats('variable',strip(_n_)),name);
run;
data want;
do i=1 to 4;
expert=symget(cats('variable',i));
output;
end;
run;
%macro ADF(y = ,x = );
proc arima data=&y;
identify var= &x stationarity =(adf = 3);
run;
%mend ADF;
You're very close... You just need to look into call execute. It will allow you to build commands and have them execute after the datastep finishes.
In the below code we're building the calls to the macro that we want to run. They will be exected as instructions after the datastep has finished processing. This will allow us to use values within the datastep to build the commands:
data want;
do i=1 to 4;
expert=symget(cats('variable',i));
call execute (cats('%ADF(y=data,x=',expert,');'));
end;
run;
There are other ways to do it but this is the simplest based on what you already have in place.
Just make sure your macro is defined prior to running the datastep.
I'd like to create dynamic code using the PUT statement. According to this document from SUGI 29 (http://www2.sas.com/proceedings/sugi29/175-29.pdf),
put
"data XXXXX; "
/ 'infile "&datadir/&compid&filetype" missover ls=' tbla_fle
';' / 'input'
;
is equivalent to running
data onecomp ;
infile
"&datadir/&compid&filetype"
missover ls = 268 ;
input
However, when I try something similar to their example, the code enclosed in the PUT statement doesn't run and is instead written to the SAS Output Log:
data _NULL_;
put // "data put_test;" / "b=2;" / "run;";
run;
In Output Log:
data put_test;
b=2;
run;
I've checked the SAS documentation, and it seems that PUT is only used to "Write lines to the SAS log, to the SAS output window, or to an external location that is specified in the most recent FILE statement." Nowhere does it say that it can be used to create dynamically generated code.
I know that I must be missing something, but I'm not sure what. I'm using SAS Enterprise Guide 4.1.
Thank you!
The idea is to use put to write your generated code to a file. You then %include the file into your SAS session to run it. What you're missing is a file statement and the %include directive.
data _null_;
file 'temp.sas'; /* redirects put to a file instead of the SAS log */
put
"data XXXXX; "
/ 'infile "&datadir/&compid&filetype" missover ls=' tbla_fle
';' / 'input'
;
run;
%include 'temp.sas';