*! version 1.5.3 PR 02jul2012 program define boxtid, eclass // -boxtid- extended to allow factor variables version 11.0 if replay() { if "`e(cmd)'"=="" | "`e(fp_cmd2)'"!="boxtid" { error 301 } di in blue "->`e(cmd)'" _rslt exit } gettoken cmd 0 : 0 xfrac_chk `cmd' if `s(bad)' { di as err "invalid or unrecognised command, `cmd'" exit 198 } /* dist=0 (normal), 1 (binomial), 2 (poisson), 3 (cox), 4 (glm), 5 (xtgee), 6(ereg/weibull). */ local dist `s(dist)' local glm `s(isglm)' local qreg `s(isqreg)' local xtgee `s(isxtgee)' local normal `s(isnorm)' if `dist'!=7 local minv 2 else local minv 1 syntax varlist(min=`minv' fv) [if] [in] [aw fw pw iw] [, /* */ ADJust(string) ALL noCONStant CENter(string) DEAD(varname) DF(string) DFDefault(int 2) /* */ INit(string) ITer(int 100) LTOLerance(real .001) EXPon(varlist) /* */ POWers(numlist) TRace ZERo(varlist) * ] if "`adjust'" != "" { if ("`center'" != "") local adjust else local center `adjust' } frac_cox "`dead'" `dist' if "`trace'"!="" local trace noi if "`constant'"=="noconstant" { if "`cmd'"=="fit" | "`cmd'"=="cox" { di as err "noconstant invalid with `cmd'" exit 198 } if "`center'"=="" { local center no di in bl "[Note: center(no) assumed with `constant']" } else { di in bl "[Note: center(no) advised with `constant']" } local options "`options' nocons" } if "`powers'"=="" local powers 2 -1 -0.5 0 0.5 1 2 3 tempname small scalar `small'=1e-6 if `dist'!=7 { gettoken y xvars : varlist local yvar `y' } else { local yvar _t local xvars `varlist' } tempvar touse quietly { marksample touse markout `touse' `varlist' `dead' if `dist'==7 replace `touse'=0 if _st==0 /* Deal with weights. */ frac_wgt `"`exp'"' `touse' `"`weight'"' local mnlnwt = r(mnlnwt) /* mean log normalized weights */ local wgt `r(wgt)' if "`dead'"!="" local dead "dead(`dead')" local nxvars: word count `xvars' /* Centering */ xfrac_adj "`center'" "`xvars'" `touse' forvalues i=1/`nxvars' { if "`r(adj`i')'"!="" { local Cen`i' center(`r(adj`i')') } local uniq`i'=r(uniq`i') } /* Degrees of freedom */ if "`df'"!="" { xfrac_dis "`df'" df 1 . "`xvars'" forvalues i=1/`nxvars' { if "${S_`i'}"!="" { local df`i' ${S_`i'} } } } /* Assign default df for vars not so far accounted for. Give 1 df if 2-3 distinct values, 2 df for 4-5 values, dfdefault df for >=6 values. Also, initialisations for vars in non-linear part of model. */ local rhs local covars local nx 0 local nbase 0 local i 1 tokenize `xvars' while "``i''"!="" { fvexpand ``i'' if "`r(fvops)'" == "true" { local isfactor`i' 1 } else local isfactor`i' 0 if `isfactor`i'' { local df`i' 1 local ++nbase local covars `covars' ``i'' } else { frac_mun ``i'' purge if "`df`i''"=="" { if `uniq`i''<=3 local df`i' 1 else if `uniq`i''<=5 local df`i'=min(2,`dfdefault') else local df`i' `dfdefault' } if `df`i''==1 { local nbase=`nbase'+1 local covars `covars' ``i'' } else { if `uniq`i''<(`df`i''+1) { noi di as err "insufficient unique values for ``i''" exit 2000 } local ++nx local rhs `rhs' ``i'' local v`nx' ``i'' local deg`nx'=int(.01+.5*`df`i'') local xnz`nx' `touse' local ifxnz`nx' "if `touse'" local lnx`nx' 1 } } local ++i } /* Assign centering macros for covars (df=1) and rhs (df>1). covars follow rhs vars in cen`i' macros. */ local ix 0 local ic `nx' forvalues i=1/`nxvars' { if `df`i''==1 { local ic=`ic'+1 local cen`ic' `Cen`i'' } else { local ix=`ix'+1 local cen`ix' `Cen`i'' } local Cen`i' } /* Vars with expon option */ if "`expon'"!="" { tokenize `expon' while "`1'"!="" { frac_in `1' "`rhs'" local lnx`s(k)' 0 mac shift } } /* Vars with zero option */ if "`zero'"!="" { tokenize `zero' while "`1'"!="" { frac_in `1' "`rhs'" local j `s(k)' local zero`j' "zero" tempvar xnz`j' gen byte `xnz`j''=(`v`j''>0 & `touse'==1) local ifxnz`j' "if `xnz`j''" mac shift } } /* Put initial values into init`v'. (Note: correct for expx(sd) transformation in estimate of power) */ if "`init'"!="" { frac_ext "`rhs'" "`init'" init forvalues v=1/`nx' { local init`v' ${S_`v'} } } forvalues v=1/`nx' { local cen forvalues l=1/`nx' { if `l'!=`v' local cen `cen' `v`l'' } `trace' init `cmd' "`y'" `v`v'' `lnx`v'' "`cen'" /// "`covars'" "`wgt'" `touse' `deg`v'' "`dead'" /// "`zero`v''" "`powers'" "`options'" "`init`v''" local pwrs `r(pwrs)' local f=r(f) initial `deg`v'' `f' "`pwrs'" forvalues i=1/`deg`v'' { local init`v'`i' ${S_`i'} } } count if `touse' local nobs=r(N) /* Determine residual and model df. */ regress `yvar' `covars' `wgt' if `touse' local rdf=e(df_r)+("`constant'"=="noconstant") /* Calc deviance=-2(log likelihood) for regression on covars only, allowing for possible weights. Note that for logit/clogit/logistic with nocons, must regress on zero, otherwise get r(102) error. */ if (`glm' | `dist'==1) & "`constant'"=="noconstant" { tempvar z0 gen `z0'=0 } `cmd' `y' `z0' `covars' `wgt' if `touse', `dead' `options' if `xtgee' & "`covars'"=="" global S_E_chi2 0 if `glm' { * Note: with Stata 8 scale param is e(phi); was e(delta) in Stata 6 * Also e(dispersp) has become e(dispers_p). loc scale 1 if abs(e(dispers_p)/e(phi)-1)>`small' & /* */ abs(e(dispers)/e(phi)-1)>`small' { loc scale = e(phi) } } frac_dv `normal' "`wgt'" `nobs' `mnlnwt' `dist' /* */ `glm' `xtgee' `qreg' "`scale'" local dev0=r(deviance) /* Initial fit for each predictor */ local wvars forvalues v=1/`nx' { tempvar x`v' tempname range`v' tmp if `lnx`v'' { fracgen `v`v'' 0 if `touse', name(`tmp') `zero`v'' /* */ noscaling rename `r(names)' `x`v'' } else gen `x`v''=`v`v'' if `touse' /* Standardize useable values of x`v' to [0,1]. */ sum `x`v'' `ifxnz`v'' scalar `range`v''=r(max)-r(min) replace `x`v''=(`x`v''-r(mean))/`range`v'' `ifxnz`v'' forvalues i=1/`deg`v'' { tempname g`v'`i' scalar `g`v'`i''=`init`v'`i''*`range`v'' tempvar w`v'`i' z`v'`i' gen double `w`v'`i''= /* */ cond(`xnz`v'',exp(`g`v'`i''*`x`v''),0) if `touse' sum `w`v'`i'' if r(min)==r(max) | r(N)<`nobs' { noi di as err "boxtid fit failed, derived variable has zero variance" exit 498 } gen double `z`v'`i''=. local wvars `wvars' `w`v'`i'' } } /* Initial fit of whole model */ `cmd' `y' `wvars' `covars' `wgt', `dead' `options' frac_dv `normal' "`wgt'" `nobs' `mnlnwt' `dist' /* */ `glm' `xtgee' `qreg' "`scale'" local devnow=r(deviance) noi di as txt _n "Iteration 0: Deviance = " as res %9.0g `devnow' forvalues v=1/`nx' { forvalues i=1/`deg`v'' { tempname b`v'`i' scalar `b`v'`i''=_b[`w`v'`i''] } } /* Iterate */ tempname gold ratio bz local devinit `devnow' local devprev `devnow' local devcon `ltolerance' /* deviance convergence criterion */ local converg 1 local totalit 0 local done 0 local j 1 while `j'<=`iter' & !`done' { local v 0 while `v'<`nx' & !`done' { local ++v /* First loop: update g2 conditional on g1 (g1 uses w1 & z1, g2 uses w2 & z2) */ forvalues i=1/`deg`v'' { local devpr2 `devnow' replace `z`v'`i''=`w`v'`i''*`x`v'' `cmd' `y' `wvars' `z`v'`i'' `covars' `wgt', /* */ `dead' `options' scalar `gold'=`g`v'`i'' cap scalar `bz'=_b[`z`v'`i''] if _rc scalar `bz'=`b`v'`i'' scalar `ratio'=`bz'/`b`v'`i'' local dun 0 local s 1 while !`dun' & `s'>-2 { if `s'<0 noi di as txt "(step sign changed)" local gf 1 while !`dun' & `gf'<=1e6 { scalar `g`v'`i''=`gold'+`s'*`ratio'/`gf' replace `w`v'`i''=cond(`xnz`v'', /* */ exp(`g`v'`i''*`x`v''),0) if `touse' count if `w`v'`i''!=. if r(N)<`nobs' { local gf=`gf'*10 noi di as txt "(invalid transformation attempt, step length divided by 10)" } else { `cmd' `y' `wvars' `covars' `wgt', /* */ `dead' `options' frac_dv `normal' "`wgt'" `nobs' `mnlnwt' /* */ `dist' `glm' `xtgee' `qreg' "`scale'" local devdiff=(r(deviance)-`devpr2') if `devdiff'<0 { /* accept new parameters */ local devnow=r(deviance) forvalues i2=1/`deg`v'' { scalar `b`v'`i2''=_b[`w`v'`i2''] } local dun 1 } else { local gf=`gf'*10 noi di as txt "(unprofitable step attempted, step length divided by 10)" } } } local s=`s'-2 } if "`trace'"!="" { noi di as txt "`v`v''(`i')" %4.0f `j2' /* */ _col(16) as res %9.0g /* */ `g`v'`i''/`range`v'' /* */ _col(28) %12.6f r(deviance) /* */ _col(44) %9.6f `devdiff' } local devpr2 `devnow' local totalit=`totalit'+1 } /* i loop (term within variable) */ } if `nx'>1 local devdiff=(`devnow'-`devprev') if "`devdiff'"=="" local devdiff 0 if `devdiff'>10 { local done 1 local converg 0 } else if `devdiff'>-`devcon' { local done 1 } if "`trace'"!="" noi di noi di as txt "Iteration `j': Deviance = " as res %9.0g `devnow' /* */ as txt " (change = " as res %9.0g `devdiff' as txt ")" local devprev `devnow' local j=`j'+1 } if !`done' | (`devnow'>`devinit') local converg 0 /* Tests for linearity for each nonlinear term (could make this an option) */ local terms: word count `wvars' local mdf=2*`terms' /* model df for power terms + Taylor terms */ forvalues v=1/`nx' { /* Replace wv with power terms for all but v'th predictor. Delete terms recursively from wvars. */ local wv `wvars' forvalues i=1/`deg`v'' { frac_str `w`v'`i'' `wv' local wv `s(new)' } `cmd' `y' `v`v'' `wv' `covars' `wgt' if `touse', /* */ `dead' `options' frac_dv `normal' "`wgt'" `nobs' `mnlnwt' `dist' /* */ `glm' `xtgee' `qreg' "`scale'" local lin`v'=_b[`v`v''] local se`v'=_se[`v`v''] local gain`v'=r(deviance)-`devnow' if `gain`v''>0 { local d `gain`v'' local n1=2*`deg`v''-1 local n2=`rdf'-`mdf' frac_pv `normal' "`wgt'" `nobs' `d' `n1' `n2' local P`v'=r(P) } else { local gain`v' 0 local P`v' 1 } } /* Final model. Approximation to SE(k): see Snedecor and Cochran 1967 p 470. */ local zvars forvalues v=1/`nx' { forvalues i=1/`deg`v'' { tempname sek`v'`i' scalar `sek`v'`i''=. local zvars `zvars' `z`v'`i'' } } `cmd' `y' `wvars' `zvars' `covars' `wgt', `dead' `options' local xp local pwrs local rc 0 local zvars local fvl forvalues v=1/`nx' { local xp`v' local pwrs`v' frac_mun `v`v'' local vn `s(name)' gen byte `vn'_1=. /* reserves name `vn'_1 */ forvalues i=1/`deg`v'' { cap scalar `sek`v'`i''=(_se[`z`v'`i'']/ /* */ abs(`b`v'`i''))/`range`v'' if _rc scalar `sek`v'`i''=. /* Rescale auxiliary variables to give their se=se(power param) */ * replace `z`v'`i''=`z`v'`i''*_se[`z`v'`i'']/`sek`v'`i'' replace `z`v'`i''=`z`v'`i''*abs(`b`v'`i'')*`range`v'' local k=`g`v'`i''/`range`v'' local pwrs`v' `pwrs`v'' `k' if `lnx`v'' { frac_ddp `k' 6 local K `r(ddp)' local e } else { local K 1 local e "expx(`k')" } noi fracgen `v`v'' `K' `K' if e(sample),`all' `e' /* */ `cen`v'' replace index(`i') `zero`v'' name(`vn') local xx1: word 1 of `r(names)' local xx2: word 2 of `r(names)' count if `xx1'!=. if r(N)<`nobs' { local rc 2001 } local xp`v' `xp`v'' `xx1' /* Rename auxiliary ("zero") variable. */ rename `xx2' `vn'p`i' local zp`v' `zp`v'' `vn'p`i' lab var `vn'p`i' "boxtid auxil p`i'(`v`v'')" local zvars `zvars' `vn'p`i' } local xp `xp' `xp`v'' local h`v' `xp`v'' `zp`v'' local fvl `fvl' `h`v'' local pwrs `pwrs' `pwrs`v'' } } /* Center covariates if necessary, and create final var list (`fvl') */ forvalues i=1/`nbase' { local j=`i'+`nx' local v: word `i' of `covars' if "`cen`j''"!="" { frac_mun `v' local n `s(name)' fracgen `v' 1 if e(sample),`all' replace noscaling `cen`j'' /* */ name(`n') local v `r(names)' } local fvl `fvl' `v' } if `rc'==0 qui `cmd' `y' `fvl' `wgt', `dead' `options' di as txt _n "[Total iterations: `totalit']" /* New code in v 1.1.6 for consistency with mfracpol (Differs from code in fracpoly because of multiple transformed predictors--offset to nbase is `nx' not 1) */ forvalues i=1/`nbase' { local j=`i'+`nx' ereturn local fp_x`j': word `i' of `covars' ereturn local fp_k`j' 1 ereturn local fp_a`j' } /* End of new code in v 1.1.6 for consistency with mfracpol */ global S_1 `nobs' global S_2 `devnow' ereturn local fp_xp `xp' ereturn local fp_pwrs `pwrs' ereturn local fp_x `rhs' ereturn scalar fp_dev=`devnow' ereturn scalar fp_cnvg=`converg' forvalues v=1/`nx' { ereturn local fp_x`v' `v`v'' /* name of v`th predictor */ ereturn local fp_k`v' `pwrs`v'' /* powers for v'th predictor */ ereturn scalar fp_g`v'=`gain`v'' /* gain for v'th predictor */ ereturn scalar fp_P`v'=`P`v'' /* P-value for nonlinearity */ ereturn scalar fp_l`v'=`lin`v'' /* linear coefficient */ ereturn scalar fp_sl`v'=`se`v'' /* SE of linear coefficient */ ereturn scalar fp_a`v'=`deg`v'' /* #auxiliaries for this var */ local S forvalues i=1/`deg`v'' { local s=`sek`v'`i'' local S `S' `s' } ereturn local fp_s`v' `S' /* se(powers) for v'th predictor */ ereturn local fp_n`v' `h`v'' /* transformed elements for var `v', including auxiliaries */ } ereturn local fp_base `covars' ereturn local fp_depv `y' ereturn scalar fp_dist=`dist' ereturn local fp_fvl `fvl' ereturn local fp_wgt "`weight'" ereturn local fp_exp "`exp'" ereturn scalar fp_nobs=`nobs' ereturn local fp_sfac `scalfac' ereturn scalar fp_nx=`nx'+`nbase' ereturn local fp_opts `dead' `options' ereturn local fp_t1t "Box-Tidwell model" ereturn local fp_cmd fracpoly ereturn local fp_cmd2 boxtid if `rc'==0 _rslt else error `rc' end program define _rslt version 8 local VV : di "version " string(_caller()) ", missing:" di as txt _n "Box-Tidwell regression model" if "`level'" != "" local level level(`level') if "`e(cmd2)'"=="stpm" `VV' ml display, `level' `options' else if "`e(cmd)'"=="stpm2" `VV' stpm2, `level' `options' else if "`e(cmd2)'"=="stcox" `VV' `e(cmd2)', `level' nohr `options' else `VV' `e(cmd)', `level' `options' di as txt "{hline 13}{c TT}{hline 64}" local nx: word count `e(fp_x)' forvalues v=1/`nx' { local var: word `v' of `e(fp_x)' local var=abbrev("`var'",12) local skip=13-length("`var'") cap local b1=e(fp_l`v') if _rc local b1 . cap local seb1=e(fp_sl`v') if _rc local seb1 . cap local pnlin=e(fp_P`v') if _rc local pnlin . frac_ddp e(fp_g`v') 3 local nlindev `r(ddp)' di as txt "`var'" _skip(`skip') "{c |}" /* */ as res _col(17) %9.0g `b1' _col(28) %9.0g `seb1' /* */ %9.2f `b1'/`seb1' /* */ as txt _col(47) "Nonlin. dev. " as res "`nlindev'" /* */ as txt _col(68) "(P = " as res %5.3f `pnlin' as txt ")" local deg: word count `e(fp_k`v')' forvalues i=1/`deg' { local k: word `i' of `e(fp_k`v')' local s: word `i' of `e(fp_s`v')' if "`k'"=="" local k . if "`s'"=="" local s . di as txt _col(11) "p`i' {c |}" _col(17) as res %9.0g `k' /* */ _col(28) %9.0g `s' // _col(41) %5.2f `k'/`s' } } di as txt "{hline 13}{c BT}{hline 64}" di as txt "Deviance:" as res %9.3f e(fp_dev) as txt "." if e(fp_cnvg)==0 { di as err "divergence, or only partial convergence achieved" exit 430 } end program define init, rclass args cmd y vv lnx adj covars wgt touse deg dead zero powers options init local wi: word count `init' /* initial powers can be null */ if `wi'>0 { if `wi'!=`deg' { noi di as err /* */ "incorrect number of initial values for `vv'" exit 198 } } if !`lnx' local isexpx expx(sd) if "`covars'`adj'"!="" local cov covars(`covars' `cen') if "`init'"=="" { tempname vn /* creates temporary name */ xfrac_154 `cmd' `y' `vv' `covars' `adj' `wgt' if `touse', /* */ degree(`deg') `dead' `zero' `isexpx' powers(`powers') `options' /* */ name(`vn') cap drop `e(fp_xp)' /* remove var(s) with funny names */ if !`lnx' local xpx=`e(fp_xpx)' /* factor if expx used */ else local xpx 1 if `deg'==1 { local dev=`e(fp_d1)' local pwr `e(fp_k1)' * Beginning of code taken from previous subroutine "sorted". local mx 1 local mp1 `e(fp_p1)' local i 1 while "`e(fp_bt`i')'"!="" { local p`i': word 1 of `e(fp_bt`i')' local d`i': word 2 of `e(fp_bt`i')' local s`i'=(`p`i'')^2 if `p`i''==`mp1' local m1 `i' if `d`i''>`d`mx'' local mx `i' local ++i } local np=`i'-1 /* find 2nd smallest deviance */ local m2 `mx' forvalues i=1/`np'{ if `d`i''<`d`m2'' & `i'!=`m1' { local m2 `i' } } /* find 3rd smallest deviance */ local m3 `mx' forvalues i=1/`np'{ if `d`i''<`d`m3''&`i'!=`m1'&`i'!=`m2' { local m3 `i' } } /* form matrices and do quadratic regression */ tempname X Y XTX XTXi XTY B matrix `X' = (1,`p`m1'',`s`m1''\1,`p`m2'',`s`m2''\1,`p`m3'',`s`m3'') matrix `Y' = (`d`m1'' \ `d`m2'' \ `d`m3'') matrix `XTX' = `X''*`X' matrix `XTXi' = syminv(`XTX') matrix `XTY' = `X''*`Y' matrix `B' = `XTXi'*`XTY' /* find initial value */ local pwrs = -0.5*`B'[2,1]/`B'[3,1] * End of code taken from previous subroutine "sorted". if abs(`pwrs')<.05 { local pwrs=sign(`pwrs')*.05 } xfrac_154 `cmd' `y' `vv' `pwrs' `covars' `adj' `wgt' /* */ if `touse', /* */ `dead' `zero' `isexpx' `options' name(`vn') cap drop `e(fp_xp)' local devsrt=e(fp_dlin) local pwrsrt `e(fp_k1)' if `devsrt'>`dev' { local pwrs `pwr' } else local pwrs `pwrsrt' } else local pwrs `e(fp_pwrs)' tokenize `pwrs' local pwrs while "`1'"!="" { if abs(`1')<.05 local 1 .05 local pwrs `pwrs' `1' mac shift } } else { /* Find initial power values in string `init' */ local pwrs forvalues i=1/`deg' { local p: word `i' of `init' cap confirm num `p' if _rc { noi di as err "invalid init() for var `vv'" exit 198 } local pwrs `pwrs' `p' } local xpx 1 } if `lnx' local xpx 1 return local pwrs `pwrs' /* initialised powers */ return scalar f = `xpx' /* factor from expx transf */ end program define initial * initialize powers args deg f pwrs forvalues i=1/`deg' { local p: word `i' of `pwrs' if abs(`p')<.001 local p=sign(`p')*.001 if `i'==1 { local p1 `p' local padd 0 } else { if abs(`p'-`p1')<.001 { /* tiebreak */ local padd=`padd'+0.22 local p=`p'+`padd' } else { local p1 `p' local padd 0 } } if `f'!=1 global S_`i'=`p'*`f' else global S_`i' `p' } end * version 1.0.0 PR 26Feb1999. program define frac_str, sclass version 6 gettoken target 0 : 0 if "`target'"=="" { error 198 } tokenize `0' local found 0 while "`1'"!="" { if "`1'"=="`target'" { local found = `found'+1 } else local new "`new' `1'" mac shift } sret local new `new' sret local found `found' end * version 1.1.0 PR 26Feb1999. program define frac_ext version 6 /* Extract values (e.g. powers, initial values) for individual variables. 1 = x-variables, 2 = value_string, 3 = name (e.g. init), 4 (optional) = separator. Example: fracext "rhs'" "`init'" init \ */ local rhs "`1'" local target "`2'" local tname "`3'" local sep "`4'" if "`sep'"=="" { local sep "," } unabbrev `rhs' local rhs `s(varlist)' local nx: word count `rhs' local j 0 while `j'<`nx' { local j=`j'+1 local n`j': word `j' of `rhs' } quietly { tokenize "`target'", parse("`sep'") local ncl 0 /* # of comma-delimited clusters */ while "`1'"!="" { if "`1'"=="`sep'" { mac shift } local ncl=`ncl'+1 local clust`ncl' "`1'" mac shift } if "`clust`ncl''"=="" { local ncl=`ncl'-1 } if `ncl'>`nx' { noi di in red "too many sets of `tname'() values specified" exit 198 } /* Disentangle each varlist:string cluster */ local i 1 while `i'<=`ncl' { tokenize "`clust`i''", parse("=:") if "`2'"!=":" & "`2'"!="=" { if `i'>1 { noi di in red /* */ "invalid `tname'() value `clust`i''" /* */ ", must be first item" exit 198 } local 2 ":" local 3 `1' local j 0 local 1 while `j'<`nx' { local j=`j'+1 local 1 `1' `n`j'' } } local arg3 `3' unabbrev `1' tokenize `s(varlist)' while "`1'"!="" { frac_in `1' "`rhs'" local val`s(k)' `arg3' mac shift } local i = `i'+1 } * transfer values to $S_* local j 0 while `j'<`nx' { local j=`j'+1 global S_`j' `val`j'' } } end