I have a pretty simple macro code:
%macro do_array(var=, num=);
%do var_num = 1 %to #
%if &var&var_num = 0 %then %do;
perc_&var&var_num = 0;
%end;
%else %do;
perc_&var&var_num = &var&var_num / total;
%end;
%end;
%mend;
Basically for all of the variables I'm just trying to get a percentage of the total. When I run it, I get an warning that I'm dividing by zero, but that's what the first step in the if statement is trying to avoid. Along with the warning, instead of a 0 for the value, I'm getting a missing value.
Any suggestions?
Thanks
Hi:
I agree that the TOTAL variable should be checked to avoid the divide by 0 error message, but depending on how you are using your variables, I believe that this %IF statement possibly needs to change to a regular DATA step IF. For example, if VAR=X and Num=1, then your %IF test would seem to be asking this of the macro processor, as if the macro processor has visibility of the DATA step variable X1 (which it doesn't):
%if xi = 0 %then %do;
perc_x1= 0;
%end;
which may not be what you think is going to happen. The %IF statement gets evaluated when the SAS Macro program is executed, but the SAS Macro facility is only generating code that will be executed. So, by the time the code goes to be executed against your data, there are no macro statements left in the code. So generally, Macro %IF statements are NOT used to test data step variables. The value of your data step variables will change with every observation. At the point in time when the macro code is executing, and generating code, no data has actually been read yet. So if you want to test whether X1 is 0, then you need a "regular" DATA step IF.
For example, if you have simple data like this:
grp x1 x2 x3 x4 x5
AAA 5 10 15 . 25
BBB 3 . 11 13 17
CCC 4 5 . 9 11
and you want to calculate new variables pctx1, pctx2, pctx3, pctx4 and pctx5, you can do it without any macro processing. See the attached screen shot.
Of course, your program could be more complicated, but I wonder why you are mixing macro programming statements with DATA step variables.
cynthia
It's the TOTAL variable you need to check for divide by 0.
Sorry about that, I originally had total, but I changed it to the variable to see if it would make a difference. Forgot to change it back before copying it here. Anyway, thank you all so much for your responses.
Hi:
I agree that the TOTAL variable should be checked to avoid the divide by 0 error message, but depending on how you are using your variables, I believe that this %IF statement possibly needs to change to a regular DATA step IF. For example, if VAR=X and Num=1, then your %IF test would seem to be asking this of the macro processor, as if the macro processor has visibility of the DATA step variable X1 (which it doesn't):
%if xi = 0 %then %do;
perc_x1= 0;
%end;
which may not be what you think is going to happen. The %IF statement gets evaluated when the SAS Macro program is executed, but the SAS Macro facility is only generating code that will be executed. So, by the time the code goes to be executed against your data, there are no macro statements left in the code. So generally, Macro %IF statements are NOT used to test data step variables. The value of your data step variables will change with every observation. At the point in time when the macro code is executing, and generating code, no data has actually been read yet. So if you want to test whether X1 is 0, then you need a "regular" DATA step IF.
For example, if you have simple data like this:
grp x1 x2 x3 x4 x5
AAA 5 10 15 . 25
BBB 3 . 11 13 17
CCC 4 5 . 9 11
and you want to calculate new variables pctx1, pctx2, pctx3, pctx4 and pctx5, you can do it without any macro processing. See the attached screen shot.
Of course, your program could be more complicated, but I wonder why you are mixing macro programming statements with DATA step variables.
cynthia
You're misunderstanding what the macro processor does, and when it does it.
If we feed var=xy and num=3 to your macro, it will do the following:
&var&var_num will evaluate to
xy1
xy2
xy3
respectively
All these are clearly not 0, so therefore the macro processor will supply the lines
perc_xy1 = xy1 / total;
perc_xy2 = xy2 / total;
perc_xy3 = xy3 / total;
to the SAS interpreter. Anytime total equates to zero, you get a warning.
The macro processor is invoked BEFORE any data step runs, and does its work without any knowledge about what happens during data step execution. It is only there to generate program text. Repeat the last sentence three times.
You most probably want this in your %do loop:
if total ne 0
then perc_&var&var_num = &var&var_num / total;
else perc_&var&var_num = 0;
This piece of code is then repeatedly supplied vor every var_num to the data step code.
Just to add, I think what you are trying to achieve is to code your own array processing. You can do this, per example 2 below, however the use of arrays would be a better idea - example 1:
Example 1:
data have;
array tot{4} 8.;
array perc{4} 8.;
tot{1}=6;
tot{2}=10;
tot{3}=8;
tot{4}=7;
do i=1 to 4;
if tot{i}=0 then perc{i}=0;
else perc{i}=tot{i} / 45;
end;
run;
Example 2:
data have;
array tot{4} 8.;
array perc{4} 8.;
tot{1}=6;
tot{2}=10;
tot{3}=8;
tot{4}=7;
run;
%macro Do_Array (Var=, Num=);
%do var_num=1 %to &Num.;
%if &var.&var_num.=0 %then %do;
perc&var_num.=0;
%end;
%else %do;
perc&var_num.=&var.&var_num. / 45;
%end;
%end;
%mend Do_Array;
data want;
set have;
%do_Array (Var=tot,Num=4);
run;
The DIVIDE function returns missing I when you divide by 0. No message.
23 data _null_;
24 x = divide(1,0);
25 put x=;
26 run;
x=I
27
28 %put NOTE: %sysfunc(divide(1,0));
NOTE: I
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.