options nonotes nosource; /*Generate random number under exponential distribution.*/ %macro exp_gen(lamda1,lamda2,n,r,drop,seed,sim); /******************************************************************** Function: Generate the simulated survival time under Exponential distribution and random dropout table. Sub-macro affiliated to %n_gssur. *******************************************************************/; %let finish=%sysevalf(1-&drop); %let ratio=%eval(&r+1); data all; do seed=1 to ∼ do i=1 to &n; if mod(i,&ratio)=1 then do;time=ranexp(&seed)/&lamda1;group=1;end; else do;time=ranexp(&seed)/&lamda2;group=2;end; output; end; end; data drop; do seed=1 to ∼ do j=1 to &n; complete=2-rantbl(&seed,&finish); output; end; end; run; %mend; /*Generate random number under weibull distribution.*/ %macro weibull_gen(lamda1,lamda2,p,n,r,drop,seed,sim); /******************************************************************** Function: Generate the simulated survival time under Weibull distribution and random dropout table. Sub-macro affiliated to %n_gssur. *******************************************************************/; %let finish=%sysevalf(1-&drop); %let ratio=%eval(&r+1); data all; do seed=1 to ∼ do i=1 to &n; call streaminit(&seed); if mod(i,&ratio)=1 then do;time=rand('weibull',&p,1/&lamda1);group=1;end; else do;time=rand('weibull',&p,1/&lamda2);group=2;end; output; end; end; data drop; do seed=1 to ∼ do j=1 to &n; complete=2-rantbl(&seed,&finish); output; end; end; run; %mend; %macro n_gssur(m1,m2,t,dtr0,look,info,min,max,len,r,drop,power,seed,sim,path); /******************************************************************** Function: To estimate sample size based on Monte Carlo simulation for TTE data in group sequential design under Exponential and Weibull distributions. Macros called: %exp_gen, %weibull_gen Program flow: Step1: Preparation. Step2: Search for minimum sample size until the specified power is achieved. In each loop, Step2-1: Data generation. Step2-2: Simulate the group sequential trial. Step2-2-1: Screen the subjects for the i-th interim analysis. Step2-2-2: Perfrom log-rank test for the i-th interim analysis. Step2-2-3: Calculate assessment indexes. Step2-2-4: Clear the temporary datasets. Step2-3: Judging & Verification. *******************************************************************/; /*Step1: Preparation*/; %let dtr=%scan(&dtr0,1,'/'); %let p=%scan(&dtr0,2,'/'); /*Calculate the parameter lamda and survival rate under exponential distribution*/ %if %lowcase(&dtr)=exp %then %do; %let lamda1=%sysevalf((-1)*%sysfunc(log(0.5))/&m1); %let lamda2=%sysevalf((-1)*%sysfunc(log(0.5))/&m2); %let s1=%sysfunc(exp(%sysevalf(&lamda1*&t*(-1)))); %let s2=%sysfunc(exp(%sysevalf(&lamda2*&t*(-1)))); %end; /*Calculate the parameter lamda and survival rate under Weibull distribution*/ %if %lowcase(&dtr)=weibull %then %do; %let lamda1=%sysevalf((-1)*%sysevalf(%sysfunc(log(0.5))**(1/&p))/&m1); %let lamda2=%sysevalf((-1)*%sysevalf(%sysfunc(log(0.5))**(1/&p))/&m2); %let s1=%sysfunc(exp(%sysevalf((-1)*%sysevalf((%sysevalf(&lamda1*&t))**&p)))); %let s2=%sysfunc(exp(%sysevalf((-1)*%sysevalf((%sysevalf(&lamda2*&t))**&p)))); %end; /*Take information time and the corresponding significance level.*/ %do i=1 %to &look; %let look&i=%scan(&info,&i,' '); %let info&i=%scan(&&look&i,1,%str(/)); %let sig&i=%scan(&&look&i,2,%str(/)); %end; /*Step2: Searching for the minimum sample size.*/ %let flag=0; %do num=&min %to &max %by &len; %last: /*Step2-1: Data generation*/ %let num0=%eval(&num*&r); %let sum=%eval(&num+&num0); %put I am simulating, N=∑ %if %lowcase(&dtr)=exp %then %do; %exp_gen(&lamda1,&lamda2,&sum,&r,&drop,&seed,&sim); %end; %if %lowcase(&dtr)=weibull %then %do; %weibull_gen(&lamda1,&lamda2,&p,&sum,&r,&drop,&seed,&sim); %end; data all;set all; if time>&t then do; censord=0; time=&t; end; else censord=1; data all;set all; retain j 0; if mod(_n_,&sum)=1 then do; if censord=1 then j=1; else if censord=0 then j=0; end; else if censord=1 then j=j+1; /*Merge time table and random drop table*/ proc sort data=all;by seed j; proc sort data=drop;by seed j; data all; merge all(in=a) drop(in=b); by seed j; if a; data all;set all; if censord=0 then censor=0; else censor=censord*complete; run; data all;set all; retain num 0; if mod(_n_,&sum)=1 then do; if censor=1 then num=1; else if censor=0 then num=0; end; else if censor=1 then num=num+1; run; /*Step2-2: Simulate the group sequential trial*/ data test;set all; proc sort data=test; by seed group; run; /*Step2-2-1: Screen the subjects for the i-th interim analysis*/ %let event=%sysfunc(int(&num*(1-&drop)*(1-&s1)+&num0*(1-&drop)*(1-&s2))); %do i=1 %to &look; %let event&i=%sysfunc(int(&event*&&info&i)); data test&i;set test; if num<=&&event&i; run; %if &i>1 %then %do; proc sql; create table mm as select * from test&i where seed not in (select seed from eff); quit; data test&i;set mm; %end; /*Step2-2-2: Perform log-rank test for the i-th interim analysis*/ ods exclude all; proc lifetest data=test&i method=KM; time time*censor(0); strata group; by seed; ods output HomTests=stat&i; run; ods output close; ods select all; data stat&i;set stat&i; if test='Log-Rank'; drop test df; stat=probit(1-probchisq/2); if stat>&&sig&i then eff=1; else if (probchisq=<.0001 and stat=.) then eff=1; else eff=0; run; %if &i=1 %then %do; data eff;set stat&i; %end; %else %do; data eff;set eff stat&i; %end; if eff=1; run; proc sql noprint; select count(*)/&sim into :cpower&i from eff; quit; %if &&cpower&i=1 %then %do; %put *****************************************************; %put Fail to search sample size. All simulated trials succeed. Pls reconsider macro parameters.; %goto exit; %end; /*Step2-2-3: Calculate the assessment indexes*/ %let j=%eval(&i-1); %if &i=1 %then %do;%let power&i=%sysfunc(compress(%sysfunc(round((%sysevalf(&&cpower&i)),.0001))));%end; %else %do;%let power&i=%sysfunc(compress(%sysfunc(round(%sysevalf(&&cpower&i-&&cpower&j),.0001))));%end; %let cpower&i=%sysfunc(compress(%sysfunc(round(&&cpower&i,.0001)))); %let alpha&i=%sysfunc(round(%sysevalf((1-%sysfunc(probnorm(&&sig&i)))*2),.000001)); %end; /*Calculate expected events*/ %let ed=0; %do i=1 %to &look; %if &i=1 %then %do; %let ed=%sysevalf(&ed+&&event&i); %end; %else %do; %let j=%eval(&i-1); %let d=%sysevalf(&&event&i-&&event&j); %let ed=%sysevalf(&ed+(%sysevalf(&d*(%sysevalf(1-&&cpower&j))))); %end; %end; data _null_; file "&path" %if (&num>&min) %then %do; mod %end; ; put "In the simulation, the dropout rate is defined as &drop..The total sample size is &sum.(N1=&num., N2=&num0.).The preplanned total number of the events is &event.. The expected number of the events is &ed.. The overall power is &&cpower&look."; put "Look" +1 "Time" +1 "Events" +1 "Alpha" +1 "Power" +1 "cPower"; %do i=1 %to &look; put "&i" +1 "&&info&i" +1 "&&event&i" +1 "&&alpha&i" +1 "&&power&i" +1 "&&cpower&i"; %end; put 50*'-'; run; /*Step2-2-4: Clear the temporary datasets*/ proc datasets lib=work memtype=data nolist nowarn; delete test eff %do i=1 %to &look;test&i. stat&i %end;; quit; /*End of the i-th interim analysis*/ /*Step2-3: Judging & Verification*/ %if (&&cpower&look>=&power and &flag=0) %then %do; %put Stage of Verification; %let flag=1; %let ss=%eval(&num*(1+&r)); %let num=%eval(&num+1); %goto last; %end; %if (&&cpower&look>=&power and &flag=1) %then %do; %put *****************************; %put End of Searching; %put The recommended total sample size is &ss.. The search histroty can be seen in &path..; data _null_; file "&path" mod; put "End of Search.."; put "The recommended total sample size is &ss.."; run; %goto exit; %end; %if (&&cpower&look<&power and &flag=1) %then %do; %let num=%eval(&num+1); %let ss=%eval(&num*(1+&r)); %goto last; %end; %if (&&cpower&look<&power and &num=&max) %then %do; %put The final sample size is not achieved. The search histroty can be seen in &path..; %end; %end; /*End of sample size searching*/ %exit: %mend; *Sample Call; %n_gssur(4.5,6,18,exp,3,0.5/2.963 0.75/2.359 1/2.014,191,210,1,2,0.2,0.8,8465,5000,E:\result_exp9.txt);