*! version 6.0.0 30nov1999 (STB-53 sg120.1) program define roccomp, rclass version 6 syntax varlist(numeric min=2) [if] [in] [fweight] [, * ] local vars: word count `varlist' if `vars'==2 { _roccom1 `0' /* independent samples */ } else { ROCcomp `0' /* correlated samples */ } return scalar p = `r(p)' return scalar df = `r(df)' return scalar chi2 = `r(chi2)' return scalar N_g = `r(groups)' tempname V mat `V'=r(V) return matrix V `V' end program define ROCcomp, sclass /* Dependent (correlated) ROC curves */ syntax varlist(numeric min=3) [if] [in] [fweight] /* */ [, BINormal Connect(string) Level(integer $S_level) /* */ Symbol(string) Bands(string) XLAbel(string) YLAbel(string) /* */ XLIne(string) YLIne(string) noGraph SEParate TEST(string) /* */ noREFline * ] marksample touse tempvar cut p w spec sens ntouse local bymax: word count `varlist' local bymax= `bymax' - 1 gettoken D 0:0 unab D : `D' local i 1 while `i'<=`bymax' { gettoken C`i' 0:0 , parse(" ,") unab C`i' : `C`i'' local grplist="`grplist' `C`i''" local i=`i'+1 } *tokenize `options' cap assert `D'==0 | `D'==1 if `touse' if _rc~=0 { noi di in red "true status variable `D' must be 0 or 1" exit 198 } if "`test'"~="" { capture di `test'[1,1] if _rc~=0 { noi di in red "matrix `test' not found" exit 198 } local rows = colsof(`test') capture assert `rows'==`bymax' if _rc~=0 { noi di in red "matrix `test' not of correct dimensions" exit 198 } } qui summ `D' if `touse', meanonly if r(min) == r(max) { di in red "Outcome does not vary" exit 198 } tempfile gphfile tempfile corfile local sensgr=" " tempvar id qui gen long `id'=_n qui compress `id' if `"`weight'"' =="" { tempvar wv qui gen int `wv' = 1 if `touse' local weight="fweight" } else { tempvar wv qui gen double `wv' `exp' } local wt=`"[fweight=`wv']"' local i 1 while `i' <= `bymax' { preserve qui keep if `touse' tempvar C gen double `C'=`C`i'' qui compress `C' tokenize `grplist' qui keep `D' `C' `wv' `bygrp' `C`i'' `id' qui logistic `D' `C' `wt', asis tempvar sens spec qui lsens, nograph gensens(`sens') genspec(`spec') if _b[`C']<0 { tempvar mark qui gen int `mark'=1 if `sens'==1 & `spec'==1 qui replace `mark'=1 if `sens'==0 & `spec'==0 replace `sens' = 1 -`sens' if `mark'!=1 replace `spec' = 1 -`spec' if `mark'!=1 } qui roctab `D' `C' `wt' , nogr level(`level') qui gen long N=r(N) qui gen double area=r(area) qui gen double se=r(se) qui gen double lb=r(lb) qui gen double ub=r(ub) local group="`C`i''" qui gen str12 bygrp= `"`group'"' rename `sens' sens`i' local troc="0"+string(round(area,0.0001)) local aroc=`"`group' ROC area: `troc'"' label var sens`i' `"`aroc'"' qui gen double x = 1-`spec' local sensgr="`sensgr' sens`i'" qui keep bygrp x sens`i' N area se lb ub `D' `C' `wv' `id' qui gen int keep=1 if `"`binormal'"'~= "" { di in gr "Fitting binormal model for: " in ye "`group'" qui rocfit `D' `C' `wt' local a=_b[intercep] local b=_b[slope] qui replace area = e(area) qui replace se = e(se_area) local alpha=invnorm(.5+`level'/200) qui replace lb = area - `alpha'*se qui replace ub = area + `alpha'*se qui replace N = e(N) local Nobs=_N local nobs=`Nobs'+300 qui set obs `nobs' tempvar k zfpf qui gen int `k'=1 if _n>`Nobs' qui replace bygrp= bygrp[_n-1] if bygrp=="" qui replace x=0 if `k'==1 & _n==`Nobs'+1 qui replace x=x[_n-1]+1/300 if x==. & `k'==1 qui gen double `zfpf'= invnorm(x) if `k'==1 qui gen double yhat`i'=`b'*`zfpf' + `a' qui replace yhat`i'=normprob(yhat`i') qui replace yhat`i'=0 if _n==_N qui replace x=0 if _n==_N qui replace sens`i'=0 if _n==_N qui replace yhat`i'=1 if _n==_N-1 qui replace x=1 if _n==_N-1 qui replace x=1 if sens`i'==1 qui replace sens`i'=1 if x==1 local troc="0"+string(round(area,0.0001)) local aroc=`"`group' ROC area: `troc'"' label var sens`i' `"`aroc'"' local sensgr="`sensgr' yhat`i'" } sum `wv' if `D'==0, meanonly local nn = r(sum) sum `wv' if `D'==1, meanonly local na = r(sum) local mgrp= bygrp if `i'~=1 { append using `"`gphfile'"' } qui save `"`gphfile'"', replace qui keep if bygrp==`"`mgrp'"' qui keep `id' `D' `C' area `wv' tempvar v01 v10 qui gen double `v01'=. qui gen double `v10'=. COMparea `D' `C' `wv' `na' `nn' area `v01' `v10' rename `v01' v01`i' rename `v10' v10`i' rename `wv' wv qui keep `D' `id' v01`i' v10`i' wv sort `id' if `i'~=1 { merge `id' using `"`corfile'"' qui drop _merge } sort `id' qui save `"`corfile'"', replace restore local i=`i'+1 } preserve qui use `"`gphfile'"', clear if `"`graph'"' == `""' { /* set graph defaults */ qui tab bygrp local max=r(r) if `"`symbol'"' == `""' { local i 1 while `i'<=`max' { local symbol `"`symbol' o"' if `"`binormal'"'~="" { local symbol `"`symbol' i"' } local i=`i'+1 } } if `"`connect'"' == `""' { local i 1 while `i'<=`max' { if `"`binormal'"'~="" { local connect `"`connect'.l"' } else local connect `"`connect'l"' local i=`i'+1 } } if `"`binormal'"'~="" { local i 1 while `i'<=`max' { local j=`i'+2 local pen=`"`pen'`j'`j'"' local i=`i'+1 } local penopt="pen(2`pen')" } if `"`bands'"' == `""' { local bands `"10"' } if `"`xlabel'"' == `""' { local xlabel `"0,.25,.5,.75,1"' } if `"`ylabel'"' == `""' { local ylabel `"0,.25,.5,.75,1"' } if `"`xline'"' == `""' { local xline `".25,.5,.75"' } if `"`yline'"' == `""' { local yline `".25,.5,.75"' } if `"`l2title'"' == `""' { local l2title `"Sensitivity"' } qui gen `w' = cond(x==0, 0, cond(x==1, 1, .)) label var `w' " " format `sensgr' `w' x %4.2f label var x `"1 - Specificity"' sort x `sensgr' if `"`separate'"'~="" { local byopt="by(bygrp)" sort bygrp x `sensgr' } if `"`refline'"'=="" { local conopt="c(l`connect')" } else { local conopt="c(.`connect')" } noi graph `w' `sensgr' x, `conopt' s(i`symbol') /* */ border t2(`"`t2title'"') bands(`bands') `penopt' /* */ xlabel(`xlabel') ylabel(`ylabel') xline(`xline') /* */ yline(`yline') `options' l2title("`l2title'") `byopt' } if `"`binormal'"'~="" { qui keep if keep==1 } qui keep if bygrp~="" sort bygrp qui by bygrp:keep if _n==1 qui gen double p=round(area*N, 1) if `"`binormal'"'~= "" { di in gr _n _col(31) "ROC" } else { di in gr _n _col(31) /* */ "ROC" _col(54) "-Asymptotic Normal--" } di in gr "`Gc'" _col(17) " Obs Area Std. Err." /* */" [`level'% Conf. Interval]" _n _dup(73) "-" local i 1 while `i' <= `bymax' { di in ye bygrp[`i'] _col(15) in yel %8.0f N[`i'] /* */ _col(26) %8.4f area[`i'] _col(39) %8.4f se[`i'] /* */ _col(54) %8.5f lb[`i'] _col(66) %8.5f ub[`i'] local i=`i'+1 } di in gr _dup(73) "-" if "`test'"=="" { di in gr "Ho: " _c tempname test mat `test'=J(comb(`bymax',2), `bymax', 0) local i 1 /* row */ local j 1 /* col */ local k 1 local mymax=`bymax' while `i' <= comb(`bymax',2) { if `j'<`mymax' { mat `test'[`i',`k'] = 1 mat `test'[`i',`j'+1] = -1 if `i'<`mymax' { di in gr /* */ "area(" in ye bygrp[`i'] in gr ") = " _c } local i=`i'+1 local j=`j'+1 } else { local j=`k'+1 local k=`k'+1 } } di in gr "area(" in ye bygrp[`bymax'] in gr ") } else { di in gr /* */ "Ho: Comparison as defined by contrast matrix: " /* */ in ye `"`test'"' } MYTEst `bymax' `D' `nn' `na' `"`test'"' `"`corfile'"' end prog def COMparea, sclass args D C wv na nn area v01 v10 tempvar Phi qui gen double `Phi'=. sort `D' `C' local i=1 while `i'<=_N { tempvar phi local x=`C'[`i'] if `D'[`i']==1 { qui gen float `phi'=`wv' if `C'<`x' & `D'==0 qui replace `phi'= 0.5*`wv' if `C'==`x' & `D'==0 qui replace `phi'= 0 if `C'>`x' & `D'==0 qui sum `phi', meanonly qui replace `Phi'=r(sum) if _n==`i' } else { qui gen float `phi'=`wv' if `C'>`x' & `D'==1 qui replace `phi'= 0.5*`wv' if `C'==`x' & `D'==1 qui replace `phi'= 0 if `C'<`x' & `D'==1 qui sum `phi', meanonly qui replace `Phi'=r(sum) if _n==`i' } qui drop `phi' local i=`i'+1 } qui replace `v10'=`Phi'/(`nn') if `D'==1 qui replace `v01'=`Phi'/(`na') if `D'==0 qui drop `Phi' qui replace `v01'=(`v01'-`area') qui replace `v10'=(`v10'-`area') end prog def MYTEst, rclass gettoken bymax 0:0 gettoken D 0:0 gettoken nn 0:0 gettoken na 0:0 gettoken test 0:0 local corfile `0' tempname T mat `T'=J(1, `bymax',1) local i 1 while `i'<=`bymax' { mat `T'[1,`i']=area[`i'] local i=`i'+ 1 } qui use `"`corfile'"', clear tempname S10 S01 S mat `S10'=J(`bymax',`bymax',1) mat `S01'=J(`bymax',`bymax',1) local r 1 while `r'<=`bymax' { local c 1 while `c'<=`bymax' { tempvar pd01 pd10 sd01 sd10 qui gen double `pd01'=v01`r'*v01`c' qui gen double `pd10'=v10`r'*v10`c' qui egen double `sd01'=sum(`pd01'*wv) qui egen double `sd10'=sum(`pd10'*wv) qui replace `sd10'=`sd10'/(`na'-1) qui replace `sd01'=`sd01'/(`nn'-1) local d10=`sd10' local d01=`sd01' mat `S10'[`r',`c'] = `d10' mat `S01'[`r',`c'] = `d01' local c=`c'+1 } local r=`r'+1 } mat `S'=(1/`na')*`S10' + (1/`nn')*`S01' tempname Cont L V R x2 iV mat `L'=`test' mat `Cont'=`L'*`T'' mat `V'=`L'*`S'*`L'' mat `iV'=syminv(`V') local df=colsof(`V')-diag0cnt(`iV') mat `R'=`Cont''*`iV'*`Cont' scalar `x2'=`R'[1,1] di in gr "chi2(" in ye `df' in gr ") = " in ye %8.2f `x2' /* */ in gr " Pr>chi2 = " in ye %8.4f chiprob(`df',`x2') return local groups=`bymax' return local chi2=`x2' return local df=`df' return local p=chiprob(`df',`x2') return matrix V `S' end