Hi, In the process of macro building , I'm stuck at a point.
I have a dataset out_surv where var has values such as
Var
0
1
3
;
I have created "&start" and "&end" macro variable from out_Surv. So, they have 0 and 3 subsequently.
In addition to this, I've 3 other globally defined macro vars already stored for the session named &S0, &S1, &S3.
I have to work in a dataset(named Surv which has an existing column Sep) where, by a help of a loop I have to iterate so that when Sep=0 then my newly created variable named total=&S0. similarly, when Sep=1 then total=&S1 and Sep=3 then total=&S3
Surv data set looks like
Sep
0
0
0
0
1
1
1
1
3
3
3
3
I have tried this below:
proc sql;
select min(var),max(var) into :min_var1, :max_var1
from out_surv;
quit;
%do i=&min_var1 %to &max_var1
data new_dataset;
set Surv;
%if Sep=&min_sepstrata. then total=&v0.;
i+1; ******this is the step where i need help*******************
run;
Kindly let me know if someone can help me with the iteration.
From your description, you do not need any macro coding at all (aside from the use of the predefined macro variables):
%let s0=111;
%let s1=222;
%let s3=333;
data surv;
input Sep;
datalines;
0
0
0
0
1
1
1
1
3
3
3
3
;
data want;
set surv;
total = symget(cats('s',sep));
run;
It looks like it would be best for you to forget (for the time being) that macros exist. This will force you to concentrate on the data/proc step code that really does the job. See Maxim 11.
You don't seem to have provided all of the details and/or explained them clearly. What are the undefined macro variables your code is referencing? What do they have to do with the issue? You talk about min and max values and use a %DO loop from the min to the max but your example data does not have every value between the min and the max (there is no 2). Does that matter? Will that cause an issue?
First step is to figure out what SAS code you want to generate.
Then you can think about how to use macro code to generate it.
It sounds like SEP is an existing variable and you want to create TOTAL?
You could just use a series of IF statements:
if sep=0 then total=&s0;
if sep=1 then total=&s1;
....
That would be easy using your current %DO loop:
%do index=&min_var1 %to &max_var1 ;
if sep=&index then total=&&s&index;
%end;
Main point is to not put the values into macro variables to begin with. Leave them in a dataset and then just join the two.
You could just eliminate the macro code and resolve the macro variable directly in SAS code.
total=symgetn(cats('s',sep));
If you want to put them into macro variables (and the list is short enough) then put them into one macro variable. Or in your case two macro variables, one with the counts and one with the category id value.
proc sql noprint;
select category,counts
into :categories separated by '|'
, :cat_counts separated by '|'
from category_counts
;
%let n_categories=&sqlobs;
quit;
Now you can loop from 1 to &N_CATEGORIES.
%do index=1 %to &n_categories;
if sep=%scan(&categories,&index,|) then total=%scan(&cat_counts,&index,|);
%end;
proc sql;
select min(var),max(var) into :min_var1, :max_var1
from out_surv;
quit;
data new_dataset;
set Surv;
%do i=&min_var1 %to &max_var1;
if Sep=&i. then total=&&s&i..;
%end;
run;
Is the above what you are looking for?
From your description, you do not need any macro coding at all (aside from the use of the predefined macro variables):
%let s0=111;
%let s1=222;
%let s3=333;
data surv;
input Sep;
datalines;
0
0
0
0
1
1
1
1
3
3
3
3
;
data want;
set surv;
total = symget(cats('s',sep));
run;
It looks like it would be best for you to forget (for the time being) that macros exist. This will force you to concentrate on the data/proc step code that really does the job. See Maxim 11.
I guess you have those values for your macro variables somewhere in a dataset; if yes, creating a format from that dataset is even better in terms of code readability and performance:
data source;
input var value;
datalines;
0 111
1 222
3 333
;
data cntlin;
set source end=done;
fmtname = "lookup";
type = 'i';
rename
var=start
value=label
;
output;
if done
then do;
hlo = 'O';
value = .;
output;
end;
run;
proc format cntlin=cntlin;
run;
data surv;
input Sep;
datalines;
0
0
0
0
1
1
1
1
2
3
3
3
3
;
data want;
set surv;
total = input(sep,lookup.);
run;
The INPUT() function will create a numeric result; if you want your result to be character, create a numeric format (type 'N') and use PUT().
The most advanced (and most performant) solution for a lookup is the use of hash objects:
data want;
set surv;
if _n_ = 1
then do;
length var total 8;
declare hash l (dataset:'source (rename=(value=total))');
l.definekey('var');
l.definedata('total');
l.definedone();
end;
if l.find(key:sep) ne 0 then total = .;
run;
Bottom line: once you have created your solution and find that it has repeating code elements, or you want to make it dynamic with the use of parameters so you can apply it effortlessly to different situations, THEN you start to think about macros. NOT before that.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.