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?
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.
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;
@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;
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;
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;
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
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.
Ready to level-up your skills? Choose your own adventure.