############################################################################ # Accelerated Titration Design Model for Phase I Clinical Trials # ############################################################################ # The program fits the model from "Accelerated Titration Designs for PhaseI# # Clinical Trials in Oncology" by Simon R., Freidlin B., Rubinstein L., # # Arbuck S., Collins G., Christian M. # # Journal of the National Cancer Institute, 1997, V89, No. 15, p 1138-1147# # # # Program was written by Boris Freidlin, bfreidlin@emmes.com (301) 2998655# # in collaboration with Richard Simon, rich@brb.nci.nih.gov # # Larry Rubinstein rubinsteinl@ctep.nci.nih.gov # ############################################################################ # The program is written in S-PLUS (updated on 04/24/98) # # Before running the program, load the functions # # by typing source('...../ph1atd'). # # To run the program, # # type ph1atd('inputfile','doselist') at the S-PLUS prompt, # # where the "inputfile" is an ASCII file which contains the patient data # # in the following format, one record(line) per course: # # Column 1: patient number # # Column 2: course number # # Column 3: dose # # Column 4: toxicity # # records should be ordered by patient and course number # # and "doselist" is an ASCII file which contains all the doses planned # # to be used in the trial, listed in a single column in ascending order # ############################################################################ # The output contains: # # 1) max likelihood estimates of the model parameters # # 2) confidence intervals for parameters with non-zero MLE # # both MLE and CI are sent to the screen and file "ph1atd.out" # # 3) graph of probabilities of grade2+,3+ and 4+ toxicities (for the first # # course) averaged over the population of patients as in Figure 5 # # in the publication.(the graph is stored in file "ph1atd1.wmf") # # # 4) graphs of probabilities of grade2+,3+ and 4+ toxicities (for the # # first course) for the patients with beta equal to MLE-SD, MLE # # MLE+SD as in Figure 6 in the publication. (the graph is stored # # in file "ph1atd2.wmf") # # Note: if the MLE of sigma beta is 0 these graphs are not produced. # ############################################################################ pco_function(bi,vp,d,dc,tox) { ## the following matrix contains log(d+alpha*D)+Bi: ## rows: correspond to the treatment courses ## columns: correspond to ss randomly generated Bi for the MC mc1_t(matrix(bi,length(bi),length(d)))+ matrix(log(d+vp[4]*dc),length(d),length(bi)) ## first level of integration: integration over epsilon from Ki to K(i+1) mc1[,]_pnorm(ifelse(tox<2,vp[1],ifelse(tox==2,sum(vp[c(1:2)]), ifelse(tox==3,sum(vp[c(1:3)]),Inf))),mean=mc1,sd=vp[6])- pnorm(ifelse(tox<2,-Inf,ifelse(tox==2,vp[1],ifelse(tox==3, sum(vp[c(1:2)]),sum(vp[c(1:3)])))),mean=mc1,sd=vp[6]) mc1 } ph1atd_function(d0in,dosein) { set.seed(7) # ss is the # of Monte-Carlo replications for the second level integration ss_1000 d00_matrix(scan(d0in),ncol=4,byrow=T) d00_d00[,2:4] dosev_matrix(scan(dosein),ncol=1,byrow=T) flik_function(vp,dvf=dddm3,vr1=vr) { fl_1 vr1_vr1*vp[5] for (pn in (1:dvf[dim(dvf)[1],4])) { dv_dvf[(dvf[,4]==pn),(1:3)] ## one patient (all courses) contribution to the likelihood fl_fl+ log(if (length(dv)>4) mean(apply(pco(vr1,vp,dv[,1],dv[,2],dv[,3]),2,prod)) else mean(pco(vr1,vp,dv[1],dv[2],dv[3]))) } -fl } # following modifications of the above function use log scale # for sb and se # this is needed for the CI calculations flikd_function(vp,dvf=dddm3,vr1=vr) { fl_1 # log scale vr1_vr1*exp(vp[5]) vp[6]_exp(vp[6]) for (pn in (1:dvf[dim(dvf)[1],4])) { dv_dvf[(dvf[,4]==pn),(1:3)] ## one patient (all courses) contribution to the likelihood fl_fl+ log(if (length(dv)>4) mean(apply(pco(vr1,vp,dv[,1],dv[,2],dv[,3]),2,prod)) else mean(pco(vr1,vp,dv[1],dv[2],dv[3]))) } -fl } ## this part takes the raw data (d00) in the form ## row1 course ## row2 dose level ## row3 toxicity ## and converts to file ready for fitting (ddd): ## row1 dose ## row2 cum dose ## toxicity ## patient number pc_0 ddd_matrix(0,dim(d00)[1],4) d00[,3]_ifelse(d00[,3]==0,1,d00[,3]) ddd[,3]_d00[,3] ddd[,1]_d00[,2] pn_0 for (i in (1:dim(d00)[1])) { if(d00[i,1]==1) ddd[i,2]_0 if(d00[i,1]!=1) ddd[i,2]_ddd[i-1,2]+ddd[i-1,1] if(d00[i,1]==1) pn_pn+1 ddd[i,4]_pn } vpi_matrix(c(0,0,0,0,.5,.5),1,6) ## initial guess for k1: mean of log dose levels with grade2 tox at course1 vpi[1]_mean(log(d00[(d00[,1]==1 & d00[,3]==2),2])) ## initial guesses for k2 and k3 correspond to 200% increments (log3) ttd_sort(d00[,2]) vpi[c(2:3)]_log(3) vpl_c(log(range(ttd)[1]),0,0,0,0,0) vpu_log(max(range(ttd)[2],vpi[2]))*c(1,1,1,1,1,1) print(date()) vr_matrix(0,ss,1) vr_rnorm(ss,mean=0,sd=1) mle_nlminb(vpi,flik,lower=vpl,upper=vpu,dvf=ddd,vr1=vr, control=nlminb.control(rel.tol=.00001, x.tol=.0001,step.min=.00001))$parameters ## confidence intervals part mled_matrix(NA,6,3) mled[1:3,1]_cumsum(mle[1:3]) mled[4:6,1]_mle[4:6] dimnames(mled)_list(c(' K1 ',' K2 ',' K3 ', 'Alpha ','Sigma Beta ', 'Sigma Epsilon '), c(' MLE ', ' Lower Bound', ' Upper bound')) mler_round(mle,3) mle[5:6]_log(mle[5:6]) sde_matrix(-1,1,6) del_mle[1]*diag(.00001,6,6) # CIs are calculated only for non zero parameters (alpha, sigma beta) del_del[,mler!=0] npr_dim(del)[2] # this loop estimates of the matrix of second derivatives of the # log likelihood until it is nonsingular repeat { dd2_matrix(0,npr,npr) for (i2 in (1:npr)) { for (j2 in (i2:npr)) { dd2[i2,j2]_(flikd((mle+del[,i2]+del[,j2]),dvf=ddd,vr1=vr)- flikd((mle+del[,i2]),dvf=ddd,vr1=vr)- flikd((mle+del[,j2]),dvf=ddd,vr1=vr)+ flikd((mle),dvf=ddd,vr1=vr))/(del[1,1]^2) dd2[j2,i2]_dd2[i2,j2] }} # get out if dd2 is not singular reduce del otherwise dd2i_apply(dd2,1,sum) if (prod(dd2i)!=0 | max(diag(del))>.01) break diag(del)[seq(along=dd2i)[dd2i==0]]_10*diag(del)[seq(along=dd2i)[dd2i==0]] } if (prod(dd2i)!=0 ) { fish_solve(dd2) sde[1]_sqrt(fish[1,1]) sde[2]_sqrt(matrix(1,1,2)%*%fish[1:2,1:2]%*%matrix(1,2,1)) sde[3]_sqrt(matrix(1,1,3)%*%fish[1:3,1:3]%*%matrix(1,3,1)) if (mler[4]<=.1 & mler[5]==0 ) sde[6]_sqrt(fish[4,4]) else {if (mler[4]<=.1 ) { sde[5]_sqrt(fish[4,4]) sde[6]_sqrt(fish[5,5])} else {if (mler[5]==0 ) { sde[4]_sqrt(fish[4,4]) sde[6]_sqrt(fish[5,5])} else { sde[4]_sqrt(fish[4,4]) sde[5]_sqrt(fish[5,5]) sde[6]_sqrt(fish[6,6]) } } } mled[1:3,2]_cumsum(mle[1:3])-1.96*sde[1:3] mled[1:3,3]_cumsum(mle[1:3])+1.96*sde[1:3] if (sde[4]>0) {mled[4,2]_(mle[4]-1.96*sde[4]) mled[4,3]_(mle[4]+1.96*sde[4]) } if (sde[5]>0) {mled[5,2]_exp(mle[5]-1.96*sde[5]) mled[5,3]_exp(mle[5]+1.96*sde[5]) } if (sde[6]>0) {mled[6,2]_exp(mle[6]-1.96*sde[6]) mled[6,3]_exp(mle[6]+1.96*sde[6]) } } else print("WARNING: Unable to calculate confidence intervals") print("Accelerated Titration Design Model for Phase I Clinical Trials") print(" MLE and 95%CI for the model parameters ") print(round(mled,3)) # put the output into the file sink('ph1atd.out') print("Accelerated Titration Design Model for Phase I Clinical Trials") print(" MLE and 95%CI for the model parameters ") print(round(mled,3)) sink() #dose scale #dls_sort(d00[d00[,1]==1,2]) #nd_length(dls) #dls1_matrix(0,1,nd) #dls1[1:(nd-1)]_dls[2:nd] #dls_dls[dls!=dls1] nd_length(dosev) dls_dosev prbt_matrix(0,nd,3) doselevel_c(1:nd) dv_sqrt(sum(mled[5:6,1]^2)) prbt[1:(nd),1]_(round((1-pnorm((mled[1,1]-log(dls)-0)/dv)),4)) prbt[1:(nd),2]_(round((1-pnorm((mled[2,1]-log(dls)-0)/dv)),4)) prbt[1:(nd),3]_(round((1-pnorm((mled[3,1]-log(dls)-0)/dv)),4)) win.printer(format="placeable metafile",file='ph1atd1.wmf') matplot(doselevel,prbt,type="l",lty=c(2,9,5),lwd=1,col=1,ylim=c(0,1), xlab='Dose Level',ylab="Probability",lab=c(25,10,9),axes=F) title('Probabilities of Grade 2+, Grade 3+, Grade 4+ Toxicities at the MLE') axis(2) axis(1,at=doselevel) box() legend(1,1,legend=c("Grade 2+","Grade 3+","Grade 4+"),lty=c(2,9,5)) dev.off() # individual plots a done only if MLE(sigma beta)>0 if (mled[5,1]>0) { win.printer(format="placeable metafile",file='ph1atd2.wmf', orientation='portrait') par(mfrow=c(3,1)) prbt[1:(nd),1]_(round((1-pnorm((mled[1,1]-log(dls)+mled[5,1])/mled[6,1])),4)) prbt[1:(nd),2]_(round((1-pnorm((mled[2,1]-log(dls)+mled[5,1])/mled[6,1])),4)) prbt[1:(nd),3]_(round((1-pnorm((mled[3,1]-log(dls)+mled[5,1])/mled[6,1])),4)) matplot(doselevel,prbt,type="l",lty=c(2,9,5),lwd=1,col=1,ylim=c(0,1), xlab='Dose Level',ylab='Probability',lab=c(25,10,9),las=1,axes=F) legend(1,1,legend=c("Grade2+","Grade3+","Grade4+"),lty=c(2,9,5)) title('Probabilities of the Toxicities at the MLE(beta)-SD') axis(2,at=c(0,.5,1)) axis(1,at=doselevel) box() prbt[1:(nd),1]_(round((1-pnorm((mled[1,1]-log(dls))/mled[6,1])),4)) prbt[1:(nd),2]_(round((1-pnorm((mled[2,1]-log(dls))/mled[6,1])),4)) prbt[1:(nd),3]_(round((1-pnorm((mled[3,1]-log(dls))/mled[6,1])),4)) matplot(doselevel,prbt,type="l",lty=c(2,9,5),lwd=1,col=1,ylim=c(0,1), xlab='Dose Level',ylab='Probability',lab=c(25,10,9),las=1,axes=F) legend(1,1,legend=c("Grade 2+","Grade 3+","Grade 4+"),lty=c(2,9,5)) title('Probabilities of the Toxicities at the MLE(beta)') axis(2,at=c(0,.5,1)) axis(1,at=doselevel) box() prbt[1:(nd),1]_(round((1-pnorm((mled[1,1]-log(dls)-mled[5,1])/mled[6,1])),4)) prbt[1:(nd),2]_(round((1-pnorm((mled[2,1]-log(dls)-mled[5,1])/mled[6,1])),4)) prbt[1:(nd),3]_(round((1-pnorm((mled[3,1]-log(dls)-mled[5,1])/mled[6,1])),4)) matplot(doselevel,prbt,type="l",lty=c(2,9,5),lwd=1,col=1,ylim=c(0,1), xlab='Dose Level',ylab='Probability',lab=c(25,10,9),las=1,axes=F) legend(1.5,1,legend=c("Grade 2+","Grade 3+","Grade 4+"),lty=c(2,9,5)) title('Probabilities of the Toxicities at the MLE(beta)+SD') axis(2,at=c(0,.5,1)) axis(1,at=doselevel) box() dev.off() } }