/* 10.0 AMC 29sep2019 */ program strmst2, rclass byable(recall) version 10 * confirm that the data are -stset- if "`_dta[_dta]'"!="st" { disp as error "data not st" exit 119 } syntax varlist(max=1 numeric) [if] [in], [tau(numlist > 0 max=1 miss) level(cilevel) /// covariates(varlist min=1 numeric) reference(numlist >= 0 max=1) rmtl] /* mark sample to use in the analysis */ marksample touse local completevars _t _d `varlist' `covariates' markout `touse' `completevars' quietly count if `touse'==1 di " " di as input "Number of observations for analysis = " r(N) global arm = "`varlist'" global level = `level' global tau = "`tau'" global covariates = "`covariates'" global time = "_t" global fail = "_d" global reference = "`reference'" /* determine unique value of the arm variable */ quietly tab $arm, matrow(matname) global narm = rowsof(matname) * confirm that supplied reference is one of the applicable values if "$reference"~="" { local okref=0 forvalues i=1(1)$narm { if $reference==el(matname,`i',1) { local okref=1 } } if `okref'==0 { disp as error "error: none of the subjects are in the group with the supplied reference value" exit 198 } } * assign reference arm (if not supplied) if "$reference"=="" { global reference = el(matname,1,1) } * list of arms in ascending order local armnumlist = "" forvalues i=1(1)$narm { local armnumlist = "`armnumlist'" + " " + string(el(matname,`i',1)) } * list of arms in descending order local armnumlistrev = "" forvalues i=$narm(-1)1 { local armnumlistrev = "`armnumlistrev'" + " " + string(el(matname,`i',1)) } * list of arms in descending order, omitting the reference category local armnumlistrev_noref = subinstr("`armnumlistrev'","$reference","",.) /* assign default tau */ local defaulttau = . foreach i of numlist `armnumlist' { quietly sum $time if $fail==1 & $arm==`i' & `touse'==1 local defaulttau = min(r(max), `defaulttau') } /* assign maximum tau */ local maxtau = . foreach i of numlist `armnumlist' { quietly sum $time if $arm==`i' & `touse'==1 local maxtau = min(r(max), `maxtau') } local maxtauf = string(`maxtau', "%9.3f") disp " " if "$tau" == "" { global tau2 = `defaulttau' local tau2f = string($tau2, "%9.3f") disp as input "The truncation time, tau, was not specified. Thus, the default tau (the minimum of the" disp as input "largest observed event time within each group), `tau2f', is used." global tau = `defaulttau' } else { if $tau > `maxtau' { disp as error "error: The truncation time, tau, needs to be shorter than or equal to the minimum of the" disp as error "largest observed time within each of the groups: `maxtauf'" exit 198 } else { if $tau > `defaulttau' { global tau2 = $tau disp in yellow "The truncation time: tau = $tau was specified, but there are no observed events after" disp in yellow "tau = $tau in at least one group. Make sure that the size of riskset at tau = $tau is" disp in yellow "large enough in each group." } else { global tau2 = $tau disp as input "The truncation time: tau = $tau was specified." } } } *-------------unadjusted analysis if "$covariates"=="" { foreach i of numlist `armnumlist' { preserve quietly keep if $arm==`i' & `touse'==1 rmst1 matrix rmst`i' = r(outrmst) matrix rmtl`i' = r(outrmtl) matrix rmst1out`i'=r(output) scalar rmstvar`i' = r(rmstvar) restore } *-------------contrast 1 (RMST difference) --- foreach i of numlist `armnumlistrev_noref' { scalar rmstdiff10 = el(rmst`i',1,1) - el(rmst$reference,1,1) scalar rmstdiff10se = sqrt(rmstvar`i' + rmstvar$reference) scalar rmstdiff10low = rmstdiff10 - invnorm(1 - (100-$level)/2/100)*rmstdiff10se scalar rmstdiff10upp = rmstdiff10 + invnorm(1 - (100-$level)/2/100)*rmstdiff10se scalar rmstdiffpval = (1 - normprob(abs(rmstdiff10 / rmstdiff10se)))*2 matrix rmstdiffresult`i' = (rmstdiff10, rmstdiff10low, rmstdiff10upp, rmstdiffpval) } *-------------contrast 2 (RMST ratio) --- foreach i of numlist `armnumlistrev_noref' { scalar rmstlogratio10 = log(el(rmst`i',1,1)) - log(el(rmst$reference,1,1)) scalar rmstlogratio10se = sqrt(rmstvar`i'/el(rmst`i',1,1)/el(rmst`i',1,1) + rmstvar$reference/el(rmst$reference,1,1)/el(rmst$reference,1,1)) scalar rmstlogratio10low = rmstlogratio10 - invnorm(1 - (100-$level)/2/100)*rmstlogratio10se scalar rmstlogratio10upp = rmstlogratio10 + invnorm(1 - (100-$level)/2/100)*rmstlogratio10se scalar rmstlogratiopval = (1 - normprob(abs(rmstlogratio10 / rmstlogratio10se)))*2 matrix rmstratioresult`i' = (exp(rmstlogratio10), exp(rmstlogratio10low), exp(rmstlogratio10upp), rmstlogratiopval) } *-------------contrast 3 (RMTL ratio) --- foreach i of numlist `armnumlistrev_noref' { scalar rmtllogratio10 = log(el(rmtl`i',1,1)) - log(el(rmtl$reference,1,1)) scalar rmtllogratio10se = sqrt(rmstvar`i'/el(rmtl`i',1,1)/el(rmtl`i',1,1) + rmstvar$reference/el(rmtl$reference,1,1)/el(rmtl$reference,1,1)) scalar rmtllogratio10low = rmtllogratio10 - invnorm(1 - (100-$level)/2/100)*rmtllogratio10se scalar rmtllogratio10upp = rmtllogratio10 + invnorm(1 - (100-$level)/2/100)*rmtllogratio10se scalar rmtllogratiopval = (1 - normprob(abs(rmtllogratio10 / rmtllogratio10se)))*2 matrix rmtlratioresult`i' = (exp(rmtllogratio10), exp(rmtllogratio10low), exp(rmtllogratio10upp), rmtllogratiopval) } *----combine results --- foreach i of numlist `armnumlistrev_noref' { matrix outunadj`i'=(rmstdiffresult`i' \ rmstratioresult`i' \ rmtlratioresult`i' ) matrix rownames outunadj`i' = RMSTdiff RMSTratio RMTLratio matrix colnames outunadj`i' = Estimate Lower$level% Upper$level% P } *------display RMST results * header di _n in gr "Restricted Mean Survival Time (RMST) by arm" di in smcl in gr "{hline 9}{c TT}{hline 49}" _n " Group" _col(10) "{c |}" /// _col(13) "Estimate" _col(25) "Std. Err." _col(40) "[$level% Conf. Interval]" di in smcl in gr "{hline 9}{c +}{hline 49}" foreach i of numlist `armnumlistrev' { di in smcl in gr %8s "arm `i'" " {c |}" in ye /// _col(14) %7.3f el(rmst1out`i',1,1) /// _col(24) %9.3f el(rmst1out`i',1,2) /// _col(37) %9.3f el(rmst1out`i',1,3) /// _col(50) %9.3f el(rmst1out`i',1,4) } * trailer di in smcl in gr "{hline 9}{c BT}{hline 49}" *------display RMTL results if "`rmtl'"=="rmtl" { * header di _n in gr "Restricted Mean Time Lost (RMTL) by arm" di in smcl in gr "{hline 9}{c TT}{hline 49}" _n " Group" _col(10) "{c |}" /// _col(13) "Estimate" _col(25) "Std. Err." _col(40) "[$level% Conf. Interval]" di in smcl in gr "{hline 9}{c +}{hline 49}" foreach i of numlist `armnumlistrev' { di in smcl in gr %8s "arm `i'" " {c |}" in ye /// _col(14) %7.3f el(rmst1out`i',2,1) /// _col(24) %9.3f el(rmst1out`i',2,2) /// _col(37) %9.3f el(rmst1out`i',2,3) /// _col(50) %9.3f el(rmst1out`i',2,4) } * trailer di in smcl in gr "{hline 9}{c BT}{hline 49}" } *------display between group contrasts foreach i of numlist `armnumlistrev_noref' { * header di _n in gr "Between-group contrast (arm `i' versus arm $reference) " di in smcl in gr "{hline 21}{c TT}{hline 50}" _n " Contrast" _col(22) "{c |}" /// _col(25) "Estimate" _col(40) "[$level% Conf. Interval]" _col(65) "P>|z|" di in smcl in gr "{hline 21}{c +}{hline 50}" * difference in RMST di in smcl in gr %8s "RMST (arm `i' - arm $reference)" " {c |}" in ye /// _col(26) %7.3f el(outunadj`i',1,1) /// _col(37) %9.3f el(outunadj`i',1,2) /// _col(50) %9.3f el(outunadj`i',1,3) /// _col(61) %9.3f el(outunadj`i',1,4) * Ratio of RMST di in smcl in gr %8s "RMST (arm `i' / arm $reference)" " {c |}" in ye /// _col(26) %7.3f el(outunadj`i',2,1) /// _col(37) %9.3f el(outunadj`i',2,2) /// _col(50) %9.3f el(outunadj`i',2,3) /// _col(61) %9.3f el(outunadj`i',2,4) if "`rmtl'"=="rmtl" { * Ratio of RMTL di in smcl in gr %8s "RMTL (arm `i' / arm $reference)" " {c |}" in ye /// _col(26) %7.3f el(outunadj`i',3,1) /// _col(37) %9.3f el(outunadj`i',3,2) /// _col(50) %9.3f el(outunadj`i',3,3) /// _col(61) %9.3f el(outunadj`i',3,4) } * trailer di in smcl in gr "{hline 21}{c BT}{hline 50}" } *--------return results --- foreach i of numlist `armnumlistrev' { return matrix rmstarm`i'=rmst1out`i' return scalar rmstvar`i'=rmstvar`i' } foreach i of numlist `armnumlistrev_noref' { return matrix unadjustedresult`i' = outunadj`i' } return scalar tau = $tau return scalar reference = $reference } *-------------adjusted analysis if "$covariates"~="" { * list in arms in ascending order, excluding the referent global armnumlist_noref = subinstr("`armnumlist'","$reference","",.) global armlist = "" * create dummy variables for each level of the treatment variable local armtext = "$arm" capture drop _I`armtext'* foreach num of numlist $armnumlist_noref { quietly g _I`armtext'_`num' = $arm==`num' label var _I`armtext'_`num' "$arm==`num'" global armlist = "$armlist " + "_I`armtext'_`num'" } global armnumlist = "`armnumlist'" noisily disp in white "Note: adjusted analysis may take a few minutes to run..." rmst2regRUN `touse' local esttype rmtlratio rmstratio rmstdiff foreach e of local esttype { matrix `e' = r(`e'adj) matrix `e'cov = r(`e'cov) } *----combine results --- matrix combinede = (rmstdiff[2..2,1..1] \ rmstratio[2..2,5..5] \ rmtlratio[2..2,5..5] ) matrix combinedl = (rmstdiff[2..2,5..5] \ rmstratio[2..2,6..6] \ rmtlratio[2..2,6..6] ) matrix combinedu = (rmstdiff[2..2,6..6] \ rmstratio[2..2,7..7] \ rmtlratio[2..2,7..7] ) matrix combinedp = (rmstdiff[2..2,4..4] \ rmstratio[2..2,4..4] \ rmtlratio[2..2,4..4] ) matrix outadj=combinede, combinedl, combinedu, combinedp matrix rownames outadj = RMSTdiff RMSTratio RMTLratio matrix colnames outadj = Estimate Lower$level% Upper$level% P *------display RMST difference results * header di _n in gr "Model summary (difference of RMST)" di in smcl in gr "{hline 15}{c TT}{hline 60}" _n " " _col(16) "{c |}" /// _col(20) "Coef." _col(27) "Std. Err." _col(41) "z" _col(46) "P>|z|" _col(54) "[$level% Conf. Interval]" di in smcl in gr "{hline 15}{c +}{hline 60}" * list out parameter estimates local npar=rowsof(beta0) forvalues p=1(1)`npar' { di in smcl in gr %11s word("$covariates2",`p') _col(15) " {c |}" in ye /// _col(18) %7.3f el(rmstdiff,`p',1) /// _col(26) %9.3f el(rmstdiff,`p',2) /// _col(38) %5.2f el(rmstdiff,`p',3) /// _col(46) %5.3f el(rmstdiff,`p',4) /// _col(54) %7.3f el(rmstdiff,`p',5) /// _col(66) %7.3f el(rmstdiff,`p',6) } * trailer di in smcl in gr "{hline 15}{c BT}{hline 60}" *------display RMST and RMTL ratio results if "`rmtl'"=="rmtl" { local esttypes rmstratio rmtlratio } else { local esttypes rmstratio } foreach e of local esttypes { * header if "`e'"=="rmstratio" { di _n in gr "Model summary (ratio of RMST)" } if "`e'"=="rmtlratio" { di _n in gr "Model summary (ratio of time-lost)" } di in smcl in gr "{hline 15}{c TT}{hline 72}" _n " " _col(16) "{c |}" /// _col(20) "Coef." _col(27) "Std. Err." _col(41) "z" _col(46) "P>|z|" /// _col(54) "exp(Coef.)" _col(66) "[$level% Conf. Interval]" di in smcl in gr "{hline 15}{c +}{hline 72}" * list out parameter estimates local npar=rowsof(beta0) forvalues p=1(1)`npar' { di in smcl in gr %11s word("$covariates2",`p') _col(15) " {c |}" in ye /// _col(18) %7.3f el(`e',`p',1) /// _col(26) %9.3f el(`e',`p',2) /// _col(38) %5.2f el(`e',`p',3) /// _col(46) %5.3f el(`e',`p',4) /// _col(56) %7.3f el(`e',`p',5) /// _col(65) %7.3f el(`e',`p',6) /// _col(78) %7.3f el(`e',`p',7) } * trailer di in smcl in gr "{hline 15}{c BT}{hline 72}" } *----return results local esttype rmtlratio rmstratio rmstdiff foreach e of local esttype { return matrix `e'adj = `e' return matrix `e'cov = `e'cov } return scalar tau = $tau return scalar reference = $reference } end *----------------------------------------------------- *--------rmst1 (one-arm)--hidden *----------------------------------------------------- program rmst1, rclass quietly { sts gen surv=s nrisk=n nevent=d keep _t surv nrisk nevent duplicates drop keep if _t < $tau sort _t count local newobs = r(N) + 1 set obs `newobs' replace _t = $tau in `newobs' g timediff = _t in 1 replace timediff = _t - _t[_n-1] if timediff==. g areas = timediff in 1 replace areas = timediff*surv[_n-1] if areas==. sum areas scalar rmst = r(sum) g wkvar = 0 replace wkvar = nevent / (nrisk * (nrisk - nevent)) if nevent<. g revareas = areas[`newobs' - _n + 1] if _n < `newobs' g revwkvar = wkvar[`newobs' - _n ] if _n < `newobs' g temp1 = sum(revareas)^2 *revwkvar sum temp1 scalar rmstvar = r(sum) scalar rmstse = sqrt(rmstvar) matrix outrmst = (rmst, rmstse, rmst - invnorm(1 - (100-$level)/2/100)*rmstse, rmst + invnorm(1 - (100-$level)/2/100)*rmstse) matrix rownames outrmst = rmst matrix colnames outrmst = Estimate StdErr Lower$level% Upper$level% matrix outrmtl = ($tau - rmst, rmstse, $tau - rmst - invnorm(1 - (100-$level)/2/100)*rmstse, $tau - rmst + invnorm(1 - (100-$level)/2/100)*rmstse) matrix colnames outrmtl = Estimate StdErr Lower$level% Upper$level% matrix rownames outrmtl = rmtl matrix output = (outrmst \ outrmtl) return matrix outrmst = outrmst return matrix outrmtl = outrmtl return matrix output = output return scalar tau = $tau return scalar rmstvar=rmstvar } end *----------------------------------------------------- *--------rmst2reg (Lu)--hidden *----------------------------------------------------- * there is one item passed to the program: rmstdiff, rmstratio, or rmtlratio program rmst2reg, rclass quietly { global esttype = "`0'" g intercept = 1 global covariates2 = "intercept $armlist $covariates" local npar = wordcount("$covariates2") * create indicator for observed time after tau g after = $time >= $tau g y0 = min($tau, $time) g d0 = $fail replace d0=1 if after==1 foreach i of numlist $armnumlist { quietly stset y0 if $arm==`i', f(d0=0) quietly sts gen surv`i'=s if $arm==`i' } g weights=. foreach i of numlist $armnumlist { quietly replace weights = d0 / surv`i' if $arm==`i' } if "$esttype"=="rmstdiff" { glm y0 $covariates2 [iweight=weights], noconstant } if "$esttype"=="rmstratio" { glm y0 $covariates2 [iweight=weights], noconstant family(poisson) } if "$esttype"=="rmtlratio" { quietly g y0loss = $tau - y0 replace y0loss = 0 if after==1 /* for rounding error */ glm y0loss $covariates2 [iweight=weights], noconstant family(poisson) } matrix beta0=e(b) matrix beta0 = beta0' svmat beta0, names(beta0) putmata beta0=beta0, replace omitmissing * the next set of calculations are done separately by arm foreach num of numlist $armnumlist { preserve quietly keep if $arm==`num' sort y0 putmata y0=y0 x=($covariates2), replace omitmissing if "$esttype"=="rmstdiff" { mata: mata_ediff(y0, x, beta0) } if "$esttype"=="rmstratio" { mata: mata_eratio(y0, x, beta0) } if "$esttype"=="rmtlratio" { putmata y0loss=y0loss, replace omitmissing mata: mata_eratio(y0loss, x, beta0) } svmat error, names(error) svmat x, names(x) forvalues j=1(1)`npar' { quietly g score`j' = x`j' * weights * error } forvalues j=1(1)`npar' { quietly g kappa1`j'=0 quietly g kappa2`j'=0 quietly g kappa3`j'=0 } quietly count local n=r(N) forvalues i=1(1)`n'{ forvalues j=1(1)`npar' { quietly replace kappa1`j' = score`j' in `i' quietly sum score`j' if y0 >= y0[`i'] quietly replace kappa2`j' = r(sum) * (1 - d0[`i']) / r(N) in `i' forvalues k=1(1)`n' { quietly count if y0[`k'] <= y0[`i'] if r(N) > 0 { quietly sum score`j' if y0 >= y0[`k'] quietly replace kappa3`j' = kappa3`j' + r(sum) * (1 - d0[`k']) / r(N)^2 in `i' } } } } forvalues j=1(1)`npar' { quietly replace kappa1`j' = round(kappa1`j', 0.0001) quietly replace kappa2`j' = round(kappa2`j', 0.0001) quietly replace kappa3`j' = round(kappa3`j', 0.0001) g kappaarm`j'= kappa1`j' + kappa2`j' - kappa3`j' } putmata kappaarm`num'=(kappaarm*), replace omitmissing restore } * back to calculations between arms mata: gamma = kappaarm$reference'*kappaarm$reference foreach num of numlist $armnumlist_noref { mata: gamma = gamma + kappaarm`num''*kappaarm`num' } mata: st_matrix("gamma", gamma) putmata x=($covariates2), replace omitmissing if "$esttype"=="rmstdiff" { mata: mata_Adiff(x) } if "$esttype"=="rmstratio" | "$esttype"=="rmtlratio" { mata: mata_Aratio(x, beta0) } svmat A, names(A) svmat gamma, names(gamma) putmata A=(A*) gamma=(gamma*), replace omitmissing mata: mata_est(A, gamma, beta0) local zscore = invnorm(1 - (100-$level)/2/100) if "$esttype"=="rmstdiff" { mata : st_matrix("cilow", st_matrix("beta0") :- `zscore' :* st_matrix("se0")) mata : st_matrix("cihigh", st_matrix("beta0") :+ `zscore' :* st_matrix("se0")) } if "$esttype"=="rmstratio" | "$esttype"=="rmtlratio" { mata : st_matrix("r0", exp(st_matrix("beta0"))) mata : st_matrix("cilow", exp(st_matrix("beta0") :- `zscore' :* st_matrix("se0"))) mata : st_matrix("cihigh", exp(st_matrix("beta0") :+ `zscore' :* st_matrix("se0"))) } return matrix beta0=beta0 return matrix se0 = se0 return matrix z0 = z0 return matrix p0 = p0 return matrix covariance = covariance if "$esttype"=="rmstratio" | "$esttype"=="rmtlratio" { return matrix r0 = r0 } return matrix cilow = cilow return matrix cihigh = cihigh drop A1 A2 A3 gamma1 gamma2 gamma3 } end *----------------------------------------------------- *--------rmst2regRUN--run the adjusted analysis *----------------------------------------------------- * there is one item passed to the program: touse, the indicator variable for complete data for inclusion in the analysis program rmst2regRUN, rclass local touse = "`0'" quietly { local esttype rmstdiff rmstratio rmtlratio foreach e of local esttype { preserve quietly keep if `touse'==1 rmst2reg `e' restore *--------return results --- matrix beta0 = r(beta0) matrix se0 = r(se0) matrix z0 = r(z0) matrix p0 = r(p0) matrix covariance = r(covariance) if "`e'"=="rmstratio" | "`e'"=="rmtlratio" { matrix r0 = r(r0) } matrix cilow = r(cilow) matrix cihigh = r(cihigh) if "`e'"=="rmstdiff" { matrix combined = beta0, se0, z0, p0, cilow, cihigh matrix roweq combined="" matrix rownames combined = $covariates2 matrix colnames combined = Coefficient StdErr z p Lower$level% Upper$level% } if "`e'"=="rmstratio" | "`e'"=="rmtlratio" { matrix combined = beta0, se0, z0, p0, r0, cilow, cihigh matrix roweq combined="" matrix rownames combined = $covariates2 matrix colnames combined = Coefficient StdErr z p exp(Coef) Lower$level% Upper$level% } return matrix `e'adj = combined matrix rownames covariance = $covariates2 matrix colnames covariance = $covariates2 return matrix `e'cov = covariance } } end *----------------------------------------------------- *--------some mata subroutines *----------------------------------------------------- mata: void mata_ediff(real colvector y0, real matrix x, real colvector beta0) { error = round(y0 - x*beta0, 0.0001) st_matrix("error", error) st_matrix("x", x) } end mata: void mata_eratio(real colvector y0, real matrix x, real colvector beta0) { error = round(y0 - exp(x*beta0),0.0001) st_matrix("error", error) st_matrix("x", x) } end mata: void mata_gamma(real matrix gammabuild, real matrix kappaarmi) { gamma = gammabuild + kappaarmi'*kappaarmi st_matrix("gamma", gamma) } end mata: void mata_Adiff (real matrix x) { A = x'*x st_matrix("A", A) } end mata: void mata_Aratio (real matrix x, real colvector beta0) { Abuild = x :* exp(x*beta0) A = Abuild'*x st_matrix("A", A) } end mata: void mata_est (real matrix A, real matrix gamma, real colvector beta0) { covariance=invsym(A)*gamma*invsym(A) varbeta=diagonal(covariance) se0 = sqrt(varbeta) z0 = beta0 :/ se0 p0 = chi2tail(1,z0:^2) st_matrix("beta0", beta0) st_matrix("se0", se0) st_matrix("p0", p0) st_matrix("z0", z0) st_matrix("covariance", covariance) } end