SAS macros to get the output for a desired range - sas

I'm new to SAS. Please help with my query.
I have my codes working fine for a single date where I have defined
%let yyyymmdd= 20020509;
data x;
set PQ.CP_&yyyymmdd
and then the conditions to get the desired output.
Now, I want my same codes to work for the period between startrange=-50 days before yyyymmdd and endrange=-10 days before yyyymmdd. How can I define this in the beginning to get the output for this period also as I want to use it further. Please suggest me a way to do this.

Ok - am answering this in the context of your previous question - to address just the current question, ignore the 'inner loop' below:
%macro loop(yyyymmdd=, startrange=, endrange=);
%local date x ds1 ds2 y;
%let date=%sysfunc(mdy(%substr(&yyyymmdd,5,2)
,%substr(&yyyymmdd,7,2)
,%substr(&yyyymmdd,1,4)));
/* this outer loop answers the curent question */
%do x=&startrange %to &endrange;
%let ds1=PQ.CP_%sysfunc(intnx(day,&date,&x),yymmddn8.);
%if %sysfunc(exist( &ds1 )) %then %do;
data x_%sysfunc(intnx(day,&date,&x),yymmddn8.);
set &ds1
/* this inner loop answers your previous question - remove if necessary */
%do y=-55 %to -10;
/* next date range is 55 to 10 days BEFORE the current iterative*/
/* value of (startrange-endrange) */
%let ds2=QA.TP_%sysfunc(intnx(day,&date,%eval(&y+&x)),yymmddn8.);
%if %sysfunc(exist( &ds2 )) %then %do;
&ds2
%end;
%end;
/* inner loop end */
;run;
%end;
%end;
/* outer loop end */
%mend;
%loop(yyyymmdd=20020509, startrange=-50, endrange=-10);

Related

Comparing date columns using sas macros

I I’m trying to compare two columns, both of which are dates. I know that some are the same so I expect to have some flagged as 1 within the macro. Which I can see when I don’t use the macro. Can anyone help.
Data test;
Set source;
*this works;
If date_1=date_2 then do;
X=1;
End;
else do;
X=0;
End;
*this doesnt work;
%macro flags(start=,flag=);
%If &start=date_2 %then %do;
&flag=1;
%End;
%else %do;
&flag=0;
%End;
%mend flags(start=flag_1,flag=x);
Run;
I tried to compare columns i was expecting a 1 if dates were the same and 0 if not.
Everything is 0 within a macro but ok if not using one.
If you want the macro to generate the same data step then make sure that it generates the IF statements also. You will need to call the macro inside of a data step so that the generated SAS code is run in a valid location.
%macro flags(start=,flag=);
if &start=date_2 then do;
&flag=1;
end;
else do;
&flag=0;
end;
%mend ;
data test;
set source;
%flags(start=flag_1,flag=x);
run;
PS SAS will evaluate a boolean expression as 1 for true and 0 for false so your code could be reduced to
%macro flags(start=,flag=);
&flag = (&start=date_2);
%mend ;
Which really does not need to be a macro.
data test;
set source;
x = (flag_1 = date_2);
run;

SAS MACRO error: Nesting of %IF statements in open code is not supported. %IF ignored

When I execute this code, it came up with an error: Nesting of %IF statements in open code is not supported. %IF ignored.
The code is as follows:
%let Vis_Perform_Filter = VISYN^=:"Y";
data cp_ref_patient_visits;
length CP_VISIT_PERFORMED 8.;
set cp_ref_patient_visits;
/* Are all visit identifiers related to actual performed visits? */
/* if select 'Yes' */
%IF %superQ(Vis_Perform_Filter) eq %THEN %DO;
CP_VISIT_PERFORMED = 1; /* 1 OR 0 */
%END;
/* if select 'No' then apply filter condition */
%ELSE %IF %superQ(Vis_Perform_Filter) ne %THEN %DO;
if &Vis_Perform_Filter. then CP_VISIT_PERFORMED = 0;
else CP_VISIT_PERFORMED = 1;
%END;
label CP_VISIT_ID = "Internal Visit Identifier"
CP_VISIT_PERFORMED = "Visit Performed Flag";
proc sort; by SUBJID CP_PATIENT CP_VISIT_ID;
run;
And the Error is:
31 %ELSE %IF %superQ(Vis_Perform_Filter) ne %THEN %DO;
ERROR: Nesting of %IF statements in open code is not supported. %IF ignored.
ERROR: Skipping to next %END statement.
I want to know why this error happens? How to solve this error?
Thank you for your help~
If you have a lot of filters that are specified as 'data step source code snippets' in possible macro variables, you may want to code a separate macro (CODEGEN_flag_logic) to examine the filters by name and determine whether or not that source code should be emitted as part of the data step.
Example:
%MACRO CODEGEN_flag_logic (FILTER, DEFAULT=1);
%* FILTER is name of macro variable containing source code that is data step logical evaluation snippet;
%if %length(&FILTER) %then
%if %symexist (&FILTER) %then
%if %length (&&&FILTER) %then
&&&FILTER;
%else
&DEFAULT;
%else
&DEFAULT;
%else
&DEFAULT;
%MEND;
%let name_flag_logic = name =: "J"; /* extant source code */
data want;
set sashelp.class;
name_flag = %codegen_flag_logic (name_flag_logic);
name_flag_inverse = %codegen_flag_logic (name_flag_logic, DEFAULT=0);
run;
%let name_flag_logic = ; /* extant macro variable with no source code */
data want;
set sashelp.class;
name_flag = %codegen_flag_logic (name_flag_logic);
run;
%SYMDEL name_flag_logic; /* macro variable not present in session or scope */
data want;
set sashelp.class;
name_flag = %codegen_flag_logic (name_flag_logic);
run;
There will still be problems if said filters contain invalid SAS code or deal with variables expected in the PDV that are not present.
Such as
/* snippet uses homeroom (which will be noted in LOG as uninitialized */
%let name_flag_logic = homeroom = 'sunny';
/* snippet is invalid sas code and will show ERROR in the LOG */
%let name_flag_logic = busroute one of (1,2,3);
You should probably move your logic about the value of the macro variable outside of the logic of the data step to make things clearer. Looks like when the macro variable is empty you want to execute the ELSE clause of the IF statement. So just set the value to something that is false.
%let Vis_Perform_Filter = VISYN^=:"Y";
%IF %superQ(Vis_Perform_Filter) eq %THEN %DO;
%let Vis_Perform_Filter=0;
%END;
Then generating your data step code does not require any macro logic, much less any nested macro logic. You just expand the value of the macro variable where the IF statement is expecting the code to use to evaluate the condition.
data cp_ref_patient_visits;
length CP_VISIT_PERFORMED 8.;
set cp_ref_patient_visits;
/* Are all visit identifiers related to actual performed visits? */
if &Vis_Perform_Filter. then CP_VISIT_PERFORMED = 0;
else CP_VISIT_PERFORMED = 1;
label CP_VISIT_ID = "Internal Visit Identifier"
CP_VISIT_PERFORMED = "Visit Performed Flag"
;
run;
proc sort; by SUBJID CP_PATIENT CP_VISIT_ID;
run;
As this blog post reveals
%if %then %else statements are not supported in open code until SAS 9.4M5. And there are restrictions to its use.
My suggestion is to get rid of the macro functions when you are inside a data step. For example, replace
%IF %superQ(Vis_Perform_Filter) eq %THEN %DO;
CP_VISIT_PERFORMED = 1; /* 1 OR 0 */
%END;
with
if VISYN ne "Y" then CP_VISIT_PERFORMED = 1;

SAS - conditional macro variable

I have a variable date=201611 and I need to create the first day of the next month in the following format '2016-12-01'. The following code works fine for the months up till 11:
%let date = 201611;
%let rok = %sysfunc(substr(&date,1,4));
%let month = %sysfunc(substr(&date,5,2));
%let xdat2_ii = &rok-%eval(&month + 1)-01;
%let xdat1 = %str(%')&xdat2_ii.%str(%');
%put &xdat1;
'2016-12-01'
I need to add some improvement to make the code working for the month 12, i.e. when the date is 201612 then to obtain '2017-01-01'.
My idea was to do it using macro, but it does not work.
%macro promenne;
%if &month < 12 %then %let xdat2_ii = &rok-%eval(&month + 1)-01
%else %if &month= 12 %then %let xdat2_ii = %eval(&rok + 1)-01-01;
%mend promenne;
Thank you for any suggestions which way to go.
When working with dates, is often easiest to use the built in date shifting functions - in this case, intnx.
/* define variable (this is a macro STRING) */
%let date=201612;
/* convert to SAS date value (numeric, num of days since 01JAN1960) */
%let dateval=%sysfunc(mdy(%substr(&date,5,2),1,%substr(&date,1,4)));
/* finally - shift to beginning of following month and format output */
%let xdat2_ii=%sysfunc(intnx(MONTH,&dateval,1,B),yymmddd10.);
%put &xdat2_ii; /* 2017-01-01 */

Unable to match macro variable with dataset variable

The character variable in dataset never matches with the macro variable. The %IF loop never comes true. Kindly advice.
I am trying to match by months and accordingly trying to create array and put counts only for specific months. Not working because the month macro variable never matches with dataset variable having month.
/*create dummy data*/
data datefile;
input tran_date date9. cnt 3.;
datalines;
13feb2015 5
10feb2015 4
11feb2015 3
05feb2015 8
08feb2015 5
01jan2015 1
20dec2014 1
31jan2015 2
23dec2014 2
12jan2015 1
;
/*calculate month*/
data datefile11;
set datefile;
tran_mon=year(tran_date)*100+month(tran_date);
run;
/*select distinct month*/
proc sql;
create table datefile12 as select distinct(tran_mon)
from datefile11 order by tran_mon;
quit;
/*convert month from numeric to character*/
data datefile11(drop=tran_mon);
informat tran_mon2 $6.;
set datefile11;
tran_mon2=tran_mon;
run;
/*create macro variables through datastep*/
data datefile13;
set datefile12;
monum = cat('mnth',_N_);
run;
data _null_;
set datefile13;
call symput(monum,trim(left(tran_mon)));
run;
/*use array to make separate column for each month and
put split count for each month to each colunms*/
%macro c;
proc sql noprint;
select count(1) into :nrow from datefile13;
quit;
%let nrow = &nrow;
data datefile14;
set datefile11;
array mon{*} mon_1 - mon_&nrow;
%do i=1 %to &nrow;
%if tran_mon2 = &&mnth&i %then %do; %put tran_mon2;
mon_&i = cnt; %end;
%else %do; mon_&i = 0 ; %end;
%end;
run;
%mend c;
%c
Your macro %if %then %do check executes while the data step is still being compiled - by the time the data step has begun to execute, there is no further opportunity to use macro logic like that.
Try doing it the other way round - write your loop using if then do data step logic instead.

SAS : how to test if a variable is numeric

I'm searching for a simple function to tell me if a given variable of a given dataset is numeric or not.
I would like to use it like this in an if statement in macro language:
%if isNumeric(ds,var) %then %do ...
But I can't find any way to achieve this simply. Do you have any idea if this is feasible?
I know it's probably trivial, but any help would be appreciated!
Thanks!
Assuming you want to know the actual type, and not the contents of a variable, there is a vartype(dsid,var-num) function which has the exact specifications you need, excepting it takes var-num not var-name. This example from the documentation should get you started; you can adapt this to use it to figure out a specific variable.
%let dsid=%sysfunc(open(mydata,i));
%let varlist=;
%do i=1 %to %sysfunc(attrn(&dsid,nvars));
%if (%sysfunc(vartype(&dsid,&i)) = N) %then
%let varlist=&varlist %sysfunc(varname
(&dsid,&i));
%end;
%let rc=%sysfunc(close(&dsid));
In Data Step using vtype function, we can also determine the type of a give variable from a dataset.
data _NULL_;
set sashelp.class (obs=1);
call symput('numvar',ifc(vtype(sex)='N','YES','NO' ));
run;
%put |&numvar|;
or, instead of call symput, we may use the following statement for elsewhere purposes.
if ifc(vtype(sex)='N','YES','NO' )='YES';
You recently edited your question so I thought I'd contribute with the following "simple function":
%if %mf_getvartype(sashelp.class,age)=N %then %put This var is numeric;
%if %mf_getvartype(sashelp.class,name)=C %then %put This var is character;
The mf_getvartype macro function is available in the SASjs core library here, and reproduced below:
/**
#file
#brief Returns variable type - Character (C) or Numeric (N)
#details
Usage:
data test;
length str $1. num 8.;
stop;
run;
%put %mf_getvartype(test,str);
%put %mf_getvartype(work.test,num);
#param libds Two part dataset (or view) reference.
#param var the variable name to be checked
#return output returns C or N depending on variable type. If variable
does not exist then a blank is returned and a note is written to the log.
#version 9.2
#author Allan Bowe
#copyright GNU GENERAL PUBLIC LICENSE v3
**/
%macro mf_getvartype(libds /* two level name */
, var /* variable name from which to return the type */
);
%local dsid vnum vtype rc;
/* Open dataset */
%let dsid = %sysfunc(open(&libds));
%if &dsid. > 0 %then %do;
/* Get variable number */
%let vnum = %sysfunc(varnum(&dsid, &var));
%if(&vnum. > 0) %then
/* Get variable type (C/N) */
%let vtype = %sysfunc(vartype(&dsid, &vnum.));
%else %do;
%put NOTE: Variable &var does not exist in &libds;
%let vtype = %str( );
%end;
%end;
%else %put dataset &libds not opened! (rc=&dsid);
/* Close dataset */
%let rc = %sysfunc(close(&dsid));
/* Return variable type */
&vtype
%mend;