My problem has two questions:
1. Can a macro be called in a data step?
2. If so, how can I loop through in a single data step that will add a new line to the data table and use a value in the table as an input parameter to that macro?
Here is a general example of what I want to do:
ITERATION = the number of times I have gone through the loop
COMPARE = the variable used to compare with RESULT
MINVAL = the minimum possible value of VALUE; set at 0 on first iteration. On all other iterations, if RESULT[i-1] < COMPARE[i-1], then MINVAL = MINVAL[i-1]; else MINVAL = VALUE[i-1]
MAXVAL = the maximum possible value of VALUE; set at 1 on first iteration. On all other iterations, if RESULT[i-1] > COMPARE[i-1], then MAXVAL = MAXVAL[i-1]; else MAXVAL = VALUE[i-1]
VALUE = the midpoint between MINVAL and MAXVAL (derived as: MINVAL + (MAXVAL-MINVAL) / 2)
RESULT = The returned value from a macro call using VALUE as an input parameter
ITERATION | COMPARE | MINVAL | MAXVAL | VALUE | RESULT |
---|---|---|---|---|---|
1 | 0.90000 | 0 | 1 | 0.5 | 0.00088 |
2 | 0.90000 | 0 | 0.5 | 0.25 | 0.46054 |
3 | 0.90000 | 0 | 0.25 | 0.125 | 0.93546 |
4 | 0.90000 | 0.125 | 0.25 | 0.1875 | 0.75793 |
5 | 0.90000 | 0.125 | 0.1875 | 0.15625 | 0.86567 |
6 | 0.90000 | 0.125 | 0.15625 | 0.140625 | 0.90512 |
7 | 0.90000 | 0.140625 | 0.15625 | 0.1484375 | 0.88658 |
8 | 0.90000 | 0.140625 | 0.1484375 | 0.14453125 | 0.89614 |
9 | 0.90000 | 0.140625 | 0.14453125 | 0.142578125 | 0.90070 |
10 | 0.90000 | 0.142578125 | 0.14453125 | 0.143554688 | 0.89844 |
11 | 0.90000 | 0.142578125 | 0.143554688 | 0.143066406 | 0.89958 |
12 | 0.90000 | 0.142578125 | 0.143066406 | 0.142822266 | 0.90014 |
13 | 0.90000 | 0.142822266 | 0.143066406 | 0.142944336 | 0.89986 |
14 | 0.90000 | 0.142822266 | 0.142944336 | 0.142883301 | 0.90000 |
In the end, I just need the last value stored in VALUE. I don't need this data table, although it would be nice to retain to make sure that it is doing what I want it to do.
Here is my code so far. It will give me the first line, but I am at a loss as to where to go from here. I am not sure how to implement the DO loop with what I am trying to do.
/*** Find the power ***/
%macro kappa_power(rate1=,rate2=,kappa=,kappa0=,n=,alpha=0.05,sided=2);
data kappa;
p1_=&rate1.;
p_1=&rate2.;
p2_=1-p1_;
p_2=1-p_1;
pe=p1_*p_1+p2_*p_2;
p0=&kappa.*(1-pe)+pe;
p22=(p0-p1_+p_2)/2;
p11=p0-p22;
p12=p1_-p11;
p21=p_1-p11;
A=(p11*((1-pe)-(p1_+p_1)*(1-p0))**2)+(p22*((1-pe)-(p2_+p_2)*(1-p0))**2);
B=(1-p0)**2*(p12*(p_1+p2_)**2+p21*(p_2+p1_)**2);
C=(p0*pe-2*pe+p0)**2;
Q=(1-pe)**-4*(A+B-C);
output;
call symputx("Q1",Q);
run;
data kappa0;
set kappa (keep=p1_ p_1 p2_ p_2 pe);
p0=&kappa0.*(1-pe)+pe;
p22=(p0-p1_+p_2)/2;
p11=p0-p22;
p12=p1_-p11;
p21=p_1-p11;
A=(p11*((1-pe)-(p1_+p_1)*(1-p0))**2)+(p22*((1-pe)-(p2_+p_2)*(1-p0))**2);
B=(1-p0)**2*(p12*(p_1+p2_)**2+p21*(p_2+p1_)**2);
C=(p0*pe-2*pe+p0)**2;
Q=(1-pe)**-4*(A+B-C);
output;
call symputx("Q0",Q);
run;
%global power;
data _null_;
Zb=(((&kappa.-&kappa0.)*sqrt(&n.)) - (quantile('NORMAL',1-(&alpha./&sided.))*sqrt(&Q0.))) / sqrt(&Q1.);
power=probnorm(Zb);
call symputx("power",power);
run;
%put Power = &power.;
%mend kappa_power;
data iteration;
i=1;
phat=.90000;
minkap=0;
maxkap=1;
kappa0=minkap+(maxkap-minkap)/2;
output;
run;
proc sql;
select kappa0 into :kappa0 from iteration having i=max(i);
quit;
%kappa_power(rate1=0.8,rate2=0.8,kappa=0.375,kappa0=&kappa0.,n=200,alpha=0.05,sided=1);
data power;
i=1;
power=&power.;
run;
data converge;
merge iteration power;
by i;
run;
You're doing a binomial search for maximum power?
I think something like the following would work, it's only sketched out, but hopefully gives you an idea. I have to actually do some work today :smileyconfused:
Given the code you already have I think it would work, but wasn't sure how to reset the parameters for each iteration in the loop based on your data, mostly because I don't have time...
I also added in a maxiteration so you don't loop forever and good for testing. There's also a while condition to test when you get close enough to a value to determine when to stop.
Good luck.
%macro kappa_power(rate1=,rate2=,kappa=,kappa0=,n=,alpha=0.05,sided=2, maxiter=20);
%do i=1 %to &maxiter %while (error bounds here);
%if &i=1 %then %do;
data iteration;
i=1;
phat=.90000;
minkap=0;
maxkap=1;
kappa0=minkap+(maxkap-minkap)/2;
run;
%end;
%else %do;
data iteration;
i=&i;
*determine new testing value here/macro parameters here;
run;
%end;
data kappa;
p1_=&rate1.;
p_1=&rate2.;
p2_=1-p1_;
p_2=1-p_1;
pe=p1_*p_1+p2_*p_2;
p0=&kappa.*(1-pe)+pe;
p22=(p0-p1_+p_2)/2;
p11=p0-p22;
p12=p1_-p11;
p21=p_1-p11;
A=(p11*((1-pe)-(p1_+p_1)*(1-p0))**2)+(p22*((1-pe)-(p2_+p_2)*(1-p0))**2);
B=(1-p0)**2*(p12*(p_1+p2_)**2+p21*(p_2+p1_)**2);
C=(p0*pe-2*pe+p0)**2;
Q=(1-pe)**-4*(A+B-C);
output;
call symputx("Q1",Q);
run;
data kappa0;
set kappa (keep=p1_ p_1 p2_ p_2 pe);
p0=&kappa0.*(1-pe)+pe;
p22=(p0-p1_+p_2)/2;
p11=p0-p22;
p12=p1_-p11;
p21=p_1-p11;
A=(p11*((1-pe)-(p1_+p_1)*(1-p0))**2)+(p22*((1-pe)-(p2_+p_2)*(1-p0))**2);
B=(1-p0)**2*(p12*(p_1+p2_)**2+p21*(p_2+p1_)**2);
C=(p0*pe-2*pe+p0)**2;
Q=(1-pe)**-4*(A+B-C);
output;
call symputx("Q0",Q);
run;
data power;
Zb=(((&kappa.-&kappa0.)*sqrt(&n.)) - (quantile('NORMAL',1-(&alpha./&sided.))*sqrt(&Q0.))) / sqrt(&Q1.);
power=probnorm(Zb);
call symputx("power",power); * you may not need this as a macro variable anymore.
run;
*merge the values to get the data you need for values table;
data converge;
merge iteration power;
run;
*append for each run;
proc append base=value data=converge;
run;
%end;
%mend kappa_power;
%kappa_power(rate1=0.8,rate2=0.8,kappa=0.375,kappa0=&kappa0.,n=200,alpha=0.05,sided=1, maxiter=20);
call execute will call macro's within a datastep.
Or you can loop through using another macro.
Another option is to use a function, ie FCMP instead of a macro.
I don't see any do loop, so not sure where you're trying to iterate, in the second last datastep (data power?)
I will give you suggestions a try and see what I can come up with. I have never heard of FCMP, but I found some documentation on it that I can read up on.
I have not tried to run the DO loop in my code yet because I wasn't sure how to implement it. That's why you couldn't find it in my code.
You're doing a binomial search for maximum power?
I think something like the following would work, it's only sketched out, but hopefully gives you an idea. I have to actually do some work today :smileyconfused:
Given the code you already have I think it would work, but wasn't sure how to reset the parameters for each iteration in the loop based on your data, mostly because I don't have time...
I also added in a maxiteration so you don't loop forever and good for testing. There's also a while condition to test when you get close enough to a value to determine when to stop.
Good luck.
%macro kappa_power(rate1=,rate2=,kappa=,kappa0=,n=,alpha=0.05,sided=2, maxiter=20);
%do i=1 %to &maxiter %while (error bounds here);
%if &i=1 %then %do;
data iteration;
i=1;
phat=.90000;
minkap=0;
maxkap=1;
kappa0=minkap+(maxkap-minkap)/2;
run;
%end;
%else %do;
data iteration;
i=&i;
*determine new testing value here/macro parameters here;
run;
%end;
data kappa;
p1_=&rate1.;
p_1=&rate2.;
p2_=1-p1_;
p_2=1-p_1;
pe=p1_*p_1+p2_*p_2;
p0=&kappa.*(1-pe)+pe;
p22=(p0-p1_+p_2)/2;
p11=p0-p22;
p12=p1_-p11;
p21=p_1-p11;
A=(p11*((1-pe)-(p1_+p_1)*(1-p0))**2)+(p22*((1-pe)-(p2_+p_2)*(1-p0))**2);
B=(1-p0)**2*(p12*(p_1+p2_)**2+p21*(p_2+p1_)**2);
C=(p0*pe-2*pe+p0)**2;
Q=(1-pe)**-4*(A+B-C);
output;
call symputx("Q1",Q);
run;
data kappa0;
set kappa (keep=p1_ p_1 p2_ p_2 pe);
p0=&kappa0.*(1-pe)+pe;
p22=(p0-p1_+p_2)/2;
p11=p0-p22;
p12=p1_-p11;
p21=p_1-p11;
A=(p11*((1-pe)-(p1_+p_1)*(1-p0))**2)+(p22*((1-pe)-(p2_+p_2)*(1-p0))**2);
B=(1-p0)**2*(p12*(p_1+p2_)**2+p21*(p_2+p1_)**2);
C=(p0*pe-2*pe+p0)**2;
Q=(1-pe)**-4*(A+B-C);
output;
call symputx("Q0",Q);
run;
data power;
Zb=(((&kappa.-&kappa0.)*sqrt(&n.)) - (quantile('NORMAL',1-(&alpha./&sided.))*sqrt(&Q0.))) / sqrt(&Q1.);
power=probnorm(Zb);
call symputx("power",power); * you may not need this as a macro variable anymore.
run;
*merge the values to get the data you need for values table;
data converge;
merge iteration power;
run;
*append for each run;
proc append base=value data=converge;
run;
%end;
%mend kappa_power;
%kappa_power(rate1=0.8,rate2=0.8,kappa=0.375,kappa0=&kappa0.,n=200,alpha=0.05,sided=1, maxiter=20);
This actually makes obvious sense. I guess I was trying to avoid using the same code that I had already written (since I often try to find the power of a Kappa statistic). I suppose it won't take me long to copy and past it into a new macro and just loop through it rather than calling the macro over and over. Thank you!
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
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.