How to use SAS macro with %DO and %IF - sas

When I ran a macro, the PassengerID_Class had a null value.
I think there is a problem with the sentence below.
%if PassengerID >= &&P&i and PassengerID < &&P&k %then PassengerID_Class = &i. ;
PassengerID is a column containing values from 1 to 891.
P1,P2,...,Pi are the quantiles.
Below is my code.
%macro do_loop;
DATA _NULL_;
SET PERCENTILES100_Transpose;
call symput("P"||left(_n_),Column1);
RUN;
Data Groups100;
Set sample;
%do i=2 %to 99;
%Let k = %eval(&i.+1);
%if PassengerID >= &&P&i and PassengerID < &&P&k %then PassengerID_Class = &i. ;
%end;
%mend;
%do_loop;

You cannot test the value of dataset variable with a macro expression. Just use the macro to generate a normal IF statement, the same as you are using the macro to generate the DATA and other SAS statements.
if &&P&i <= PassengerID < &&P&k then PassengerID_Class = &i. ;
But why use macro code at all? Just read the cutoff values into a temporary array.
data Groups100;
set sample;
array p[100] _temporary_;
if _n_=1 then do index=1 by 1 until(eof);
set PERCENTILES100_Transpose(keep=column1) end=eof;
p[index]=column1;
end;
PassengerID_Class=0;
do index=2 to 99 until(PassengerID_Class>0);
if p[index] <= PassengerID < p[index+1] then PassengerID_Class = index ;
end;
drop index column1 ;
run;
Note that using the data directly will also avoid loss of precision caused by converting the numbers into text strings to store macro variables.

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;

Iterate date in loop in SAS

need help on one query , I have to iterate date in do loop that is in format of yymmd6.(202112) so that once the month reach to 12 then its automatically change to next year first month.
///// code////////
%let startmo=202010 ;
%let endmo= 202102;
%macro test;
%do month= &startmo %to &endmo;
Data ABC_&month;
Set test&month;
X=&month ;
%end;
Run;
%mend;
%test;
//////////
Output should be 5 dataset as
ABC_202010
ABC_202011
ABC_202012
ABC_202101
ABC_20210
I need macro variable month to be resolved 202101 once it reached to 202012
Those are not actual DATE values. Just strings that you have imposed your own interpretation on so that they LOOK like dates to you.
Use date values instead and then it is easy to generate strings in the style you need by using a FORMAt.
%macro test(startmo,endmo);
%local offset month month_string;
%do offset = 0 to %sysfunc(intck(month,&startmo,&endmo));
%let month=%sysfunc(intnx(month,&startmo,&offset));
%let month_string=%sysfunc(putn(&month,yymmn6.));
data ABC_&month_string;
set test&month_string;
X=&month ;
format X monyy7.;
run;
%end;
%mend;
%test(startmo='01OCT2020'd , endmo='01FEB2021'd)
And if you need to convert one of those strings into a date value use an INFORMAT.
%let date=%sysfunc(inputn(202010,yymmn6.));
I would prefer to use a do while loop.
check whether the last 2 characters are 12, if so, change the month part to 01.
code
%let startmo=202010 ;
%let endmo= 202102;
%macro test;
%do %while(&startmo <= &endmo);
Data ABC_&startmo;
Set test&startmo;
X=&startmo ;
Run;
%end;
%let mon = %substr(&startmo, 5, 2);
%let yr = %substr(&startmo, 1, 4);
%if &mon = 12 %then %do;
%let m = 01;
%let startmo = %sysfunc(cat(%eval(&yr + 1), &m));
%end;
%else %do;
%let startmo = %eval(&startmo + 1);
%end;
%mend;
%test;

sas macro : how to make multiful column using macro?

I'm SAS user.
I want to assign year columns using date values.
for example, here is my code, below.
I want to make Y_2010, Y_2011, Y_2012 , Y_2013, Y_2014 in work.total data set.
but there is only Y_2014 as a result.
How can I change the code as I can get right result which I intended first?
options mcompilenote = all;
%let a = Y_ ;
%macro B(YMIN, YMAX) ;
%do i = &YMIN %to &YMAX ;
DATA TOTAL ;
SET SASUSER.EMPDATA ;
IF YEAR(HIREDATE) = &i THEN &a&i = 1 ;
ELSE &a&i = 0 ;
RUN;
%end;
%mend;
%B (2010, 2014) ;
Because you are repeatedly re-creating the output dataset only the final version is available. To fix the macro move the %DO loop inside the DATA step so that you are generating all of the variables in a single data step.
%macro B(YMIN, YMAX) ;
DATA TOTAL ;
SET SASUSER.EMPDATA ;
%do i = &YMIN %to &YMAX ;
IF YEAR(HIREDATE) = &i THEN &a&i = 1 ;
ELSE &a&i = 0 ;
%end;
RUN;
%mend;
But there is no need to a macro for this. Just use normal SAS statements. For example you could use an ARRAY statement to define the variables and then loop over the array and set the values. Note that the result of a boolean expression in SAS is 0 when false and 1 when true so you can eliminate the IF/THEN/ELSE statement and just use an assignment statement.
DATA TOTAL ;
SET SASUSER.EMPDATA ;
array &a &a&ymin - &a&ymax;
do i=&ymin to &ymax ;
&a[i-&ymin+1] = (year(hiredata)=i);
end;
drop i;
RUN;

Controlling program flow - sas

Below is the code to execute a set of data-steps based on the value of the increment variable "i". Since I have assigned the value of i to 1 (numeric and not character value). Ideally the first data-step block need to execute, but in the below case the second data-step block is executing.
%put &i. ; prints 1 in to the log window.
%macro DSN;
%let i = 1 ;
data new_DSN;
run;
%if i = 1 %then %do;
data Dummy ;
run;
data DUMMY_ ;
set DUMMY new_DSN ;
run;
%end;
%else %if i ^= 1 %then %do ;
data DUMMY_ ;
set DUMMY_ new_DSN ;
run;
%end;
%mend DSN;
%DSN;
Your IF statement is not calling &I macro variable, but simply comparing string I to 1. This also explains why your second loop running because technically speaking string "I" is not equal to "1". You just need to put ampersand in front of I in both %IF statements. I also put two %PUT statements to easier see where code is running. See below:
%macro DSN;
%let i = 1 ;
data new_DSN;
run;
%if &i = 1 %then %do;
%PUT First Loop Run;
data Dummy ;
run;
data DUMMY_ ;
set DUMMY new_DSN ;
run;
%end;
%else %if &i ^= 1 %then %do ;
%PUT Second Loop Run;
data DUMMY_ ;
set DUMMY_ new_DSN ;
run;
%end;
%mend DSN;
%DSN;

SAS Is it possible not to use "TO" in a do loop in MACRO?

I used to use a %do ... %to and it worked fine , but I when I tried to list all character values without %to I got a message ERROR: Expected %TO not found in %DO statement
%macro printDB2 ;
%let thisName = ;
%do &thisName = 'Test1' , 'Test2' , 'Test3' ;
proc print data=&thisName ;
run ;
%end ;
%mend printDB2 ;
I know how to complete this task using %to or %while . But I am curious is it possible to list all character values in the %do ? How can I %do this ?
If your goal here is to loop through a series of character values in some macro logic, one approach you could take is to define corresponding sequentially named macro variables and loop through those, e.g.
%let mvar1 = A;
%let mvar2 = B;
%let mvar3 = C;
%macro example;
%do i = 1 %to 3;
%put mvar&i = &&mvar&i;
%end;
%mend example;
%example;
Alternatively, you could scan a list of values repeatedly and redefine the same macro var multiple times within your loop:
%let list_of_values = A B C;
%macro example2;
%do i = 1 %to 3;
%let mvar = %scan(&list_of_values, &i, %str( ));
%put mvar = &mvar;
%end;
%mend example2;
%example2;
I've explicitly specified that I want to use space as the only list delimiter for scan - otherwise SAS uses lots default delimiters.