I'm triying to run the next Macro, and I have hoped the expression "&minimum_age le &age le &maximum_age" take a false value, so output at log should be: "Under the required age", but evaluation is take value true. I really do not understand it.
%let date_of_birth = '11may2008'd;
%let minimum_age = 18;
%let maximum_age = 72;
%let age = %sysfunc(int(%sysfunc(yrdif(&date_of_birth,%sysfunc(today()),'AGE'))));
%put &= &age;
%Macro test;
%if not(&minimum_age le &age le &maximum_age) %then %put Under the required age;
%Mend;
%test;
Evaluation :
&minimum_age le &age is evaluated to "0"
then
0 le &maximum_age is evaluated to 1
so not(1) = 0
and finally , %put Under the required age is not executed
Change to :
%let date_of_birth = '11may2008'd;
%let minimum_age = 18;
%let maximum_age = 72;
%let age = %sysfunc(int(%sysfunc(yrdif(&date_of_birth,%sysfunc(today()),'AGE'))));
%put &= &age;
%Macro test;
%put %eval(&minimum_age ge &age);
%put %eval(&age ge &maximum_age);
%if &minimum_age ge &age or &age ge &maximum_age %then %put Under or up to the required age;
%Mend;
%test;
Related
I'm encountering problems when I use the CALL SYMPUT function inside a if-then loop in SAS.
%LET food=kebab;
DATA _NULL_;
IF &food.=pizza THEN DO;
CALL SYMPUT('price',12);
CALL SYMPUT('fat',5);
END;
ELSE IF &food.=kebab THEN DO;
CALL SYMPUT('price',6);
CALL SYMPUT('fat',4);
END;
RUN;
%put &food.;
%put &price.;
%put &fat.;
The variables actually take these values :
food = kebab ; price = 12 (instead of desired value 6) ; fat = 5 (instead of 4)
Thanks in advance for any explanation.
Because you're using a data step, your IF statements need quotes. If it was %IF then your code would be closer to correct.
%LET food=kebab;
DATA _NULL_;
IF "&food." = "pizza" THEN DO;
CALL SYMPUT('price',12);
CALL SYMPUT('fat',5);
END;
ELSE IF "&food." = "kebab" THEN DO;
CALL SYMPUT('price',6);
CALL SYMPUT('fat',4);
END;
RUN;
%put &food.;
%put &price.;
%put &fat.;
Another option is full macro logic, this will work in SAS 9.4M5+
%LET food=kebab;
%IF &food.=pizza %THEN %DO;
%let price = 12;
%let fat=5;
%END;
%ELSE %IF &food.=kebab %THEN %DO;
%let price = 6;
%let fat=4;
%END;
%put &food.;
%put &price.;
%put &fat.;
EDIT: If you're not on SAS 9.4M5+ which supports open macro code you need to wrap your logic in a macro.
%macro create_variables();
%global price fat;
%LET food=kebab;
%IF &food.=pizza %THEN %DO;
%let price = 12;
%let fat=5;
%END;
%ELSE %IF &food.=kebab %THEN %DO;
%let price = 6;
%let fat=4;
%END;
%put &food.;
%put &price.;
%put &fat.;
%mend;
%create_variables();
I'm trying validate if a char macro variable contains another substrings in either order.
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test/minoperator;
%if Coop Fin in &variable %then %put i = 1;
%else %put i = 0;
%mend;
%Test;
Coop Fin in &variable resolves to TRUE, but Coop TDC resolves to FALSE.
Are there anything form to do it, without import order?
You can do a regular expression matching, the logic below ignores the order:
Solution:
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test/minoperator;
%if %sysfunc(prxmatch('Coop',"&variable.")) & %sysfunc(prxmatch('TDC',"&variable.")) %then %put i = 1;
%else %put i = 0;
%mend;
%Test;
Output:
MLOGIC(TEST): Beginning execution.
SYMBOLGEN: Macro variable VARIABLE resolves to Coop Fin TDC Real Telco
SYMBOLGEN: Macro variable VARIABLE resolves to Coop Fin TDC Real Telco
MLOGIC(TEST): %IF condition %sysfunc(prxmatch('Coop',"&variable.")) & %sysfunc(prxmatch('TDC',"&variable.")) is TRUE
MLOGIC(TEST): %PUT i = 1
i = 1
MLOGIC(TEST): Ending execution.
If you want to check if ANY of the words exist then you need to parse in string and run for each word:
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test(input) /minoperator;
%local j n str;
%let n=%sysfunc(countw(&input));
%let i=0;
%do j=1 %to &n;
%let str = %scan(&input,&j);
%if &str in &variable %then %let i = 1;
%else %put i = 0;
;
%end;
%put i = &i;
%mend;
%Test(Coop Fin);
%Test(Coop TDC);
If you want all words then you need count the number of times it resolves to true and only output if that is equal to the count.
%let variable = Coop Fin TDC Real Telco;
options mlogic mprint symbolgen;
%Macro Test(input) /minoperator;
%local j n str;
%let n=%sysfunc(countw(&input));
%let i=0;
%do j=1 %to &n;
%let str = %scan(&input,&j);
%if &str in &variable %then %let i = %eval(&i+1);
;
%end;
%if &i=&n %then
%put i = &i;
%else %put i = 0;
;
%mend;
%Test(Coop Fin);
%Test(Coop TDC x);
I can't figure out how to pass to a conditional macro the values of a dataset variable. Let's say we HAVE:
data HAVE;
input id name $ value ;
datalines;
1 pluto 111
2 paperino 222
3 trump 333
4 topo 444
;
run;
I would like to use dataset variable name inside a sas conditinal macro to make other IF conditions.
What i mean is to use this code with another IF step before the conditional macro (or inside the conditional macro)
options minoperator mlogic;
%macro test(var) / mindelimiter=',';
%if &var in(pippo,pluto) %then %do; "if &var"n = name; end;
%else %do;"mod &var"n = name;end;
%mend test;
data want;
set have;
%test(pippo);
%test(arj);
%test(frank);
%test(pluto);
%test(george);
run;
For example:
options minoperator mlogic;
%macro test(var) / mindelimiter=',';
if name = &var then do;
%if &var in(pippo,pluto) %then %do "if &var"n = name; %end;
%else %do; "mod &var"n = name; end;
end;
%mend test;
but the IF name = &var is always true... there's some problem with using the name dataset variable inside the macro.
EDIT AFTER first answer
example code of conditioning inside the conditional macro:
%macro test(var) / mindelimiter=',';
%if &var in(pippo pluto) %then %do;
if name = 'pluto' then ifif_&var. = name;
if_&var. = name;
%end;
%else %do;
mod_&var. = name;
%end;
%mend test;
it' just an example off course it's almost useless.
There's nothing inherently wrong with using it that way, though you have some errors in your code.
%macro test(var) / mindelimiter=',';
if name = "&var" then do;
%if &var in(pippo pluto) %then %do; if_&Var. = name; %end;
%else %do; mod_&var. = name; %end;
end;
%mend test;
That works, or at least as far as I can tell works for what you want.
Looks like you want to generate code from your data.
data _null_;
set have end=eof;
if _n_=1 then call execute('data want;set have;');
call execute(cats('%nrstr(%test)(',name,')'));
if eof then call execute('run;');
run;
Can not use Xcommands in SAS EG. No access to SAS Management Console. How can I get a list of files in a directory without using Xcommands?
Tried DINFO but can only get 1 piece of info. Need a list of all files in the selected directory. Am I missing something here?
data a;
rc=filename("mydir", c:\");
put "rc = 0 if the directory exists: " rc=;
did=dopen("mydir");
put did=;
numopts=doptnum(did);
put numopts=;
do i = 1 to numopts;
optname = doptname(did,i);
put i= optname=;
optval=dinfo(did,optname);
put optval=;
output;
end;
run;
I've not used Enterprise Guide but how about using a pipe'd filename? You cn then use that with the infile statement to put the result of the query into a dataset...
filename dirlist pipe "ls /<your-path>/*";
data dirlist ;
infile dirlist ;
format fname $300. ;
input fname $ ;
run;
Here's a couple of macros we use to do this. The main macro is %file_list but it also requires the %isDir macro in order to run. Some usage examples:
%put %file_list(iPath=e:\blah\); * TEST AGAINST A DIR THAT DOESNT EXIST;
%put %file_list(iPath=e:\SASDev); * TEST AGAINST A DIR THAT EXISTS;
%put %file_list(iPath=e:\SASDev\,iFiles_only=1); * LIST ONLY FILES;
%put %file_list(iPath=e:\sasdev\,iFiles_only=1,iFilter=auto); * FILTER TO ONLY FILES THAT CONTAIN THE STRING AUTO;
%isDir macro definition:
/******************************************************************************
** PROGRAM: CMN_MAC.ISDIR.SAS
**
** DESCRIPTION: DETERMINES IF THE SPECIFIED PATH EXISTS OR NOT.
** RETURNS: 0 IF THE PATH DOES NOT EXIST OR COULD NOT BE OPENED.
** 1 IF THE PATH EXISTS AND CAN BE OPENED.
**
** PARAMETERS: iPath: THE FULL PATH TO EXAMINE. NOTE THAT / AND \ ARE TREATED
** THE SAME SO &SASDIR/COMMON/MACROS IS THE SAME AS
** &SASDIR\COMMON\MACROS.
**
******************************************************************************/
%macro isDir(iPath=,iQuiet=1);
%local result dname did rc;
%let result = 0;
%let check_file_assign = %sysfunc(filename(dname,&iPath));
%put ASSIGNED FILEREF (0=yes, 1=no)? &check_file_assign &iPath;
%if not &check_file_assign %then %do;
%let did = %sysfunc(dopen(&dname));
%if &did %then %do;
%let result = 1;
%end;
%else %if not &iQuiet %then %do;
%put &err: (ISDIR MACRO).;
%put %sysfunc(sysmsg());
%end;
%let rc = %sysfunc(dclose(&did));
%end;
%else %if not &iQuiet %then %do;
%put &err: (ISDIR MACRO).;
%put %sysfunc(sysmsg());
%end;
&result
%mend;
%filelist macro definition:
/******************************************************************************
** PROGRAM: MACRO.FILE_LIST.SAS
**
** DESCRIPTION: RETURNS THE LIST OF FILES IN A DIRECTORY SEPERATED BY THE
** SPECIFIED DELIMITER. RETURNS AN EMPTY STRING IF THE THE
** DIRECTORY CAN'T BE READ OR DOES NOT EXIST.
**
** PARAMETERS: iPath: THE FULL PATH TO EXAMINE. NOTE THAT / AND \ ARE TREATED
** THE SAME SO &SASDIR/COMMON/MACROS IS THE SAME AS
** &SASDIR\COMMON\MACROS. WORKS WITH BOTH UNIX AND WINDOWS.
**
******************************************************************************/
/*
** TODO. THERES ABOUT 100 WAYS THIS COULD BE IMPROVED SUCH AS SIMPLIFYING IF STATEMENTS FOR FILTERS...
*/
%macro file_list(iPath=, iFilter=, iFiles_only=0, iDelimiter=|);
%local result did dname cnt num_members filename rc check_dir_exist check_file_assign;
%let result=;
%let check_dir_exist = %isDir(iPath=&iPath);
%let check_file_assign = %sysfunc(filename(dname,&iPath));
%put The desired path: &iPath;
%if &check_dir_exist and not &check_file_assign %then %do;
%let did = %sysfunc(dopen(&dname));
%let num_members = %sysfunc(dnum(&did));
%do cnt=1 %to &num_members;
%let filename = %qsysfunc(dread(&did,&cnt));
%if "&filename" ne "" %then %do;
%if "&iFilter" ne "" %then %do;
%if %index(%lowcase(&filename),%lowcase(&iFilter)) eq 0 %then %do;
%goto next;
%end;
%end;
%if &iFiles_only %then %do;
%if %isDir(iPath=%nrbquote(&iPath/&filename)) %then %do;
%goto next;
%end;
%end;
%let result = &result%str(&iDelimiter)&filename;
%next:
%end;
%else %do;
%put ERROR: (CMN_MAC.FILE_LIST) FILE CANNOT BE READ.;
%put %sysfunc(sysmsg());
%end;
%end;
%let rc = %sysfunc(dclose(&did));
%end;
%else %do;
%put ERROR: (CMN_MAC.FILE_LIST) PATH DOES NOT EXIST OR CANNOT BE OPENED.;
%put %sysfunc(sysmsg());
%put DIRECTORY EXISTS (1-yes, 0-no)? &check_dir_exist;
%put ASSIGN FILEREF SUCCESSFUL (0-yes, 1-no)? &check_file_assign;
%end;
/*
** RETURN THE RESULT. TRIM THE LEADING DELIMITER OFF THE FRONT OF THE RESULTS.
*/
%if "&result" ne "" %then %do;
%qsubstr(%nrbquote(&result),2)
%end;
%mend;
%let path=C:\ETC;
filename parent "&path\Data\CSV";
data files;
length file_name $50;
drop rc did i;
did=dopen("parent");
if did > 0 then do;
do i=1 to dnum(did);
file_name=dread(did,i);
output;
end;
rc=dclose(did);
end;
else put 'Could not open directory';
run;
* Some additions;
%global name;
%global count2;
%let name=;
%let count2=;
proc sql;
select file_name into :name separated by '*' from work.files;
%let count2 = &sqlobs;
quit;
This works fine. I use &name for other macro and do something with files... (load from CSV, for example).
I have a large sas dataset (1.5m obs, ~250 variables) that I need to split into several smaller sas datasets of equal size for batch processing. Each dataset needs to contain all the variables but only a fraction of the obs. What is the fastest way of doing this?
You could do something like the following:
%macro splitds(inlib=,inds=,splitnum=,outid=);
proc sql noprint;
select nobs into :nobs
from sashelp.vtable
where libname=upcase("&inlib") and memname=upcase("&inds");
quit;
%put Number of observations in &inlib..&inds.: &nobs;
data %do i=1 %to &splitnum.;
&outid.&i
%end;;
set &inds.;
%do j=1 %to (&splitnum.-1);
%if &j.=1 %then %do;
if
%end;
%else %do;
else if
%end;
_n_<=((&nobs./&splitnum.)*&j.) then output &outid.&j.;
%end;
else output &outid.&splitnum.;
run;
%mend;
An example call to split MYLIB.MYDATA into 10 data sets named NEWDATA1 - NEWDATA10 would be:
%splitds(inlib=mylib,inds=mydata,splitnum=10,outid=newdata);
Try this. I haven't tested yet, so expect a bug somewhere. You will need to edit the macro call to BATCH_PROCESS to include the names of the datasets, number of new data sets, etc.
%macro nobs (dsn);
%local nobs dsid rc;
%let nobs=0;
%let dsid = %sysfunc(open(&dsn));
%if &dsid %then %do;
%let nobs = %sysfunc(attrn(&dsid,NOBS));
%end;
%else %put Open for dataset &dsn failed - %sysfunc(sysmsg());
%let rc = %sysfunc(close(&dsid));
&nobs
%mend nobs;
%macro batch_process(dsn_in,dsn_out_prefix,number_of_dsns);
%let dsn_obs = &nobs(&dsn_in);
%let obs_per_dsn = %sysevalf(&dsn_obs / &number_of_dsns);
data
%do i = 1 %to &number_of_dsns;
&dsn_out_prefix.&i
%end;
;
set &dsn_in;
drop _count;
retain _count 0;
_count = _count + 1;
%do i = 1 %to &number_of_dsns;
if (1 + ((&i - 1) * &obs_per_dsn)) <= _count <= (&i * &obs_per_dsn) then do;
output &dsn_out_prefix.&i;
end;
%end;
run;
%mend batch_process;
%batch_process( dsn_in=DSN_NAME , dsn_out_prefix = PREFIX_ , number_of_dsns = 5 );