Hello,
I am storing the my population cohort number by treatment in global macro variables.: &n1. -&n5.
proc sql noprint;
select count(distinct subjid) into :n1-:n5 from pop group by trt;
quit;
%put &n1 &n2 &n3 &n4 &n5 ;
I want to calculate the percentage of the variables of interest based on the treatment group.
mx1 mx2 mx3 mx4 mcat1 mcat2
trt1 percent based on &n1
trt2 percent based on &n2
trt3 percent based on &n3
trt4 percent based on &n4
trt5 percent based on &n5
In a data step I am trying to use a do loop to call in the denominator based on the iteration value.
data final(keep= c1-c6);
length c1-c6 $20;
set final0 ;
do i=1 to 5;
if mx1 not in (.,0) then c1= strip(put(mx1 ,5.)) ||" ("||strip(put(mx1 /&&n&i.*100,5.1))||")"; else c1="0";
if mx2 not in (.,0) then c2= strip(put(mx2 ,5.)) ||" ("||strip(put(mx2 /&&n&i.*100,5.1))||")"; else c2="0";
if mx3 not in (.,0) then c3= strip(put(mx3 ,5.)) ||" ("||strip(put(mx3 /&&n&i.*100,5.1))||")"; else c3="0";
if mx4 not in (.,0) then c4= strip(put(mx4 ,5.)) ||" ("||strip(put(mx4 /&&n&i.*100,5.1))||")"; else c4="0";
if mcat1 not in (.,0) then c5= strip(put(mcat1,5.))||" ("||strip(put(mcat1/&&n&i.*100,5.1))||")"; else c5="0";
if mcat2 not in (.,0) then c6= strip(put(mcat2,5.))||" ("||strip(put(mcat2/&&n&i.*100,5.1))||")"; else c6="0";
end;
run;
The data step is not working because of this &&n&i.
What am I missing?
thx
There are two different issues related to macro processing in this bit of code:
data final(keep= c4-c9); length c4-c9 $20; set final2 ; do i = 1 to 6; if mx1 not in (.,0) then c4= strip(put(mx1 ,5.)) ||" ("||strip(put(mx1 / &&n&i*100,5.1))||")"; else c4="0"; if mx2 not in (.,0) then c5= strip(put(mx2 ,5.)) ||" ("||strip(put(mx2 / &&n&i*100,5.1))||")"; else c5="0"; if mx3 not in (.,0) then c6= strip(put(mx3 ,5.)) ||" ("||strip(put(mx3 / &&n&i*100,5.1))||")"; else c6="0"; if mx4 not in (.,0) then c7= strip(put(mx4 ,5.)) ||" ("||strip(put(mx4 / &&n&i*100,5.1))||")"; else c7="0"; if mcat1 not in (.,0) then c8= strip(put(mcat1,5.))||" ("||strip(put(mcat1/&&n&i*100,5.1))||")"; else c8="0"; if mcat2 not in (.,0) then c9= strip(put(mcat2,5.))||" ("||strip(put(mcat2/&&n&i*100,5.1))||")"; else c9="0"; end;
The first one is that MACRO values resolve and generate code before the data step executes.
Second is the MACRO processor does not ever see data step values as you use with "Do i =" . i is a data step variable. &&n&i uses a macro variable i. Not the same thing at all.
If your SUBJID do not have duplicate records you making a lot of extra work attempting this.
Create a data set with Trt01an and MERGE (or join) that data set on TRT01AN to get the counts onto the records.
Then use what ever count variable you created instead of &&n&i
I have no clue what all that put and concatenate stuff is for. You may want to look at the CATS function so you don't need all those || and Strip() calls. the CATS function strips values before concatenation.
The data step is not working...
Please, in this case and in all future cases, if your code is not working, show us the ENTIRE log for this data step. When you say "not working..." and give no further explanation, we can't help. We have to ask for the log. So from now on, show us the log (the ENTIRE log for this data step) without us asking.
I get the feeling that there's no need for macro variables and no need for do loops either — I think you are working way too hard to get this information — but until I have a better explanation of what you are trying to do, and example data, I'll just leave it there.
@Kc2 wrote:
Hello,
I am storing the my population cohort number by treatment in global macro variables.: &n1. -&n5.
By "population cohort number" do you mean an "identification number" or the count of members in the cohort?
If your data has the cohort identifier in it this extremely like not needed at all.
When you discuss percentage there are always 2 things that you need to specify a numerator and denominator.
You don't even mention what mx1 etc might be or represent.
And a final bit, do you need a report, people read these, or a data set for input to another procedure (again very likely not need such summary)?
Here is an example of a report that I think is similar to what you are describing with "age" in the role of your TRT values, and levels of Sex as the MX. You can run this because you should have the SASHELP.Class data set, which is not very large and you can look at it.
proc tabulate data=sashelp.class; class sex age; tables age, (sex all='All sexes')*(n colpctn rowpctn pctn) ; run;
This caluclates percentages with several different denominators, the ROWPCTN uses the total count on the row, the COLPCTN uses the total count for the column and the PCTN uses the total count for all the records.
If by trt1 trt2 trt3 ... and mx1 mx2 mx3 ... you mean multiple variables it may be that your data is structured incorrectly and you should have single variable TRT that has 5 levels and MX that has 4 and Mcat with 2 levels.
OR Provide example data in the form of a data step.
Hello,
By population cohort I meant the denominator by treatment group
21461 proc sql noprint;
21462 select count(distinct subjid) into :n1-:n6 from ds2 group by trt01an;
21463 quit;
NOTE: PROCEDURE SQL used (Total process time):
real time 0.01 seconds
cpu time 0.01 seconds
21464 %put &n1 &n2 &n3 &n4 &n5 &n6 ;
1 1 3 9 4 3
for this data step the variables need to be divided by the denominator of that particular group.
for treatment group 1 the denominator =1 . For treatment group 3 the denominator =3.
For treatment group 4 the denominator =9 etc...
I am not sure why i is getting resolved as i and not the actual number of the iteration.
data final(keep= c4-c9);
length c4-c9 $20;
set final2 ;
do i = 1 to 6;
if mx1 not in (.,0) then c4= strip(put(mx1 ,5.)) ||" ("||strip(put(mx1 / &&n&i*100,5.1))||")"; else c4="0";
if mx2 not in (.,0) then c5= strip(put(mx2 ,5.)) ||" ("||strip(put(mx2 / &&n&i*100,5.1))||")"; else c5="0";
if mx3 not in (.,0) then c6= strip(put(mx3 ,5.)) ||" ("||strip(put(mx3 / &&n&i*100,5.1))||")"; else c6="0";
if mx4 not in (.,0) then c7= strip(put(mx4 ,5.)) ||" ("||strip(put(mx4 / &&n&i*100,5.1))||")"; else c7="0";
if mcat1 not in (.,0) then c8= strip(put(mcat1,5.))||" ("||strip(put(mcat1/&&n&i*100,5.1))||")"; else c8="0";
if mcat2 not in (.,0) then c9= strip(put(mcat2,5.))||" ("||strip(put(mcat2/&&n&i*100,5.1))||")"; else c9="0";
end;
run;
21475
21476 data final(keep= c4-c9);
21477 length c4-c9 $20;
21478 set final2 ;
21479
21480 do i = 1 to 6;
21481
21482 if mx1 not in (.,0) then c4= strip(put(mx1 ,5.)) ||" ("||strip(put(mx1 / &&n&i*100,5.1))||")"; else c4="0";
NOTE: Line generated by the macro variable "I".
1 &ni
-
22
WARNING: Apparent symbolic reference NI not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, INPUT, PUT.
21483 if mx2 not in (.,0) then c5= strip(put(mx2 ,5.)) ||" ("||strip(put(mx2 / &&n&i*100,5.1))||")"; else c5="0";
NOTE: Line generated by the macro variable "I".
1 &ni
-
22
WARNING: Apparent symbolic reference NI not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, INPUT, PUT.
21484 if mx3 not in (.,0) then c6= strip(put(mx3 ,5.)) ||" ("||strip(put(mx3 / &&n&i*100,5.1))||")"; else c6="0";
NOTE: Line generated by the macro variable "I".
1 &ni
-
22
WARNING: Apparent symbolic reference NI not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, INPUT, PUT.
21485 if mx4 not in (.,0) then c7= strip(put(mx4 ,5.)) ||" ("||strip(put(mx4 / &&n&i*100,5.1))||")"; else c7="0";
NOTE: Line generated by the macro variable "I".
1 &ni
-
22
WARNING: Apparent symbolic reference NI not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, INPUT, PUT.
21486 if mcat1 not in (.,0) then c8= strip(put(mcat1,5.))||" ("||strip(put(mcat1/&&n&i*100,5.1))||")"; else c8="0";
NOTE: Line generated by the macro variable "I".
1 /&ni
-
22
WARNING: Apparent symbolic reference NI not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, INPUT, PUT.
21487 if mcat2 not in (.,0) then c9= strip(put(mcat2,5.))||" ("||strip(put(mcat2/&&n&i*100,5.1))||")"; else c9="0";
NOTE: Line generated by the macro variable "I".
1 /&ni
-
22
WARNING: Apparent symbolic reference NI not resolved.
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, INPUT, PUT.
21488
21489 end;
21490
21491
21492 run;
There are two different issues related to macro processing in this bit of code:
data final(keep= c4-c9); length c4-c9 $20; set final2 ; do i = 1 to 6; if mx1 not in (.,0) then c4= strip(put(mx1 ,5.)) ||" ("||strip(put(mx1 / &&n&i*100,5.1))||")"; else c4="0"; if mx2 not in (.,0) then c5= strip(put(mx2 ,5.)) ||" ("||strip(put(mx2 / &&n&i*100,5.1))||")"; else c5="0"; if mx3 not in (.,0) then c6= strip(put(mx3 ,5.)) ||" ("||strip(put(mx3 / &&n&i*100,5.1))||")"; else c6="0"; if mx4 not in (.,0) then c7= strip(put(mx4 ,5.)) ||" ("||strip(put(mx4 / &&n&i*100,5.1))||")"; else c7="0"; if mcat1 not in (.,0) then c8= strip(put(mcat1,5.))||" ("||strip(put(mcat1/&&n&i*100,5.1))||")"; else c8="0"; if mcat2 not in (.,0) then c9= strip(put(mcat2,5.))||" ("||strip(put(mcat2/&&n&i*100,5.1))||")"; else c9="0"; end;
The first one is that MACRO values resolve and generate code before the data step executes.
Second is the MACRO processor does not ever see data step values as you use with "Do i =" . i is a data step variable. &&n&i uses a macro variable i. Not the same thing at all.
If your SUBJID do not have duplicate records you making a lot of extra work attempting this.
Create a data set with Trt01an and MERGE (or join) that data set on TRT01AN to get the counts onto the records.
Then use what ever count variable you created instead of &&n&i
I have no clue what all that put and concatenate stuff is for. You may want to look at the CATS function so you don't need all those || and Strip() calls. the CATS function strips values before concatenation.
If you don't want to take the time to fix your approach to be more rational and efficient you could just replace the attempts to mix data step variables and macro variable with a call to the SYMGETN() function using a macro variable name generated by the CATS() function.
symgetn(cats('n',i))
For example:
data final(keep= c4-c9);
length c4-c9 $20;
set final2 ;
array c $20 c4-c9;
array mx mx1-mx4 mcat1 mcat2 ;
do i = 1 to 6;
if mx[i] then c[i] = cat(catx(' (',mx[i],put(100*mx[i]/symgetn(cats('n',i)),5.1)),')');
else c[i]='0';
end;
run;
Let's make up some test data:
%let n1=1;
%let n2=1;
%let n3=3;
%let n4=9;
%let n5=4;
%let n6=3;
data final2;
input mx1-mx4 mcat1 mcat2;
cards;
0 1 2 3 4 .
;
Results:
1140 1141 data _null_; 1142 set final; 1143 put (c:) (=/); 1144 run; c4=0 c5=1 (100.0) c6=2 (66.7) c7=3 (33.3) c8=4 (100.0) c9=0
Thanks for providing the log. Others have explained why your code doesn't work.
Now, re-design the approach so that macro variables are not needed and loops are not needed.
for this data step the variables need to be divided by the denominator of that particular group.
Try PROC FREQ or PROC TABULATE or PROC REPORT, this will be much simpler than anything you have shown so far.
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.