BookmarkSubscribeRSS Feed
BenBrady
Obsidian | Level 7

I want to write a macro for the following piece of code:

 


data nd;
set nd;
if in2016=1 then do;
    if road = 98 then air_a = 98; /*non*/
    else if road = 2 then air_a = 0;
end;

if road = 1 then do;
    if car_a in (10, 1) then bike_res_a = 0; /*withdrew or completed*/
end;

if car_a in (2,4) then do; * currently or deferred;
    if bike_a = 1 then bike_res_a = 0; *year12;
    else if bike_a in (2,3,4,5,6) then bike_res_a = bike_a - 1;
    else if bike_a in (7,8,9,10) then bike_res_a = 6;
    else if bike_a  =  11  then bike_res_a = 7;
    else if bike_a  in (12 13) then bike_res_a = 8;
    else if bike_a  =  14 then bike_res_a = 9;
    else if bike_a  >= 15 then bike_res_a = 0;
end;
      
run;

 

I want this to run through three times. One where there is _a, one where there is _b and one where there is _c. So I want to run through a,b,c. Just wandering how to do this in a macro?

5 REPLIES 5
Reeza
Super User

You should use arrays not a macro here. 

 

https://stats.idre.ucla.edu/sas/seminars/sas-arrays/

 

Additionally you can consider using a format instead of IF/THEN statements. 

This paper has some good examples. 

 

http://www2.sas.com/proceedings/sugi30/001-30.pdf

Kurt_Bremser
Super User

Use array processing, as @Reeza already suggested.

And replace the if-then-else-if chain with a select block:

data nd;
set nd;
array bikes {3} bike_a bike_b bike_c;
array bikeres {3} bike_res_a bike_res_b bike_res_c;
if in2016=1 then do;
    if road = 98 then air_a = 98; /*non*/
    else if road = 2 then air_a = 0;
end;

if road = 1 then do;
    if car_a in (10, 1) then bike_res_a = 0; /*withdrew or completed*/
end;

if car_a in (2,4) then do; * currently or deferred;
  do i = 1 to 3;
    select (bikes{i});
      when (1) bike_res{i} = 0; *year12;
      when (2,3,4,5,6) bike_res{i} = bikes{i} - 1;
      when (7,8,9,10) bike_res{i} = 6;
      when (11) bike_res{i} = 7;
      when (12,13) bike_res{i} = 8;
      when (14) bike_res{i} = 9;
      otherwise bike_res{i} = 0;
    end; * select;
  end; * iterative do;
end;
drop i;
run;
Shmuel
Garnet | Level 18

@Kurt_Bremser, your code doesn't replace all   _a   variables as asked.   See lines:

 

/*-------------------------------------------------------------------------------------------------/

if in2016=1 then do;

   if road = 98 then air_a = 98; /*non*/

   else if road = 2 then air_a = 0; end;

if road = 1 then do;

   if car_a in (10, 1) then bike_res_a = 0; /*withdrew or completed*/

end;

if car_a in (2,4) then do;

/*-------------------------------------------------------------------------------------------------/

 

 

Therefore, I think it is easier to be done by a macro.

I too preffer the CASE instead IF-THEN-ELSE method.

So proposed code is:

 

%macro doit(suffix);
	if in2016=1 then do;
		if road = 98 then air&suffix. = 98; /*non*/
		else if road = 2 then air&suffix. = 0;
	end;

	if road = 1 then do;
		if car&suffix. in (10, 1) then bike_res&suffix. = 0; /*withdrew or completed*/
	end;

	if car&suffix. in (2,4) then do; * currently or deferred;
	   select (bikes&suffix.);
		  when (1) bike_res&suffix. = 0; *year12;
		  when (2,3,4,5,6) bike_res&suffix. = bikes&suffix. - 1;
		  when (7,8,9,10) bike_res&suffix. = 6;
		  when (11) bike_res&suffix. = 7;
		  when (12,13) bike_res&suffix. = 8;
		  when (14) bike_res&suffix. = 9;
		  otherwise bike_res&suffix. = 0;
	   end; * select;
	end;
%mend doit;

data nd;
set nd;
    %doit(_a);     
    %doit(_b);     
    %doit(_c); 
run;    
Kurt_Bremser
Super User

Yes, then the do loop needs to be moved out one level to cover all variables:

data nd;
set nd;
array airs {3} air_a air_b air_c;
array cars {3} car_a car_b car_c;
array bikes {3} bike_a bike_b bike_c;
array bikeres {3} bike_res_a bike_res_b bike_res_c;
do i = 1 to 3;
  in2016 = 1 then do;
    if road = 98 then airs{i} = 98; /*non*/
    else if road = 2 then airs{i} = 0;
  end;
  if road = 1 then do;
    if cars{i} in (10,1) then bikeres{i} = 0; /*withdrew or completed*/
  end;
  if car_a in (2,4) then do; * currently or deferred;
    select (bikes{i});
      when (1) bike_res{i} = 0; *year12;
      when (2,3,4,5,6) bike_res{i} = bikes{i} - 1;
      when (7,8,9,10) bike_res{i} = 6;
      when (11) bike_res{i} = 7;
      when (12,13) bike_res{i} = 8;
      when (14) bike_res{i} = 9;
      otherwise bike_res{i} = 0;
    end; * select;
  end;
end; * iterative do;
drop i;
run;
Tom
Super User Tom
Super User

Just use arrays.  Take your code and replace _a with (i) and add the array statements and a DO loop.

data nd;
  set nd;
  array air air_a air_b air_c ;
  array car car_a car_b car_c ;
  array bike bike_a bike_b bike_c ;
  array bike_res bike_res_a bike_res_b bike_res_c;
  do i=1 to dim(air);
    if in2016=1 then do;
      if road = 98 then air(i) = 98; /*non*/
      else if road = 2 then air(i) = 0;
    end;

    if road = 1 then do;
      if car(i) in (10, 1) then bike_res(i) = 0; /*withdrew or completed*/
    end;

    if car(i) in (2,4) then do; * currently or deferred;
      if bike(i) = 1 then bike_res(i) = 0; *year12;
      else if bike(i) in (2,3,4,5,6) then bike_res(i) = bike(i) - 1;
      else if bike(i) in (7,8,9,10) then bike_res(i) = 6;
      else if bike(i)  =  11  then bike_res(i) = 7;
      else if bike(i)  in (12 13) then bike_res(i) = 8;
      else if bike(i)  =  14 then bike_res(i) = 9;
      else if bike(i)  >= 15 then bike_res(i) = 0;
    end;
  end;      
run;
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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 5 replies
  • 1547 views
  • 0 likes
  • 5 in conversation