*! version 1.1.0 AGB 10dec2013 /* History 1.1.0 10dec2013 Sample size and power for non-inferiority designs corrected. Other changes: - Point mass at time 0 for distribution functions enabled. - Index parameter for the Harrington-Fleming test was dropped in translation from STUDYSI.ADO to ARTSURV.ADO. It is now restored and this option fot the test method no longer gives an error message. - Store expected proportions lost to follow-up and crossing over. 1.0.8 02apr2010 store duration of recruitment and recruitment weights; and,for each arm, loss to follow-up probability at maximum follow-up time, expected proportion lost to follow-up 1.0.7 19oct2009 calculate, output and store expected number of events per group 1.0.6 24oct2006 output to artpep via macro allow numlist in probabilities times in edf0() etc 1.0.5 06jul2005 no changes here, changes to some help files 1.0.4 20dec2004 syntax change to trend option to allow using in artsurv.dlg 1.0.3 13dec2004 using option added to allow filing of probs and hazard ratios minor tidying up of nomenclature -nohead- option added, to suppress banner heading Median was incorrectly forced to be an integer, now real Minor change to output to report median survival time, if input Fixed bug in reporting recruitment weights when too many specified 1.0.2 12nov2004 Allow edf0(), ldf(), wdf() to be specified for periods beyond that given by nperiod(). Change necessary for -artpep- to work correctly. Create default for recrt() and aratios() if unspecified. 1.0.1 26may2004 [First public release] */ program define artsurv, rclass version 10.0 syntax [using/] [, ALpha(real 0.05) ARatios(string) CRover(string) DEtail(int 0) DIstant(int 0) /// DOses(string) EDf0(string) FP(int 0) noHEad HRatio(string) INDexsurv(real 1) hr(int 1) LDf(string) /// LG(string) MEDian(real 0) METhod(string) N(integer 0) NGroups(integer 2) NI(int 0) /// NPeriod(integer 1) Onesided(int 0) POwer(real 0.8) PWehr(string) REcrt(string) REPLACE /// TRend(int 0) TUnit(int 1) WDf(string) WG(string)] local version "survival version 1.1.0, 10 December 2013" sreturn clear local artpep "alpha(`alpha') aratios(`aratios') hratio(`hratio') ngroups(`ngroups') ni(`ni') onesided(`onesided') trend(`trend') tunit(`tunit')" if `"`edf0'"'!="" { local artpep "`artpep' edf0(`edf0')" } if `"`median'"'!="" { local artpep "`artpep' median(`median')" } if `"`wdf'"'!="" { local artpep "`artpep' wdf(`wdf') wg(`wg')" if `"`crover'"'!="" { local artpep "`artpep' crover(`crover')" } if `"`pwehr'"'!="" { local artpep "`artpep' pwehr(`pwehr')" } } if `"`ldf'"'!="" { local artpep "`artpep' ldf(`ldf') lg(`lg')" } if `"`doses'"'!="" { local artpep "`artpep' doses(`doses')" } if `"`method'"'!="" { local artpep "`artpep' method(`method')" } global S_ARTPEP `"`artpep'"' * Cancel returned errmess macro since can't be handled by Stata 8 dialog system *local reterr "return local errmess" local reterr "noi di as err" if `ngroups'<2 { `reterr' "invalid number of groups, must be at least 2" exit 198 } if "`recrt'"=="" { local recrt 0,0,0 } if "`aratios'"=="" { local aratios 1 } if "`edf0'"=="" { if `median'>0 { /* define survival prob in period 1 and put into edf0() */ local s0=exp(-ln(2)/`median') local edf0 `s0',1 } else { `reterr' "Baseline distribution function edf0() or median() required" exit 198 } } if `onesided' & `ngroups'>2 { `reterr' "one-sided test not allowed with >2 groups" exit 198 } if `trend'==0 { /* default */ local trend "" } else { local trend "trend" } if (`ngroups'==2) { /* use trend test on 2 groups - more accurate for large alpha values */ local trend trend } if `alpha'<=0 | `alpha'>=1 { `reterr' "alpha() out of range" exit 198 } if `n'<=0 & (`power'<=0 | `power'>=1) { `reterr' "power() out of range (0,1)" exit 198 } if `ni' { if `ngroups'>2 { `reterr' "No more than 2 groups are allowed for non-inferiority designs" exit 198 } if ("`crover'"~="")|("`pwehr'"~="")|("`wg'"~="")|("`wdf'"~="") { `reterr' "Cross-over not allowed for non-inferiority designs" exit 198 } } if "`lg'"!=""&"`ldf'"=="" { `reterr' "Loss to follow-up time distribution function required" exit 198 } if "`wg'"!=""&"`wdf'"=="" { `reterr' "Withdrawal time distribution function required" exit 198 } if `tunit'<1 | `tunit'>7 { `reterr' "Invalid time-unit code" exit 198 } if `distant'==0 { /* default means non-distant (i.e. local) alternatives */ local local "" local locmess "(local)" } else { local local "nolocal" local locmess "(distant)" } if "`aratios'"!="" { local nratio=wordcount("`aratios'") if `nratio'>`ngroups' { `reterr' "You must give no more than `ngroups' allocation ratios" exit 198 } tokenize `aratios' local default 1 forvalues i=1/`nratio' { if "``i''"!="1" { local default 0 } } if `default' { local aratios } } * `Alpha' is value as supplied; `alpha' is for use in calculations local Alpha `alpha' if `onesided' { local alpha=2*`alpha' local sided one } else local sided two local Power `power' /* as supplied */ if `ni' { local trialtype "Non-inferiority" } else local trialtype "Superiority" if "`method'"=="b" | "`method'"=="u" | (`ni' & ("`method'"=="c")) { local trialtype "`trialtype' - events at end of trial" } else local trialtype "`trialtype' - time-to-event outcome" tokenize `""One year" "6 months" "One quarter (3 months)" "One month" "One week" "One day" "Unspecified""' local lperiod ``tunit'' tokenize `""year" "six-month period" "quarter" "month" "week" "day" "time-period unit""' local lperiod2 ``tunit'' if `n'==0 { local ssize 1 } else local ssize 0 if "`method'"=="" { local method l } local m1=substr("`method'",1,1) if "`m1'"=="l" { local test "Unweighted logrank test" local tstat 1 } else if "`m1'"=="t" { local test "Weighted logrank test: Tarone-Ware" local tstat 2 } else if "`m1'"=="h" { local tstat 3 local index `indexsurv' local test "Weighted logrank test: Harrington-Fleming (index `index')" } else if "`m1'"=="b" { if `ni' { local test "Conditional test with fixed marginal totals" } else { local test "Conditional test using Peto's approximation to the odds-ratio" } local tstat 0 local condit "condit" if `ni' local nvm 1 /* Estimation of variance of difference in proportions under the null hypothesis */ } else if "`m1'"=="u" { local tstat 0 local test "Unconditional binomial test" local condit if `ni' local nvm 0 if "`local'"!="" { local nol "nolocal" } } else if `ni' & ("`m1'"=="c") { local tstat 0 local test "Constrained maximum likelihood" local condit local nvm 2 if "`local'"!="" { local nol "nolocal" } } else { `reterr' "method(`method') not allowed" exit 198 } * Prepare for saving survival probs and hazard ratios to file if `"`using'"'!="" { if substr(`"`using'"',-4,.)!=".dta" { local using `using'.dta } if "`replace'"=="" { confirm new file `"`using'"' } tempname tmp local postvl surv hr postfile `tmp' group period `postvl' using `using', `replace' } quietly { global initrec preserve drop _all set obs `ngroups' capture set matsize 400 /* not often needed, but may be if detail is specified. */ gen byte grp=_n sort grp _ar `ngroups' `aratios' /* Setup allocation ratio */ local allocr "$AR" global AR if `ni' { // Alloc ratio to be used in art2bin // local ar21 = cond("`aratios'"=="1", 1, ar[2]/ar[1]) } if `tstat'!=0 { if "`trend'"!=""|"`doses'"!="" { _dose `ngroups' `doses' /* Setup doses for trend */ local doses "$DOSE" global DOSE tempname dose mkmat dose if grp>1,matrix(`dose') } } if "`trend'"!=""|"`doses'"!="" { local trtest "Linear trend test: doses = " } else local trtest "Global test" expand `nperiod' sort grp by grp:gen int period=_n sort grp period * Calculate event survival and hazard functions local sp=!`fp' /* flag for survival probs supplied in edf0() */ if `sp' { local ev "survival" } else local ev "cumulative event" capture _survf esf0, np(`nperiod') gr(1) cpr(`edf0') event(baseline `ev' probability) reverse(`sp') local rc=_rc if `rc' { `reterr' "`s(err)'" *return local errcode `rc' *exit 0 exit `rc' } local m=`nperiod'+1 replace esf0=esf0[_n-`nperiod'] in `m'/l _condsrv esf0 cesf0 gen double el0=-log(cesf) gen double cesf=cesf0 gen double el=el0 _hr `ngroups',`hratio' /* Setup hazard ratio function */ local hratios forvalues i=1/`ngroups' { if `i'>1 { local hratios `hratios', `s(hr`i')' } else local hratios `s(hr`i')' } if `ni' { tempvar hr1 dhr sort period grp qui by period: gen double `hr1' = hr/hr[1] sort grp period qui by grp:gen double `dhr'=abs(`hr1'-`hr1'[1]) local nph 0 forvalues i=1/`ngroups' { summ `dhr' if grp==`i' local nph = `nph' + (r(max)>1e-16) } if `nph' { noi di as err "Non-proportional hazards are not allowed for non-inferiority designs" exit 198 } local hr21:display %4.2f `hr1'[_N] drop `hr1' `dhr' } /* _hr leaves behind variables grp period esf hr. esf is the empirical survival function, taking the hr into account. Also, get hazard ratios in each period (use this in detailed output only) Compute survival/failure function for ith group in each period for later display */ forvalues i=1/`ngroups' { local edf`i' local hratios`i' forvalues j=1/`nperiod' { sum esf if grp==`i' & period==`j' if `sp' { local esfij=r(mean) } else local esfij=1-r(mean) local edf: display %5.3f `esfij' local edf`i' `edf`i'' `edf' sum hr if grp==`i' & period==`j' local hrij=r(mean) local hr: display %6.3f `hrij' local hratios`i' `hratios`i'' `hr' if `"`using'"'!="" { if `j'==1 { * post survival probs and hr of 1 at "period 0" post `tmp' (`i') (0) (1) (.) } post `tmp' (`i') (`j') (`esfij') (`hrij') } } } * Calculate loss to follow-up survival function if "`lg'"=="" { gen double lsf=1 } else { capture _survf lsf,np(`nperiod') gr(`lg') cpr(`ldf') /* */ event(cumulative probability of loss to follow-up) local rc=_rc if `rc' { `reterr' "`s(err)'" *return local errcode `rc' *exit 0 exit `rc' } replace lsf=1 if lsf==. } _condsrv lsf clsf gen double ll=-log(clsf) * Calculate withdrawal survival function and post withdrawal hazard ratio gen double pwehr=1 if "`wg'"=="" { gen double wsf=1 /* Survival function for withdrawal time (cont part) */ gen double wmass0=0 /* Proportion withdrawn at time 0 */ gen double cwsf=1 /* Conditional surv function for withdr time */ gen double wl=0 /* Withdrawal rate */ } else { capture _survf wsf wmass0,np(`nperiod') gr(`wg') cpr(`wdf') /* */ event(cumulative probability of withdrawal) local rc=_rc if `rc' { `reterr' "`s(err)'" *return local errcode `rc' *exit 0 exit `rc' } replace wsf=1 if wsf==. replace wmass0=0 if wmass0==. _condsrv wsf cwsf gen double wl=-log(cwsf) * Set postwithdrawal event hazard ratio if "`crover'"!="" { local co "crover(`crover')" } if "`pwehr'"!="" { local ph "pwehr(`pwehr')" } pwh, ngroups(`ngroups') wg(`wg') `co' `ph' } * Recruitment cdf, weights, rates and proportion not adminstr censored if "`recrt'"!="" { local rec "recr(`recrt')" } _recd,np(`nperiod') `rec' local R=$recper global recper by grp:gen double in0=recdf[_N-_n+1] /* in study at start of period */ by grp:gen double inwt=recwt[_N-_n+1] by grp:gen double inir=recir[_N-_n+1] drop recdf recwt recir tempname es ls wm0 ws ep lp wp mu V0 V1 * Obsrved proportion of failure, loss to followup and withdrawal if `ni' { tempvar k expand 2 if grp==2 sort grp period qui by grp period: gen byte `k'=_n replace grp = 0 if `k'==2 sort grp period foreach var in cesf el hr esf { replace `var' = `var'[_n+`nperiod'] if grp==0 } drop `k' } _subdf esubdf lsubdf wsubdf if `ni' replace grp = 3 if grp==0 sort grp period by grp:gen int last=_n==_N mkmat esf if last,matrix(`es') mkmat esubdf if last,matrix(`ep') mkmat lsf if last,matrix(`ls') mkmat lsubdf if last,matrix(`lp') mkmat wmass0 if last,matrix(`wm0') mkmat wsf if last,matrix(`ws') mkmat wsubdf if last,matrix(`wp') local ep0 // Event prob at max follow-up (as designed) for display local expev // Expected proportion of primary events at study end local ep1 // Same as expev formatted for display local lp0 // Prob of loss to follow-up at max follow-up (as designed) for display local explf // Expected proportion lost to follow-up at study end local lp1 // Same as explf formatted for display local wp0 // Prob of withdrawal (crossover) at max follow-up (as designed) for display local expwd // Expected proportion withdrawn (from allocated trt) at study end local wp1 // Same as expwd formatted for display local co local NG = cond(`ni', `ngroups'+1, `ngroups') forvalues i=1/`NG' { local expev`i' = `ep'[`i',1] local explf`i' = `lp'[`i',1] local expwd`i' = `wp'[`i',1] if ~(`ni' & (`i'==2)) { frac_ddp (1-`es'[`i',1]) 3 local ep0 `ep0'`co' `r(ddp)' frac_ddp `expev`i'' 3 local ep1 `ep1'`co' `r(ddp)' frac_ddp `explf`i'' 3 local lp1 `lp1'`co' `r(ddp)' frac_ddp `expwd`i'' 3 local wp1 `wp1'`co' `r(ddp)' local expev `expev' `expev`i'' local explf `explf' `explf`i'' local expwd `expwd' `expwd`i'' local co "," } } local co forvalues i=1/`ngroups' { frac_ddp (1-`ls'[`i',1]) 3 local lp0 `lp0'`co' `r(ddp)' frac_ddp (1-`ws'[`i',1])*(1-`wm0'[`i',1])+`wm0'[`i',1] 3 local wp0 `wp0'`co' `r(ddp)' local co "," } if `ni' { local expev12 = `expev1'/(1+`ar21') + `expev3'*`ar21'/(1+`ar21') drop if grp==3 } if `tstat'==0 { /* if `ni' { local Sided if `onesided' local Sided onesided local margin = `expev2'-`expev1' if `n'==0 { art2bin `expev1' `expev3', margin(`margin') ar(`ar21') /// alpha(`alpha') power(`power') `Sided' local n `r(n)' } else { local n0 = floor(`n'/(1+`ar21')) local n1 = floor(`n'*`ar21'/(1+`ar21')) art2bin `expev1' `expev3', margin(`margin') n0(`n0') /// n1(`n1') alpha(`alpha') `Sided' local power `r(power)' } local D = ceil(`n'*`expev12') } else { */ local pr forvalues i=1/`ngroups' { local pr "`pr' `expev`i''" } if "`doses'"!="" { local Doses "doses(`doses')" } if "`aratios'"!="" { local Aratios "aratios(`aratios')" } local ap2 local nvmethod if `ni' { local ap2 ap2(`expev3') local nvmethod nvm(`nvm') } artbin, pr(`pr') ngroups(`ngroups') `Aratios' `condit' /// alpha(`Alpha') n(`n') power(`Power') `trend' `Doses' /// distant(`distant') onesided(`onesided') ni(`ni') `ap2' `nvmethod' local `r(allocr)' local doses "$dose" local power=r(power) local n=r(n) local D=r(D) * } } else { gen double P=ar*esubdf if last replace P=sum(P) tempname P scalar `P'=P[_N] _muv `mu' `V0' `V1' `ngroups' `tstat' `index' tempname IV0 A q0 a b local K=`ngroups'-1 scalar `b'=1-`power' if "`trend'`doses'"=="" { mat `IV0'=syminv(`V0') mat `A'=`mu''*`IV0' mat `A'=`A'*`mu' scalar `q0'=`A'[1,1] scalar `a'=invchi2(`K',1-`alpha') if "`local'"=="" { if `n'==0 { local n=npnchi2(`K',`a',`b') local n=`n'/`q0' if `ni' { local n = `n'*`P'/(`expev12') } } else { if `ni' { local nn = `n'*`expev12'/(`P') } else { local nn = `n' } scalar `b'=nchi2(`K',`nn'*`q0',`a') } } else { tempname B a0 a1 q1 eta g psi l mat `A'=`IV0'*`V1' scalar `a0'=trace(`A') mat `B'=`A'*`A' scalar `a1'=trace(`B') mat `A'=`A'*`IV0' mat `A'=`mu''*`A' mat `A'=`A'*`mu' scalar `q1'=`A'[1,1] if `n'==0 { * ******************************* * Solve for n iteratvely tempname n0 nl nu b0 sm scalar `sm'=0.001 local i 1 scalar `n0'=npnchi2(`K',`a',`b') scalar `n0'=`n0'/`q0' _pe2 `a0' `q0' `a1' `q1' `K' `n0' `a' `b0' if abs(`b0'-`b')<=`sm' { local i 0 } else { if `b0'<`b' { scalar `nu'=`n0' scalar `nl'=`n0'/2 } else { scalar `nl'=`n0' scalar `nu'=2*`n0' } } while `i' { scalar `n0'=(`nl'+`nu')/2 _pe2 `a0' `q0' `a1' `q1' `K' `n0' `a' `b0' if abs(`b0'-`b')<=`sm' { local i 0 } else { if `b0'<`b' { scalar `nu'=`n0' } else scalar `nl'=`n0' local i=`i'*((`nu'-`nl')>1) } } local n=`n0' if `ni' { local n = `n'*`P'/(`expev12') } * ******************************* } else { if `ni' { local nn = `n'*`expev12'/(`P') } else { local nn = `n' } _pe2 `a0' `q0' `a1' `q1' `K' `nn' `a' `b' } } } else { /* trend test */ tempname tr q1 mat `A'=`dose''*`V0' mat `A'=`A'*`dose' scalar `q0'=`A'[1,1] mat `A'=`dose''*`mu' scalar `tr'=`A'[1,1] if "`local'"=="" { scalar `q1'=`q0' } else { mat `A'=`dose''*`V1' mat `A'=`A'*`dose' scalar `q1'=`A'[1,1] } scalar `a'=sqrt(`q0')*invnorm(1-`alpha'/2) if `n'==0 { scalar `a'=`a'+sqrt(`q1')*invnorm(`power') local n=(`a'/`tr')^2 if `ni' { local n = `n'*`P'/(`expev12') } } else { if `ni' { local nn = `n'*`expev12'/(`P') } else { local nn = `n' } scalar `a'=abs(`tr')*sqrt(`nn')-`a' scalar `b'=1-normprob(`a'/sqrt(`q1')) } } if `ni' { scalar `P'=`n'*`expev12' } else { scalar `P'=`n'*`P' } local D=round(`P',1)+(round(`P',1)<`P') local n=round(`n',1)+(round(`n',1)<`n') local power=1-`b' } } * Display output. if `ngroups'==2 { local gplist "(groups 1,2)" } else if `ngroups'==3 { local gplist "(groups 1,2,3)" } else local gplist "(groups 1,..,`ngroups')" local off 42 local maxwidth 78 local skip=`maxwidth'-length("ART - ANALYSIS OF RESOURCES FOR TRIALS")-length("(`version')") local longstring=`maxwidth'-`off' if "`head'"=="" { di as text _n "{hi:ART} - {hi:A}NALYSIS OF {hi:R}ESOURCES FOR {hi:T}RIALS" /* */ _skip(`skip') "(`version')" _n "{hline `maxwidth'}" display as text "A sample size program by Abdel G Babiker, Patrick Royston & Friederike Barthel," display as text "MRC Clinical Trials Unit at UCL, London WC1V 6LJ, UK." _n "{hline `maxwidth'}" } di as text "Type of trial" _col(`off') as res "`trialtype'" if `ni' { artformatnos, n(`test') maxlen(`longstring') } else { artformatnos, n(`test' `locmess') maxlen(`longstring') } local nlines=r(lines) forvalues i=1/`nlines' { if `i'==1 { di as text "Statistical test assumed" _col(`off') as res "`r(line`i')'" } else di as text _col(`off') as res " `r(line`i')'" } /* if `ni' { local vmethod `nvmethod`nvm'' di as text "Null variance estimation method" _col(`off') as res "`vmethod'" } */ di as text "Number of groups" _col(`off') as res "`ngroups'" di as text "Allocation ratio" _col(`off') as res "`allocr'" if `ngroups'>2 & "`trtest'"!="" { di as text "`trtest'" _col(`off') as res "`doses'" } di as text _n "Total number of periods" _col(`off') as res `nperiod' di as text "Length of each period" _col(`off') as res "`lperiod'" di if `median'>0 { if `median'!=1 { local ess s } di as text "Baseline median survival time" _col(`off') as res "`median' `lperiod2'`ess'" } if `sp' { local phrase Survival } else local phrase Cum. event forvalues i=1/`ngroups' { if `ni' { artformatnos, n(`edf1') maxlen(`longstring') } else { artformatnos, n(`edf`i'') maxlen(`longstring') } local nlines=r(lines) forvalues j=1/`nlines' { if `j'==1 { di as text "`phrase' probs per period (group `i')" _col(`off') as res "`r(line`j')'" } else di as text _col(`off') as res " `r(line`j')'" } } di as text "Number of recruitment periods" _col(`off') as res `R' di as text "Number of follow-up periods" _col(`off') as res `nperiod'-`R' tokenize "`recrt'", parse(",") local recm `5' if "`recm'"=="" { local recm 0 } if "`recm'"=="0" { di as text "Method of accrual" _col(`off') as res "Uniform" } else di as text "Method of accrual" _col(`off') as res "Exponential, parameter(s) = `recm'" if `R'>0 { * Format recruitment period weights artformatnos, n($recwt) maxlen(`longstring') local nlines=r(lines) forvalues i=1/`nlines' { if `i'==1 { di as text "Recruitment period-weights" _col(`off') as res "`r(line`i')'" } else di as text _col(`off') as res " `r(line`i')'" } } if `ni' { di as txt _n "Non-inf margin HR (grp 2 rel to grp 1)" _col(`off') as res "`hr21'" } else { di as text _n "Hazard ratios as entered `gplist'" _col(`off') as res "`hratios'" if `detail' { local phrase "Hazard ratios" forvalues i=1/`ngroups' { artformatnos, n(`hratios`i'') maxlen(`longstring') local nlines=r(lines) forvalues j=1/`nlines' { if `j'==1 { di as text "`phrase' per period (group `i')" _col(`off') as res "`r(line`j')'" } else di as text _col(`off') as res " `r(line`j')'" } } } } di as text "Alpha" _col(`off') %5.3f as res `Alpha' " (`sided'-sided)" if `ssize'==1 { di as text "Power (designed)" _col(`off') %5.3f as res `Power' * return scalar power=`Power' local mess (calculated) } if `ssize'==0 { di as text "Power (calculated)" _col(`off') %5.3f as res `power' * return scalar power=`power' local mess (designed) } di as text _n "Total sample size `mess'" _col(`off') as res `n' di as text "Expected total number of events" _col(`off') as res `D' di as text "{hline `maxwidth'}" // Compute numbers of events in each group local sumwt 0 local events forvalues i = 1 / `ngroups' { if "`aratios'" == "" local w`i' 1 else local w`i' : word `i' of `aratios' local sumwt = `sumwt' + `w`i'' } local ev1 = (`n' * `w1' / `sumwt') * `expev1' local events = ceil(`ev1') if `ni' { local ev2 = (`n' * `w2' / `sumwt') * `expev3' local eee = ceil(`ev2') local events `events', `eee' } else { forvalues i = 2 / `ngroups' { local ev`i' = (`n' * `w`i'' / `sumwt') * `expev`i'' local eee = ceil(`ev`i'') local events `events', `eee' } } if `detail' { di as text "Values given below apply to each group at the end of the trial" /// _n "{hline `maxwidth'}" di as text "Unadjusted event probs `gplist'" _col(`off') as res "`ep0'" di as text "Unadjusted loss to follow-up probs" _col(`off') as res "`lp0'" if ~`ni' di as text "Unadjusted cross-over probabilities" _col(`off') as res "`wp0'" di as text _n "*Expected numbers of events per group" _col(`off') as res "`events'" di as text "Expected proportions with event" _col(`off') as res "`ep1'" di as text "Expected proportions lost to follow-up" _col(`off') as res "`lp1'" if ~`ni' di as text "Expected proportions with cross-over" _col(`off') as res /// "`wp1'" _n as text "{hline `maxwidth'}" di as text "* Rounded to next whole number of events above the exact expected number" } if `"`using'"'!="" { postclose `tmp' * Transpose file using -reshape- use `"`using'"', replace quietly reshape wide `postvl', i(period) j(group) forvalues j=1/`ngroups' { label var surv`j' "group `j' survival probabilities" label var hr`j' "group `j' hazard ratios" } label var period "Periods of `lperiod'" save `"`using'"', replace } if ~`ni' return local expwdn `expwd' return local expltf `explf' foreach i of numlist `ngroups'(-1)1 { return scalar ev`i' = `ev`i'' } return local expev `expev' return local Events `events' return scalar events=`D' if `ssize'==1 { return scalar n=`n' } else { return scalar power=`power' } if `ssize'==1 { return scalar power=`Power' } else { return scalar n=`n' } return scalar alpha=`alpha' if ~`ni' return local wdlprob `wp0' return local ltfprob `lp0' return local evprob `ep0' return local recwt $recwt return local recper `R' return local allocr `allocr' return scalar ngroups = `ngroups' end * ***************************************************************************** program define _ar local ngroups "`1'" macro shift tempname sar qui gen double ar=. if "`1'"=="" { qui replace ar=1/`ngroups' local allocr "Equal group sizes" } else { scalar `sar'=0 local i 1 while `i'<=_N { if "`1'"!="" { confirm number `1' if `1'<=0 { noi di as err "Allocation ratio <=0 not alllowed" exit 198 } qui replace ar=`1' in `i' } else { qui replace ar=ar[`i'-1] in `i' } scalar `sar'=`sar'+ar[`i'] frac_ddp ar[`i'] 2 local allocr "`allocr'`co'`r(ddp)'" local i=`i'+1 local co ":" macro shift } qui replace ar=ar/`sar' } global AR "`allocr'" end * ***************************************************************************** program define _dose local ngroups "`1'" macro shift qui gen double dose=. if "`1'"=="" { qui replace dose=_n-1 local doses "1,...,`ngroups'" } else { local i 1 while `i'<=_N { if "`1'"!="" { confirm number `1' if `1'<0 { di as err "Dose < 0 not alllowed" exit 198 } qui replace dose=`1' in `i' } else qui replace dose=dose[`i'-1] in `i' frac_ddp dose[`i'] 2 local score "`score'`co'`r(ddp)'" local i=`i'+1 local co "," macro shift } local doses "`score'" local p=dose[1] replace dose=dose-`p' } global DOSE "`doses'" end * ***************************************************************************** program define _survf, sclass * Calculates survival function * !! PR: option "reverse(1)" allows input of survival probs; default reverse(0) is failures probs. sreturn clear syntax newvarlist(min=1 max=2), gr(string) cpr(string) np(string) EVent(string) [reverse(int 0)] local reterr "sreturn local err" tempname small scalar `small'=1e-16 gettoken sf mass0: varlist *!! PR gen double `sf'=. if "`mass0'"!="" { gen double `mass0'=. } local m 0 tokenize `gr' while "`1'"!="" { local ++m local g`m' "`1'" macro shift } tokenize "`cpr'", parse(";") forvalues i=1/`m' { local cpr`i' "`1'" macro shift 2 } forvalues l=1/`m' { if "`mass0'"!="" { replace `mass0'=0 if grp==`g`l'' } replace `sf'=1 if grp==`g`l'' tokenize "`cpr`l''",parse(",") if "`1'"==""|"`1'"=="," { `reterr' "`event' required" exit 499 } local cp "`1'" /* Cumulative probabilities */ local cpt "`2'" /* Cumulative probabilities times */ // Updated PR 24oct2006 to allow numlists for probabilities times if "`cpt'"=="," local cpt "`3'" if "`cpt'"=="" local cpt "1(1)`np'" * numlist "`cpt'", ascending integer range(>=1) numlist "`cpt'", ascending integer range(>=0) local cpt `r(numlist)' local k : word count `cp' local kt : word count `cpt' if `kt'<`k' { `reterr' "`event' times required" exit 499 } tempname s0 s1 s a scalar `s0'=1 scalar `s'=1 local t 0 forvalues i=1/`k' { local p: word `i' of `cp' if `reverse' { local oneminusp `p' local p=1-`p' } else local oneminusp=1-`p' if `oneminusp'>`s'|`p'>1 { `reterr' "Inappropriate `event'" exit 499 } local pt: word `i' of `cpt' if `pt'>=`small' { if `pt'<`t' { `reterr' "`event' times must be in increasing order" exit 499 } if abs(`pt'-int(`pt'))>`small' { `reterr' "`event' times cannot include fractions of periods" exit 499 } if `pt'>`np' { /* times beyond nperiod are valid! */ } } if `pt'<`small' { if "`mass0'"=="" { `reterr' "Probability mass at 0 not allowed" exit 499 } replace `sf'=1 if grp==`g`l'' replace `mass0'=`p' if grp==`g`l'' scalar `s0'=`oneminusp' } else { scalar `s1'=`oneminusp'/`s0' scalar `a'=`s1'/`s' /* by grp:replace `sf'=(`a')^((_n-`t')/(`pt'-`t')) if _n>`t' & grp==`g`l'' by grp:replace `sf'=`sf'*`s' if _n>`t' & grp==`g`l'' */ by grp:replace `sf'=`s'*(`a'^((_n-`t')/(`pt'-`t'))) if _n>`t' & grp==`g`l'' scalar `s'=`s1' local t "`pt'" } } } end * **************************************************************************** program define _condsrv args sf csf tempname small scalar `small'=1e-16 by grp:gen double `csf'=`sf' if _n==1 by grp:replace `csf'=1 if _n>1 & `sf'[_n-1]<`small' by grp:replace `csf'=`sf'/`sf'[_n-1] if _n>1 & `sf'[_n-1]>=`small' replace `csf'=`small' if `csf'<`small' end * **************************************************************************** program define _hr, sclass tempname small scalar `small'=1e-16 tokenize "`*'", parse(",") local ngroups "`1'" macro shift 2 gen double hr=1 local k 0 while "`1'"!="" & `k'<=`ngroups' { local ++k local h`k' "`1'" sreturn local hr`k' `1' macro shift 2 } forvalues l=1/`k' { tokenize `h`l'' local i 1 while "`1'"!="" { by grp:replace hr=`1' if _n==`i' & grp==`l' local r "`1'" local t "`i'" local ++i macro shift } by grp:replace hr=`r' if _n>`t'&grp==`l' } gen double lhr=log(hr) sort period grp egen double mlhr=sum(lhr),by(period) replace mlhr=mlhr/`k' replace lhr=mlhr if grp>`k' replace hr=exp(mlhr) if grp>`k' /* Verify groups do not have identical event time distribution */ sort period grp by period:replace lhr=abs(hr-hr[1]) summ lhr if abs(r(max))<`small' { noi di as err "Groups have identical event time distibution" exit 198 } drop lhr mlhr sort grp period replace cesf=cesf^hr gen double esf=cesf by grp:replace esf=esf*esf[_n-1] if _n>1 replace el=el*hr end * **************************************************************************** program define pwh syntax, ngroups(int) wg(string) [ crover(string) pwehr(string) ] sort period grp /* Start by setting HR for all groups except group 1 to design HR for group 1. For group 1, set its HR to that for group 2. */ by period:gen double defpwehr=hr[1] by period:replace defpwehr=hr[2] if grp==1 /* m is number of groups from wg() subject to withdrawal from allocated treatment */ local m 0 tokenize `wg' while "`1'"!="" { local ++m local wgrp`m' "`1'" macro shift } /* If crover() is specified but not pwehr(), design HR(s) from group crossed-over to are allocated to corresponding group from wg(). Example: wg(2 3) crover(1 2) => group 2 crosses over to HR from group 1, group 3 crosses over to HR from group 2. */ if "`pwehr'"==""&"`crover'"!="" { local k 0 tokenize `crover' while "`1'"!="" { local k=`k'+1 by period:replace pwehr=hr[`1'] if grp==`wgrp`k'' macro shift } } else { if "`pwehr'"!=""&"`crover'"!="" { noi dis in bl "Crossover destination (crover) ignored" } local k 0 tokenize "`pwehr'", parse(",") while "`1'"!=""&`k'<=`ngroups' { local ++k local h`k' "`1'" macro shift 2 } local l 1 while `l'<=min(`k',`m') { sort grp period tokenize `h`l'' local i 1 while "`1'"!="" { by grp:replace pwehr=`1' if _n==`i' & grp==`wgrp`l'' local r "`1'" local t "`i'" local ++i macro shift } by grp:replace pwehr=`r' if _n>`t' & grp==`wgrp`l'' local ++l } } * Default post withdrawal event hazard ratio if `k'<`m' { local l=`k'+1 while `l'<=`m' { if "`wgrp`l''"=="1" { local defpwed 2 } else local defpwed 1 noi di as text /* */ "Post withdrawal event hr in group `wgrp`l'' not specified." /* */ " Post withdrawal event time" _n "distribution is assumed" /* */ " to be the same as in group `defpwed'." replace pwehr=defpwehr if grp==`wgrp`l'' local ++l } } drop defpwehr sort grp period end * **************************************************************************** program define _wasdist /* Survival distribution adjusted for withdrawal (treatment change) at the abscissas generated by x={x1,...xk} for symmetric Guassian quadrature. ie the functions are calculated at z1i(=0.5*(1-xi)),z2i(=0.5*(1+xi)); i=1,...k */ syntax newvarlist(min=1 max=2), at(string) tempname small scalar `small'=1e-16 gettoken sf pdf: varlist gen double `sf'=. if "`pdf'"!="" { gen double `pdf'=. } tempvar safter * Survival fn if withdrawal occured at time=0 sort grp period gen double `safter'=cesf0^pwehr by grp:replace `safter'=`safter'*`safter'[_n-1] if _n>1 tempvar I J a a2 A B B2 tmp ea eb sa local N=_N expand `N' *d *noi di as err "expanding in _wasdist on N=`N', vars = " r(k) " resources = " r(k)*r(N) sort grp period qui by grp period:gen long `I'=_n /* !! PR edit */ sort grp `I' period gen double `a'=0 gen double `a2'=0 qui by grp `I':replace `a'=el0*pwehr[_n-`I'+1] if _n>=`I' qui by grp `I':replace `a2'=el0*pwehr[_n-`I'] if _n>`I' qui by grp `I':replace `a2'=el0 if _n==`I' qui by grp `I':gen double `A'=sum(`a')-`a' gen double `B'=0 qui by grp `I':replace `B'=el0*(pwehr[_n-`I']-pwehr[_n-`I'+1]) if _n>`I' qui by grp `I':replace `B'=el0*(1-pwehr[1]) if _n== `I' qui by grp `I':gen double `B2'=sum(`B')-`B' qui by grp `I':replace `B'=sum(`B') tempvar c wlI b1 b2 ehI qui by grp `I':gen double `wlI'=wl[`I'] qui by grp `I':gen double `ehI'=el0[`I']*(hr[`I']-1) qui by grp `I':gen double `c'=cond(`I'==1,1,esf[`I'-1]*wsf[`I'-1]) replace `B'=`B'+`wlI'+`ehI' replace `B2'=`B2'+`wlI'+`ehI' gen double `b1'=cond(`wlI'<`small',0,`wlI'/`B') gen double `b2'=cond(`wlI'<`small',0,`wlI'/`B2') if "`at'"=="" { local at 1 } local mx : word count `at' if `mx'==1 { gen int IX=1 } else { expand `mx' sort grp period `I' by grp period `I':gen long IX=_n } gen double x=1 expand 2 sort grp period `I' IX local by "by grp period `I' IX" `by':gen long JX=_n /* !! PR edit */ tokenize `at' forvalues i=1/`mx' { `by':replace x=cond(_n==1,0.5*(1-`1'),0.5*(1+`1')) if IX==`i' macro shift } sort IX JX grp period `I' local by "by IX JX grp" tempvar L L2 gen double `L'=`b1'*(exp(-`a'*x)-exp(-(`a'+`B')*x)) qui `by' period:replace `L'=0 if _n>period gen double `L2'=`b2'*(exp(-(`a2'+`B2')*x)-exp(-`B2')*exp(-`a2'*x)) qui `by' period:replace `L2'=0 if _n>=period replace `L'=`L'+`L2' qui `by' period:replace `sf'=sum(`c'*exp(-`A')*`L') if "`pdf'"!="" { replace `L'=`b1'*(`a'*exp(-`a'*x)-(`a'+`B')*exp(-(`a'+`B')*x)) qui `by' period:replace `L'=0 if _n>period replace `L2'=`b2'*((`a2'+`B2')*exp(-(`a2'+`B2')*x)-exp(-`B2')*`a2'*exp(-`a2'*x)) qui `by' period:replace `L2'=0 if _n>=period replace `L'=`L'+`L2' qui `by' period:replace `pdf'=sum(`c'*exp(-`A')*`L') } qui `by' period:keep if _n==_N sort IX JX grp period `by':replace `safter'=exp(-el0*pwehr*x)*cond(_n==1,1,`safter'[_n-1]) `by':replace `c'=cond(_n==1,1,esf[_n-1]*wsf[_n-1]) replace `sf'=`sf'+`c'*exp(-(el+wl)*x) replace `sf'=(1-wmass0)*`sf'+wmass0*`safter' if "`pdf'"!="" { replace `pdf'=`pdf'+`c'*(el+wl)*exp(-(el+wl)*x) replace `pdf'=(1-wmass0)*`pdf'+wmass0*el0*pwehr*`safter' } sort grp period IX JX end * ***************************************************************************** program define _subdf * Subdistribution functions for failure and loss to followup. syntax newvarlist(min=1 max=3)[, at(real 1)] tokenize `varlist' local esdf `1' local lsdf `2' local wsdf `3' gen double `esdf'=. if "`lsdf'"!="" { gen double `lsdf'=. } if "`wsdf'"!="" { gen double `wsdf'=. } tempname small x scalar `small'=1e-16 scalar `x'=`at' tempvar safter acir * Survival fn if withdrawal occured at time=0 sort grp period gen double `safter'=cesf0^pwehr by grp:replace `safter'=`safter'*`safter'[_n-1] if _n>1 tempvar I J a a2 A B B2 c wlI b1 b2 ehI l EP011 EP012 EP021 EP022 EP111 EP121 pL pJ pI compress local N=_N expand `N' *d *noi di as err "expanding in _subdf on N=`N', vars = " r(k) " resources = " r(k)*r(N) sort grp period qui by grp period:gen long `I'=_n sort grp `I' period gen double `a'=0 gen double `a2'=0 qui by grp `I':replace `a'=el0*pwehr[_n-`I'+1] if _n>=`I' qui by grp `I':replace `a2'=el0*pwehr[_n-`I'] if _n>`I' qui by grp `I':replace `a2'=el0 if _n==`I' qui by grp `I':gen double `A'=sum(`a')-`a' gen double `B'=0 qui by grp `I':replace `B'=el0*(pwehr[_n-`I']-pwehr[_n-`I'+1]) if _n>`I' qui by grp `I':replace `B'=el0*(1-pwehr[1]) if _n== `I' qui by grp `I':gen double `B2'=sum(`B')-`B' qui by grp `I':replace `B'=sum(`B') qui by grp `I':gen double `wlI'=wl[`I'] qui by grp `I':gen double `ehI'=el0[`I']*(hr[`I']-1) qui by grp `I':gen double `c'=cond(`I'==1,1,esf[`I'-1]*wsf[`I'-1]) replace `B'=`B'+`wlI'+`ehI' replace `B2'=`B2'+`wlI'+`ehI' gen double `b1'=cond(`wlI'<`small',0,`wlI'/`B') gen double `b2'=cond(`wlI'<`small',0,`wlI'/`B2') sort grp period `I' local alpha 1 gen double `l'=ll local beta 0 while `beta'<=1 { local i 1 while `i'<=2 { if `i'==1 { local y 1 } else local y=`x' local j 1 while `j'==1|`j'==2 & `beta'==0 { if `j'==1 { replace `l'=ll } else replace `l'=ll-inir _intgrlf `pJ' `alpha' `beta' `y' `l' `a' _intgrlf `pI' `alpha' `beta' `y' `l' `a' `B' replace `pJ'=`b1'*(`pJ'-`pI') qui by grp period:replace `pJ'=0 if _n>period _intgrlf `pL' `alpha' `beta' `y' `l' `a2' `B2' _intgrlf `pI' `alpha' `beta' `y' `l' `a2' replace `pL'=`b2'*(`pL'-exp(-`B2')*`pI') qui by grp period:replace `pL'=0 if _n>=period replace `pL'=`pL'+`pJ' qui by grp period:gen double `EP`beta'`i'`j''= /* */ sum(`c'*exp(-`A')*`pL') local j=`j'+1 } local i=`i'+1 } local beta=`beta'+1 } if "`lsdf'"!="" { tempvar l LP011 LP012 LP021 LP022 LP111 LP121 pL pJ pI if "`wsdf'"!="" { tempvar WP011 WP012 WP021 WP022 WP111 WP121 } local alpha 0 gen double `l'=ll local beta 0 while `beta'<=1 { local i 1 while `i'<=2 { if `i'==1 { local y 1 } else local y=`x' local j 1 while `j'==1|`j'==2&`beta'==0 { if `j'==1 { replace `l'=ll } else replace `l'=ll-inir _intgrlf `pJ' `alpha' `beta' `y' `l' `a' _intgrlf `pI' `alpha' `beta' `y' `l' `a' `B' replace `pJ'=`b1'*(`pJ'-`pI') qui by grp period:replace `pJ'=0 if _n>period _intgrlf `pL' `alpha' `beta' `y' `l' `a2' `B2' _intgrlf `pI' `alpha' `beta' `y' `l' `a2' replace `pL'=`b2'*(`pL'-exp(-`B2')*`pI') qui by grp period:replace `pL'=0 if _n>=period replace `pL'=`pL'+`pJ' qui by grp period:gen double `LP`beta'`i'`j''= /* */ sum(`c'*exp(-`A')*`pL') local j=`j'+1 } local i=`i'+1 } local beta=`beta'+1 } } qui by grp period:keep if _n==_N sort grp period qui by grp:replace `c'=cond(_n==1,1,esf[_n-1]*wsf[_n-1]) qui by grp:replace `pL'=cond(_n==1,1,`safter'[_n-1]) tempvar A gen double `A'=el0*pwehr local alpha 1 local beta 0 while `beta'<=1 { local i 1 while `i'<=2 { if `i'==1 { local y 1 } else local y=`x' local j 1 while `j'==1|`j'==2&`beta'==0 { if `j'==1 { replace `l'=ll } else replace `l'=ll-inir _intgrlf `pI' `alpha' `beta' `y' `l' el wl replace `EP`beta'`i'`j''=`EP`beta'`i'`j''+`c'*`pI' _intgrlf `pI' `alpha' `beta' `y' `l' `A' replace `EP`beta'`i'`j''=(1-wmass0)*`EP`beta'`i'`j''+ /* */ wmass0*`pL'*`pI' local j=`j'+1 } local i=`i'+1 } local beta=`beta'+1 } tempvar P F by grp:gen double `P'=in0*`EP011' by grp:gen double `F'=(1-$initrec)*inwt replace `P'=`P'-(`EP012'-`EP011')*`F'/(exp(inir)-1) if inir!=0 replace `P'=`P'-`EP111'*`F' if inir==0 by grp:replace `P'=`P'*cond(_n==1,1,lsf[_n-1]) by grp:replace `esdf'=sum(`P')-`P' by grp:replace `P'=in0*`EP021' replace `P'=`P'-(`EP022'-`EP021')*`F'/(exp(inir)-1) if inir!=0 replace `P'=`P'-`EP121'*`F' if inir==0 by grp:replace `P'=`P'*cond(_n==1,1,lsf[_n-1]) replace `esdf'=`esdf'+`P' if "`lsdf'"!="" { local alpha 0 local beta 0 while `beta'<=1 { local i 1 while `i'<=2 { if `i'==1 { local y 1 } else local y=`x' local j 1 while `j'==1 |`j'==2 & `beta'==0 { if `j'==1 { replace `l'=ll } else replace `l'=ll-inir _intgrlf `pI' `alpha' `beta' `y' `l' el wl if "`wsdf'"!="" { gen double `WP`beta'`i'`j''=`pI' } replace `LP`beta'`i'`j''=`LP`beta'`i'`j''+`c'*`pI' qui by grp:replace `pL'=cond(_n==1,1,`safter'[_n-1]) _intgrlf `pI' `alpha' `beta' `y' `l' `A' replace `LP`beta'`i'`j''=(1-wmass0)*`LP`beta'`i'`j''+ wmass0*`pL'*`pI' local j=`j'+1 } local i=`i'+1 } local beta=`beta'+1 } tempvar P F by grp:gen double `P'=in0*`LP011' by grp:gen double `F'=(1-$initrec)*inwt replace `P'=`P'-(`LP012'-`LP011')*`F'/(exp(inir)-1) /* */ if inir!=0 replace `P'=`P'-`LP111'*`F' if inir==0 by grp:replace `P'=`P'*ll*cond(_n==1,1,lsf[_n-1]) by grp:replace `lsdf'=sum(`P')-`P' by grp:replace `P'=in0*`LP021' replace `P'=`P'-(`LP022'-`LP021')*`F'/(exp(inir)-1) /* */ if inir!=0 replace `P'=`P'-`LP121'*`F' if inir==0 by grp:replace `P'=`P'*ll*cond(_n==1,1,lsf[_n-1]) replace `lsdf'=`lsdf'+`P' if "`wsdf'"!="" { tempvar P F H by grp:gen double `P'=in0*`WP011' by grp:gen double `F'=(1-$initrec)*inwt replace `P'=`P'-(`WP012'-`WP011')*`F'/(exp(inir)-1) /* */ if inir!=0 replace `P'=`P'-`WP111'*`F' if inir==0 gen double `H'=lsf*esf*wsf by grp:replace `P'=`P'*wl*cond(_n==1,1,`H'[_n-1]) by grp:replace `wsdf'=sum(`P')-`P' by grp:replace `P'=in0*`WP021' replace `P'=`P'-(`WP022'-`WP021')*`F'/(exp(inir)-1) /* */ if inir!=0 replace `P'=`P'-`WP121'*`F' if inir==0 by grp:replace `P'=`P'*wl*cond(_n==1,1,`H'[_n-1]) replace `wsdf'=`wsdf'+`P' replace `wsdf'=(1-wmass0)*`wsdf'+wmass0 } } end * ---------------------------------------------------------------------------- program define _intgrlf /* _intgrlf R a b x mu varlist: calculates (sum(varlist))^a*{integral from 0 to x of (y^b)*exp(-(sum(varlist+mu)*y))dy }; a=0 or 1; b=0 or 1 */ local result "`1'" local a "`2'" local b "`3'" local x "`4'" local mu "`5'" macro shift 5 tempvar sv c gen double `sv'=0 while "`1'"!="" { replace `sv'=`sv'+`1' macro shift } local small 1e-16 gen double `c'=`sv'+`mu' if `a'==0 { replace `sv'=1 } capture drop `result' gen double `result'=`sv'*(1-exp(-`c'*`x'))/`c' replace `result'=`sv'*`x' if `c'<`small' if `b'==1 { replace `result'=(`result'-`sv'*`x'*exp(-`c'*`x'))/`c' replace `result'=`sv'*`x'*`x'/2 if `c'<`small' } end * ***************************************************************************** program define _recd * Setup recruitment time distribution tempname small scalar `small'=1e-16 syntax, RECrt(string) [np(integer 1)] if "`recrt'"=="" { local R 0 /* Length of recruitment period */ gen double recwt=0 /* Interval weight for recruitment */ } else { tokenize "`recrt'", parse(",") local R1 "`1'" if "`R1'"=="," { local R1 "`np' 0" } else macro shift macro shift if "`1'"=="" { local recwt 1 /* Interval weight for recruitment */ local recir 0 /* Rec time dist shape within intervals: 0=uniform; >0=truncated exponential */ } else { local recwt "`1'" if "`1'"=="," { local recwt 1 } else macro shift macro shift local recir "`1'" if "`1'"=="" { local recir 0 } } tokenize `R1' local R "`1'" confirm number `R' if "`2'"!="" { local init "`2'" /* Initial recruitment at time 0 */ confirm number `init' } else local init 0 gen double recwt=0 * screen for "*" notation and expand if used. Save wts as global macro recwt. global recwt local nrecwt=wordcount("`recwt'") local nwt 0 local done 0 forvalues i=1/`nrecwt' { if `done'==0 { local this: word `i' of `recwt' tokenize `this', parse("*") if "`2'"=="*" { confirm integer number `1' local toadd `1' local last `3' } else { local toadd 1 local last `this' } confirm number `last' local nwt1=`nwt'+`toadd' if `nwt1'>`R' & `R'>0 { noi di as err "[too many recruitment period-weights specified, truncated at `R']" local nwt1 `R' local toadd=`R'-`nwt' local done 1 } forvalues j=1/`toadd' { global recwt $recwt `last' } local nwt `nwt1' } } * If fewer weights than recruitment periods are specified, fill with last value if `nwt'<`R' { local i1=`nwt'+1 forvalues i=`i1'/`R' { global recwt $recwt `last' } } local recwt $recwt * If length of recruitment is less than number of periods, fill with zero if `R'<`np' { local i1=`R'+1 forvalues i=`i1'/`np' { global recwt $recwt 0 } } tokenize `recwt' local i 1 local last "recwt" /* In case `R'=0 */ while `i'<=`R' & "`1'"!="" { confirm number `1' by grp:replace recwt=`1' if _n==`i' local ++i local last "`1'" macro shift } by grp:replace recwt=`last' if _n>=`i' & _n<=`R' gen double recir=0 tokenize `recir' local i 1 local last "recir" /* In case `R'=0 */ while `i'<= `R' & "`1'"!="" { confirm number `1' by grp:replace recir=`1' if _n==`i' local ++i local last "`1'" macro shift } by grp:replace recir=`last' if _n>=`i'&_n<=`R' } tempvar srecwt by grp:gen double `srecwt'=sum(recwt) local sc=`srecwt'[_N] /* Sum of wts in last grp. Same for all groups */ if `R'<`small'|`sc'<`small' { local init 1 capture drop recir gen double recir=0 gen double recdf=1 } else { replace recwt=recwt/`sc' by grp:gen double recdf=`init'+(1-`init')*`srecwt'/`sc' if _n<=`R' by grp:replace recdf=1 if _n>=`R' } global initrec=`init' global recper=`R' end * **************************************************************************** program define _muv * Calculate mean and covariance matrix of logrank O-E args mu V0 V1 ng test index tempname small scalar `small'=1e-16 * Guassian quadratures local NQ "5" local XQ "0.1488743384 0.4333953941 0.6794095662 0.8650633666 0.9739065285" local WQ "0.2955242247 0.2692667193 0.2190863625 0.1494513491 0.0666713443" by grp:gen double tmp=cond(_n==1,1,lsf[_n-1]) _wasdist sf pdf,at(`XQ') gen double r1=pdf/sf sort x period grp qui by x period:gen double hr1=r1/r1[1] sort grp period IX JX gen double w=1 tokenize `WQ' forvalues i=1/`NQ' { replace w=`1' if IX==`i' macro shift } replace lsf=tmp*exp(-ll*x) replace tmp=(1-$initrec)*inwt gen double instudy=in0-tmp*x if abs(inir)<=`small' replace instudy=in0-tmp*(exp(inir*x)-1)/(exp(inir)-1) if abs(inir)>`small' * Logrank weight if `test'==2 { egen double lrwt=sum(ar*lsf*sf),by(x period) replace lrwt=sqrt(instudy*lrwt) } else if `test'==3 { egen double lrwt=sum(ar*lsf*sf),by(x period) drop tmp egen double tmp=sum(ar*lsf),by(x period) replace lrwt=(lrwt/tmp)^`index' } else gen double lrwt=1 * hr-weighted proportion at risk under null and alternative hypothesis egen double atrisk0=sum(ar*lsf*sf),by(x period) replace atrisk0=ar*lsf*sf/atrisk0 egen double atrisk1=sum(ar*lsf*sf*hr1),by(x period) replace atrisk1=ar*lsf*sf*hr1/atrisk1 * Event sub-pdf egen double subpdf=sum(ar*lsf*pdf),by(x period) replace subpdf=instudy*subpdf * (Test statistic mean)/(Total sample size) egen double mu=sum(w*lrwt*(atrisk1-atrisk0)*subpdf),by(grp) replace mu=mu/2 * Variance of (Test statistic)/sqrt(Total sample size) expand `ng' sort x period grp by x period grp:gen int K=_n sort x period K grp by x period K:gen double atriskK0=atrisk0[K] by x period K:gen double atriskK1=atrisk1[K] egen double V0=sum(w*lrwt^2*atriskK0*((grp==K)-atrisk0)*subpdf),by(K grp) replace V0=V0/2 egen double V1=sum(w*lrwt^2*atriskK1*((grp==K)-atrisk1)*subpdf),by(K grp) replace V1=V1/2 sort K grp period by K grp:keep if _n==_N keep grp K mu V0 V1 ar esubdf lsubdf wsubdf * Update reshape to version 7 and later /* reshape gr K 1-`ng' reshape vars mu V0 V1 reshape cons grp ar esubdf lsubdf wsubdf reshape wide */ reshape wide mu V0 V1, i(grp ar esubdf lsubdf wsubdf) j(K) sort grp forvalues i=2/`ng' { local v0 "`v0' V0`i'" local v1 "`v1' V1`i'" } mkmat mu1 if grp>1,matrix(`mu') mkmat `v0' if grp>1,matrix(`V0') mkmat `v1' if grp>1,matrix(`V1') keep grp ar esubdf lsubdf wsubdf end * ***************************************************************************** program define _pe2 * Calculate beta=P(type II error) args a0 q0 a1 q1 k n a b tempname b0 b1 f l scalar `b0'=`a0'+`n'*`q0' scalar `b1'=`a1'+2*`n'*`q1' scalar `l'=`b0'^2-`k'*`b1' scalar `f'=sqrt(`l'*(`l'+`k'*`b1')) scalar `l'=(`l'+`f')/`b1' scalar `f'=`a'*(`k'+`l')/`b0' scalar `b'=nchi2(`k',`l',`f') end