BookmarkSubscribeRSS Feed
willow2010
Calcite | Level 5
I have the following piece of SAS code worked and gave me 34 rows of records returned as expected.

DATA IRSLOAD.IRSLOAD(KEEP = IRS_CRN, IRS_REVRSN, IRS_PAY, IRS_REG, IRS_METH, ENV, IRS_PTNRPID, IRS_LINKNO, IRS_PTNRKWD, IRS_LINKRSN, IRS_AREA, RANDOM);
SET IRSLOAD.IRSLOAD;
RETAIN I 1;
IF IRS_PAY='FTB' THEN DO;
IF I LE 34 AND IRS_AREA = ('AS1')
THEN DO;
IRS_METH = 'PHO';
OUTPUT IRSLOAD.IRSLOAD;
I = I + 1;
END;
RETURN;

And then I rewrote it as a macro being called by SAS, I have 5315 rows in return and have no IRS_METH written as 'PHO' and as if the output data is exactly the same as the input. Can anyone give me some advice?

%MACRO SEL(IRS_AREA,NUM);
%LET I = 1;
%IF &I LE NUM AND &IRS_AREA=IRS_AREA
%THEN %DO;
IRS_METH = 'PHO';
OUTPUT IRSLOAD.IRSLOAD;
&I = &I + 1;
%END;
%MEND SEL;

DATA IRSLOAD.IRSLOAD(KEEP = IRS_CRN, IRS_REVRSN, IRS_PAY, IRS_REG, IRS_METH, ENV, IRS_PTNRPID, IRS_LINKNO, IRS_PTNRKWD, IRS_LINKRSN, IRS_AREA, RANDOM);
SET IRSLOAD.IRSLOAD;
IF IRS_PAY='FTB' THEN DO;
%SEL(AS1,34);
END;
RETURN; Thank you cynthia : )


Message was edited by: willow2010
7 REPLIES 7
Cynthia_sas
SAS Super FREQ
Hi:
I am not seeing macro code in the program snippet you posted. It is possible that some special character, interfered with your posting and cut it off.

Be aware that symbols in your code, such as the GT sign > or the LT sign < do present a problem for the forum posting software. Generally, if you post code with either of these characters (such as you show in your TAGATTR value), the post will get truncated because the forum software thinks you are about to use an HTML tag and so it "eats up" the rest of your message looking for a valid HTML tag.

The way around this is to use the entities, < and > for < and > respectively, as described in this previous forum posting:
http://support.sas.com/forums/thread.jspa?messageID=27609毙

There is a difference between a DATA step DO loop and a SAS Macro facility program %DO loop. It is not clear which type of DO loop you mean.

cynthia
ArtC
Rhodochrosite | Level 12
There are a couple of things working against you here. Foremost is your use of the macro macro %IF-%THEN and &I. The macro language is primarily used to generate code. That is not what you intend here. Just because you enclosed a portion of the DATA step within a macro, does not require you to change the contents of the macro to macro language elements.

As an aside (an aside because this will not help this example) the code
[pre]
&i = &i+1;
[/pre]

resolves to
[pre]
1=1+1;
[/pre]

Macro variable arithmetic works differently than DATA step arithmetic:
[pre]
%let i = %eval(&i+1);
[/pre]
Cynthia_sas
SAS Super FREQ
Hi:
Art is right -- if you want to generate a DATA step IF statement, then you do not want to replace it with a Macro %IF statement. In addition, if your original IF statement was:
[pre]
IF I le 34 and IRS_AREA = ('AS1')
[/pre]

...then this is the IF statement you need to generate. Let's say that you have a macro variable to substitute for the 34 (&NUM) and another macro variable to substitute fors the AS1 value (&IRS_AREA). Then you would have something like this:

[pre]
IF I le &NUM and IRS_AREA = ("&IRS_AREA")
[/pre]

In the above IF statement, at compile time, the value specified for &NUM will be substituted into the IF statement as a numeric constant. Then, because IRS_AREA (the dataset variable is character) in your comparison, it MUST be in quotes for the comparison to work (in DATA step syntax), so you will need to surround the macro variable reference for &IRS_AREA with double quotes so the macro variable reference will resolve.

This is a good paper with an overview of how you start with a working program and then turn it into a macro program, including macro logic:
http://www2.sas.com/proceedings/sugi28/056-28.pdf

I am, however, not quite clear on what the purpose of your macro program is.... or why simple macro variables (instead of a macro program) wouldn't work in this instance. I'm guessing you want to be able to change the value for &NUM/&I and the value for &IRS_AREA in order to have a more generic DATA step program. Also, not clear to me is why you are using %LET, which will only resolve at macro compile time.

Perhaps if you read the above paper and then Google for more papers on Macro processing (especially ones written by the most excellent macro guru, Art Carpenter), you will have a better idea of how macro variables and macro programs and macro %IF work and how they are different from a regular DATA step IF statement. For example, consider this simple macro program:
[pre]
** build IF statement that will be resolved at compile time and
** inserted into the DATA step program;

%macro wantclass(want=, get=);
** NUM and SEX are DATASET variables;
** &WANT and &GET are MACRO variables;
if sex = "&want" and num le &get then do;
%if &want = F %then %do;
newvar = 'FFFFF';
%end;
%else %if &want = M %then %do;
newvar = 'MMMMM';
%end;
output;
num = num + 1;
end;

%mend wantclass;

** Test the macro program with F and 3;
data classF;
set sashelp.class;
retain num 1;
if age le 15 then do;
%wantclass(want=F, get=3)
end;
run;

proc print data=classF;
title "1) Data in Work.ClassF";
run;

** Now run for Males and 4;
data classM;
set sashelp.class;
retain num 1;
if age le 15 then do;
%wantclass(want=M, get=4)
end;
run;

proc print data=classM;
title "2) Data in Work.ClassM";
run;
[/pre]

The purpose of the %WANTCLASS macro program is just to generate an IF statement -- in this context, a macro program for a single IF statement is sort of overkill, but consider how the IF statement will be changed by my 2 separate invocations. When I create the CLASSF dataset, I pass the macro program want=F and get=3; then when I create the CLASSM dataset, I pass the macro program want=M and get=4. Note that &WANT and &GET are macro variables that will be resolved at when the macro tokenizer scans and resolves all macro references. On the other hand, SEX and NUM are DATA step variables -- they either come from my dataset (SEX) or I am creating it in my program (NUM) for the purpose of controlling the condition and number of obs for output.

Particularly, study the difference between the "regular" IF statement -- that is responsible for the OUTPUT and the %IF statement that is being used to create a new dataset variable called NEWVAR. The way the %IF statement works is that SAS will resolve the value of &WANT and will write out either NEWVAR='FFFFF'; or NEWVAR='MMMMM'; By the time the compiler gets the code, it will only see the assignment statement because the %IF statement will no longer be in the code.

As I said, I think the design of your program needs some rethinking based on an clear understanding of how macro programs and macro variables work. I always compare the macro process to a big, but invisible, typewriter. Every time the macro processor sees a reference to &WANT, it will type the current value of &WANT into whatever code is being generated. But that's all the macro processor is -- a big, invisible typewriter.

cynthia
willow2010
Calcite | Level 5
Thank you Cynthia and Art C both of your great help, and especial thanks for Cynthia’s detailed writings. Apparently I am quite new to SAS and SAS Macro. Now I have rewritten my code in this way:
%MACRO SEL(IRS_AREA,NUM);
%LET I = 1;
IF I LT &NUM AND IRS_AREA = ("&IRS_AREA")
THEN DO;
IRS_METH = 'PHO';
OUTPUT IRSLOAD.IRSLOAD;
%LET I = %EVAL(&I + 1);
END;
%MEND SEL;
DATA IRSLOAD.IRSLOAD (KEEP=IRS_CRN, IRS_REVRSN, IRS_PAY IRS_REG, IRS_METH, ENV, IRS_PTNRPID, IRS_LINKNO, IRS_PTNRKWD, IRS_LINKRSN, IRS_AREA RANDOM)
;
SET IRSLOAD.IRSLOAD;
IF IRS_PAY='FTB' THEN DO;
%SEL(AS1,34,1);
END;
RETURN;
It gives me 67 rows in return instead of 34. Looks to me the macro syntax is working but somehow the do loop is not working precisely.
I’m not sure whether this piece of information may help: With my original code without the macro, at some stage I tried to change RETAIN I; to I = 1; and put it just below IF IRS_PAY=’FTB’ THEN DO; I also had 67 rows in return. Looks like it’s the same ‘logic’, the same ‘problematic’ logic that I‘ve still not quite figured it out why...
Cynthia_sas
SAS Super FREQ
Hi:
What DO loop??? There is no DO loop in your program. I only see an IF statement with a single DO block. For every observation in the input dataset, the IF statement will execute 1 time. A SAS Data step program executes all the statements in the program for every observation, unless you use conditional logic to determine which conditions should result in which statements being executed.

As Art explained, the %LET is NOT the statement you want to use at this point. %LET will only resolve ONE (1) time -- at compile time. &I is NOT being incremented 34 times. I'm not even sure what you need &I for.

If you carefully examine my program, you will see that I manually increment the DATA step variable NUM -- it is NOT a macro variable. NUM is used as a counter to count the number of observations that have been output. The test in my code:
[pre]
if sex = "&want" and num le &get then do;

RESOLVES to
if sex = "F" and num le 3 then do;

for the invocation for females and then for males, resolves to:
if sex = "M" and num le 4 then do;

[/pre]

Note in my program that I NEVER increment &I and certainly don't use a %LET. My assignment statement is:
[pre]
num = num + 1;
[/pre]

In my code, &GET is the equivalent of your &I -- note that I do NOT increment &GET. Also, in your macro definition for parameters, I only see 2 parameters:
%MACRO SEL(IRS_AREA,NUM); which are IRS_AREA and NUM. Yet, in your invocation, you specify 3 parameters:
%SEL(AS1,34,1);

What is the 3rd positional parameter???? What purpose does it serve???

Perhaps it's time to take a step back and look at your original, working program.
[pre]
DATA IRSLOAD.IRSLOAD(KEEP = IRS_CRN, IRS_REVRSN, IRS_PAY, IRS_REG, IRS_METH, ENV, IRS_PTNRPID,
IRS_LINKNO, IRS_PTNRKWD, IRS_LINKRSN, IRS_AREA, RANDOM);
SET IRSLOAD.IRSLOAD;
RETAIN I 1;
IF IRS_PAY='FTB' THEN DO;
IF I LE 34 AND IRS_AREA = ("AS1") THEN DO;
IRS_METH = 'PHO';
OUTPUT IRSLOAD.IRSLOAD;
I = I + 1;
END;
END;
RUN;
[/pre]

Why did you take your working program and replace the I=I+1; statement with a %LET statement??? There is no DO loop here. You are just incrementing I for every observation you output. You could just as easily have coded:
[pre]
%let irs_area = AS1;
%let NUM = 34;

DATA IRSLOAD.IRSLOAD(KEEP = IRS_CRN, IRS_REVRSN, IRS_PAY, IRS_REG, IRS_METH, ENV, IRS_PTNRPID,
IRS_LINKNO, IRS_PTNRKWD, IRS_LINKRSN, IRS_AREA, RANDOM);
SET IRSLOAD.IRSLOAD;
RETAIN I 1;
IF IRS_PAY='FTB' THEN DO;
IF I LE &NUM AND IRS_AREA = ("&IRS_AREA") THEN DO;
IRS_METH = 'PHO';
OUTPUT IRSLOAD.IRSLOAD;
I = I + 1;
END;
END;
RUN;
[/pre]

Can you describe the reason why you thought you needed to use &I and a %LET statement?? An example of a DATA step DO loop is:
[pre]
DO I = 1 to 34 by 1;
...some statement or statements that you want to execute 34 times...
END;

or a macro %DO loop:
%DO I = 1 %to 34;
... what the macro facility should write 34 times in the code...
%END;
[/pre]

I think that before you go much farther, you need to understand the difference between a macro program and a regular DATA step program. I'm still trying to figure our what purpose the macro program serves in this program -- unless you plan to "resuse" this IF statement in multiple programs.

cynthia
willow2010
Calcite | Level 5
Hi, Cyntha
Sorry for my misleading and non-precise language. Somehow I managed to get my code worked finally.
%MACRO SEL(IRS_AREA,NUM,I);
IF IRS_AREA = ("&IRS_AREA")
THEN DO;
IF &I LE &NUM
THEN DO;
IRS_METH = 'PHO';
OUTPUT IRSLOAD.IRSLOAD;
&I = &I + 1;
END;
END;
%MEND SEL;

DATA IRSLOAD.IRSLOAD (KEEP=IRS_CRN, IRS_REVRSN, IRS_PAY, IRS_REG, IRS_METH, ENV, IRS_PTNRPID, IRS_LINKNO, IRS_PTNRKWD, IRS_LINKRSN, IRS_AREA RANDOM)
;
SET IRSLOAD.IRSLOAD;
RETAIN I 1;
IF IRS_PAY='FTB' THEN DO;
%SEL(AS1,34,I);
End;
Return;

The number of rows returned is now 34. And with my last reply, the return row number is 67, after a careful study, this is a number of all IRS_PAY = ‘FTB’ and IRS_AREA in (‘AS1’) cases as if the ‘I LT &NUM’ thing can’t really do the ‘filter’ as what I expected and it worked well now in this nested if-then structure.

I did have some consideration on this
if sex = "F" and num le 3 then do;
...
else
if sex = "M" and num le 4 then do;

structure, but it wasn’t exactly what I wanted. For in my previous step which was not included in my original question, I have randomised all records in this category: IRS_PAY = ‘FTB’ and IRS_AREA in (‘AS1’), they all have IRS_METH defaulted as something else, now what I wanted is to take the first 34 records out of this group and have IRS_METH assigned as ‘PHO’.

Nevertheless, there is a great lot I can learn from your reply. I shall take some good read in a near future.

Cheers!
Cynthia_sas
SAS Super FREQ
Hi:
I'm glad you got it working. But the only reason it's working is that &I resolves to the character string I and so the statement:
[pre]
&I = &I + 1;
[/pre]

will be resolved as:
[pre]
I = I + 1;
[/pre]

and there is really NO need, from what I see for you to use &I at all. You could just have easily written
[pre]
I = I + 1;
[/pre]

in the code and the program would have worked the same way.

cynthia

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 7 replies
  • 2530 views
  • 1 like
  • 3 in conversation