*! version 1.3, Chao Wang, 21/05/2019
* calculates median survival time using Cox/Poisson regression
program medsurv

version 13.1
syntax [, id(varname numeric) riskset(varname numeric)]

tempvar medsurv_int haz hazc cumhaz surv surv_diff
tempfile medsurv timeintervals


/* run model like this:
stsplit, at(failures) riskset(interval)

generate time_exposed = _t - _t0
poisson _d ibn.interval protect age calcium, exposure(time_exposed) noconstant irr

*/

preserve
quietly drop if `riskset'==.  // some of these are censored

// save the interval-time relationship
local exposure=substr(e(offset),4,length(e(offset))-4)
keep `riskset' `exposure' _t
rename _t medsurv
label variable medsurv "Median survival time"
bysort `riskset': egen _max_time_exposed=max(`exposure')
drop `exposure'
ren _max_time_exposed `exposure'
quietly duplicates drop
quietly save `timeintervals'

restore, preserve

sort `id' _t
quietly sum `riskset'
local max_interval=r(max)
quietly levelsof `id', local(ids)
quietly foreach i of local ids {

 // expand the data
 count if `id'==`i'
 if r(N)<`max_interval' {
  sum _t if `id'==`i'
  expand `max_interval'-r(N)+1 if `id'==`i' & _t==r(max)
  replace `riskset'=1 if `id'==`i'
  replace `riskset'=sum(`riskset') if `id'==`i'
 }

 // predict: this is to save data size
 merge m:1 `riskset' using `timeintervals', update replace nogen
 predict `haz' if `id'==`i'
 gen `cumhaz'=sum(`haz') if `id'==`i'

 gen `surv'=exp(-`cumhaz')
 gen `surv_diff'=0.5-`surv'
 replace `surv_diff'=. if `surv_diff'<0

 sum `surv_diff' if `id'==`i'
 drop if (`surv_diff'>r(min) & `id'==`i') | (`surv_diff'==. & `id'==`i')

 drop `haz' `cumhaz' `surv' `surv_diff'

}

/* alternatively, sth similar
stcox protect age calcium
predict double bhc, basehc
gen hazc=bhc*exp(xb)
*/

/* cal also try below for non time-varying covaraite:
stcox protect age calcium
predict bchaz, basechazard
gen cumhaz1=bchaz*exp(xb)
*/

/* extract baseline hazard function
matrix b=e(b)'
svmat b
gen bh=exp(b1)

sum interval
local max=r(max)
keep in 1/`max'
keep bh
gen interval=_n

save ttt.dta, replace

restore

merge m:1 interval using ttt.dta
 gen haz=bh*exp(xb)
*/

* create a datset containing the predicted median survival for each subject
keep `id' `riskset'
quietly merge m:1 `riskset' using `timeintervals',keep(match) nogen
quietly save `medsurv'
restore

* now merge median survival into main dataset
quietly merge m:1 `id' using `medsurv', nogen keep(master match)

end