BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
BN_RN17
Calcite | Level 5

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

1 ACCEPTED SOLUTION

Accepted Solutions
Cynthia_sas
SAS Super FREQ

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


percent_in_array_no_macro.png

View solution in original post

6 REPLIES 6
SASKiwi
PROC Star

It's the TOTAL variable you need to check for divide by 0.

BN_RN17
Calcite | Level 5

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.

Cynthia_sas
SAS Super FREQ

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


percent_in_array_no_macro.png
Kurt_Bremser
Super User

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.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

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;

data_null__
Jade | Level 19

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

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
  • 6 replies
  • 2913 views
  • 7 likes
  • 6 in conversation