*! diagti 2.051, 7 April 2004 *! by PT Seed (paul.seed@kcl.ac.uk) *! Immediate version of diagt, *! based on diagtest.ado (Aurelio Tobias, STB-56: sbe36) *! *! Likelihood ratios added *! Odds ratios and ROC areas added *! CI based on csi, or *! add 0.5 when zeros appear of with -sf- option when calculating LRs program define diagti , rclass version 8.0 set trace off tokenize "`*'", parse(" ,") local i= 1 while `i' <= 4 { confirm integer number ``i'' local i = `i' + 1 } local a = `1' local b = `2' local c = `3' local d = `4' local n1_ = `a' + `b' local n2_ = `c' + `d' local n_1 = `a' + `c' local n_2 = `b' + `d' local n = `n_1' + `n_2' mac shift 4 local options "Prev(string) Level(real $S_level) noTable sf sf0 tb Woolf Bamber Hanley Odds Star" parse "`*'" if "`star'" == "star" { local star "di "" } else { local star "*" } if "`prev'" ~= "" { local _prev_ "(lr)" } `star' tb affects the quantities based on risk ratios: if index("`prev'", "%") { local prev = real(subinstr("`prev'","%","",1)) cap assert `prev' >=0 & `prev' < 100 if _rc { di in red "Prevalence must be between 0 and 100% exit _rc } } else if "`prev'" ~= ""{ local prev = `prev'*100 cap assert `prev' >=0 & `prev' < 100 if _rc { di in red "Prevalence must be between 0 and 100% di in red "Percentages must be indicated by ''%'' sign exit _rc } } if ((`a'*`b'* `c'*`d' == 0) & ("`sf0'" ~= "")) | ("`sf'" ~= "") { local sf "(sf)" local sk_sf = 1 local tb local woolf * NOTE: sf overrides tb and woolf } else { local sk_sf = 0 if "`tb'" ~= "" { local _tb_ "(tb)" local _woolf_ "(tb)" } if "`woolf'" ~= "" { local _woolf_ "(Woolf)" } } * set trace on `star' Prevalence if "`_prev_'" == ""{ qui cii `n' `n1_', level(`level') local prev = 100*r(mean) local prev_lb = 100*r(lb) local prev_ub = 100*r(ub) } `star' Sensitivity qui cii `n1_' `a' , level(`level') local sens = 100*r(mean) local sens_lb = 100*r(lb) local sens_ub = 100*r(ub) `star' Specificity qui cii `n2_' `d', level(`level') local spec = 100*r(mean) local spec_lb = 100*r(lb) local spec_ub = 100*r(ub) `star' Without substitution formula if "`sf'" == "" { qui csi `a' `c' `b' `d' , or `tb' `woolf' level(`level') `star' Likelihood ratios qui csi `a' `c' `b' `d' , `tb' or local lrp = r(rr) local lrp_ub = r(ub_rr) local lrp_lb = r(lb_rr) `star' Odds ratio local or = r(or) local or_lb = r(lb_or) local or_ub = r(ub_or) qui csi `b' `d' `a' `c' , `tb' local lrn = r(rr) local lrn_ub = r(ub_rr) local lrn_lb = r(lb_rr) * If prevalence not given, PVs and CIs worked out directly. if "`_prev_'" == "" { `star' PPV qui cii `n_1' `a', level(`level') local ppv = 100*r(mean) local ppv_lb = 100*r(lb) local ppv_ub = 100*r(ub) `star' NPV qui cii `n_2' `d', level(`level') local npv = 100*r(mean) local npv_lb = 100*r(lb) local npv_ub = 100*r(ub) } `star' If prevalence given, PVs and CIs worked out from LRs. if "`_prev_'" ~= "" { `star' Give CI for PVs local odds = `prev'/(100-`prev') local ppv = (`sens'*`prev') / ((`sens'*`prev')+((100-`spec')*(100-`prev')))*100 local ppv_lb = `lrp_lb'*`odds' local ppv_ub = `lrp_ub'*`odds' local npv = ((`spec'*(100-`prev')) / (((100-`sens')*`prev')+(`spec'*(100-`prev'))))*100 local npv_lb = `lrn_ub'*`odds' local npv_ub = `lrn_lb'*`odds' foreach v in ppv_lb ppv_ub { local `v' = 100*``v''/(1+``v'') } foreach v in npv_lb npv_ub { local `v' = 100/(1+``v'') } } } `star' With substitution formula else { local zval = invnorm(1-(1-`level'/100)/2) local a0 = `a' + .5 local b0 = `b' + .5 local c0 = `c' + .5 local d0 = `d' + .5 `star' NOTE: Likelihood ratios depend on sensitivity & specificity `star' They are not affected by prevalences local lrp = (`a0'/(`a0'+`b0'))/(`c0'/(`c0'+`d0')) local lrp_v = `b0'/(`a0'*(`a0'+`b0')) + `d0'/(`c0'*(`c0'+`d0')) local lrp_lb = exp(ln(`lrp')-`zval'*sqrt(`lrp_v')) local lrp_ub = exp(ln(`lrp')+`zval'*sqrt(`lrp_v')) local lrn = (`b0'/(`a0'+`b0'))/(`d0'/(`c0'+`d0')) local lrn_v = `a0'/(`b0'*(`a0'+`b0')) + `c0'/(`d0'*(`c0'+`d0')) local lrn_lb = exp(ln(`lrn')-`zval'*sqrt(`lrn_v')) local lrn_ub = exp(ln(`lrn')+`zval'*sqrt(`lrn_v')) local or = (`a0'/(`b0'))/(`c0'/(`d0')) local or_v = 1/`a0' + 1/`b0' + 1/`c0' + 1/`d0' local or_lb = exp(ln(`or')-`zval'*sqrt(`or_v')) local or_ub = exp(ln(`or')+`zval'*sqrt(`or_v')) `star' If prevalence not given, PVs and CIs worked out directly. if "`_prev_'" == "" { `star' PPV qui cii `n_1' `a', level(`level') local ppv = 100*r(mean) local ppv_lb = 100*r(lb) local ppv_ub = 100*r(ub) `star' NPV qui cii `n_2' `d', level(`level') local npv = 100*r(mean) local npv_lb = 100*r(lb) local npv_ub = 100*r(ub) } `star' If prevalence given, PVs and CIs worked out from LRs. if "`_prev_'" ~= "" { `star' Give CI for PVs local odds = `prev'/(100-`prev') local ppv_lb = `lrp_lb'*`odds' local ppv = ((`sens'*`prev') / ((`sens'*`prev')+((100-`spec')*(100-`prev'))))*100 local npv = ((`spec'*(100-`prev')) / (((100-`sens')*`prev')+(`spec'*(100-`prev'))))*100 local ppv_ub = `lrp_ub'*`odds' local npv_lb = `lrn_ub'*`odds' local npv_ub = `lrn_lb'*`odds' foreach v in ppv_lb ppv_ub { local `v' = 100*``v''/(1+``v'') } foreach v in npv_lb npv_ub { local `v' = 100/(1+``v'') } } } `star' ROC area if _N >0 { preserve clear } qui { tabi `a' `b' \ `c' `d' expand pop drop if pop == 0 drop pop rename col test label var test "Test result" label define test 1 "Pos." 2 "Neg." 0 "Neg." label values test test rename row true label var true "True disease status" label define true 1 "Abnormal" 2 "Normal" 0 "Normal" label values true true recode true 2 = 0 recode test 2 = 0 roctab true test , `bamber' `hanley' local roc = r(area) local roc_lb = r(lb) local roc_ub = r(ub) if "`bamber'" ~= "" { local roc_opt = "(Bamber)" } if "`hanley'" ~= "" { local roc_opt = "(Hanley)" } } `star' Table if "`table'" == "" { tabulate true test, matcell(T) `options' } di _n in gr _col(51) "[`level'% Confidence Interval]" di in gr "---------------------------------------------------------------------------" if "`_prev_'" == "" { di in gr "Prevalence Pr(A)" _col(44) in ye %6.2g `prev' "%" _col(54) %6.2g `prev_lb' "%" _col(65) %6.3g `prev_ub' "%" } else { di in gr "Prevalence Pr(A)" _col(44) in ye %6.3g `prev' "%" in gr _col(54) "----- (given) -----" } di in gr "---------------------------------------------------------------------------" di in gr "Sensitivity Pr(+|A)" _col(44) in ye %6.3g `sens' "%" _col(54) %6.3g `sens_lb' "%" _col(64) %6.3g `sens_ub' "%" di in gr "Specificity Pr(-|N)" _col(44) in ye %6.3g `spec' "%" _col(54) %6.3g `spec_lb' "%" _col(64) %6.3g `spec_ub' "%" di in gr "ROC area (Sens. + Spec.)/2" _col(46) in ye %5.3g `roc' _col(56) %5.3g `roc_lb' _col(66) %5.3g `roc_ub' _skip(1) in gr "`roc_opt'" di in gr "---------------------------------------------------------------------------" di in gr "Likelihood ratio (+) Pr(+|A)/Pr(+|N)" _col(45) in ye %6.3g `lrp' _col(55) %6.3g `lrp_lb' _col(65) %6.3g `lrp_ub' in gr _skip(`sk_sf') "`sf'" _skip(1) "`_tb_'" di in gr "Likelihood ratio (-) Pr(-|A)/Pr(-|N)" _col(45) in ye %6.3g `lrn' _col(55) %6.3g `lrn_lb' _col(65) %6.3g `lrn_ub' in gr _skip(`sk_sf') "`sf'" _skip(1) "`_tb_'" di in gr "Odds ratio LR(+)/LR(-)" _col(44) in ye %7.3g `or' _col(55) %6.3g `or_lb' _col(63) %8.3g `or_ub' in gr _skip(`sk_sf') "`sf'" _skip(1) "`_woolf_'" di in gr "Positive predictive value Pr(A|+)" _col(44) in ye %6.3g `ppv' "%" _col(54) %6.3g `ppv_lb' "%" _col(64) %6.3g `ppv_ub' "%" in gr _skip(1) "`_prev_'" di in gr "Negative predictive value Pr(N|-)" _col(44) in ye %6.3g `npv' "%" _col(54) %6.3g `npv_lb' "%" _col(64) %6.3g `npv_ub' "%" in gr _skip(1) "`_prev_'" di in gr "---------------------------------------------------------------------------" * set trace on if "`odds'" ~= "" { local oprev = `prev'/(100-`prev') if "`_prev_'" == "" { local oprev_lb = `prev_lb'/(100-`prev_lb') local oprev_ub = `prev_ub'/(100-`prev_ub') } local opos = `ppv'/(100-`ppv') local opos_lb = `ppv_lb'/(100-`ppv_lb') local opos_ub = `ppv_ub'/(100-`ppv_ub') local oneg = (100-`npv')/`npv' local oneg_lb = (100-`npv_lb')/`npv_lb' local oneg_ub = (100-`npv_ub')/`npv_ub' if "`_prev_'" == "" { di in gr "Pre-test odds prev/(1-prev)" _col(44) in ye %6.3g `oprev' _col(54) %6.3g `oprev_lb' _col(64) %6.3g `oprev_ub' in gr _skip(1) "`_prev_'" } else { di in gr "Pre-test odds prev/(1-prev)" _col(44) in ye %6.3g `oprev' in gr _col(54) "----- (given) -----" } * di in gr "Post-test odds (+) Pr(A|+)/(1-Pr(A|+))| _col(44) in ye %6.2f |opos| _col(54) %6.2f |opos_lb| _col(64) %6.2f |opos_ub| in gr _skip(2fs) ||_prev_|| di in gr "Post-test odds (+) Pr(A|+)/(1-Pr(A|+))" _col(44) in ye %6.3g `opos' _col(54) %6.3g `opos_lb' _col(64) %6.3g `opos_ub' in gr _skip(2) "`_prev_'" di in gr "Post-test odds (-) Pr(A|-)/(1-Pr(A|-))" _col(44) in ye %7.3g `oneg' _col(54) %7.3g `oneg_lb' _col(64) %7.3g `oneg_ub' in gr _skip(1) "`_prev_'" di in gr "---------------------------------------------------------------------------" local retlist "oneg opos oprev " foreach ret in `retlist' { cap ret scalar `ret'_ub = ``ret'_ub' cap ret scalar `ret'_lb = ``ret'_lb' cap ret scalar `ret' = ``ret'' } } if "`sf'" ~= "" { di in gr _n "(sf) Likelihood ratios are estimated using the substitution formula. di in gr " 0.5 is added to all cell frequencies before calculation." } if "`_prev_'" ~= "" { di in gr _n "(lr) Values and confidence intervals are based on likelihood di in gr " ratios" _skip(`sk_sf') "`sf', assuming that the prevalence is known exactly." } if (`a'*`b'* `c'*`d' == 0) & ("`sf'" == "") { di _n in gr " Missing values or confidence intervals may be estimated di in gr " using the -sf- or -sf0- options." } local retlist "`retlist' roc or lrn lrp npv ppv prev spec sens " foreach ret in `retlist' { cap ret scalar `ret'_ub = ``ret'_ub' cap ret scalar `ret'_lb = ``ret'_lb' cap ret scalar `ret' = ``ret'' } end exit