*! Version 1.0 - March 11, 2004 - N.Orsini program rocss, rclass version 8 syntax varlist (min=2 max=2 numeric) [if] [in] [, NCut(numlist >0 max=1 integer) GRaph SAVEData(string) REPlace] preserve marksample touse quietly { tempvar y p parse "`varlist'" , parse(" ") gen `y' = `1' if `touse' gen `p' = `2' if `touse' tab `p' local ncp = r(r) /* check the outcome and the probability */ capture assert `y' == 1 | `y' == 0 if `touse' if _rc != 0 { di in r "The outcome variable is not coded 0/1" exit 198 } capture assert `p' >= 0 & `p' <= 1 if `touse' if _rc != 0 { di in r "The probability variable must range in the interval 0 / 0.1" exit 198 } /* fix the number equally spaced probability intervals in the range 0, 1.*/ if "`ncut'" != "" { local st = 1/`ncut' } else { local ncut = 10 local st = 1/10 } tempname TOT Y1 Y0 su `y' if `touse' scalar `TOT' = r(N) return scalar N = `TOT' count if `y' == 1 & `touse' scalar `Y1' = r(N) count if `y' == 0 & `touse' scalar `Y0' = r(N) /* use a post-file to record the results */ tempname mcss tempfile mroc qui postfile `mcss' cutoff sens spec cclass using mroc , replace local c = 0 local cp = 0 /* loop to calculate sensitivity, specificity */ forvalues i = 0(`st') 1.000000000002 { tempname NSENS SENS NSPEC SPEC CUTOFF CORRCLASS if `c' != 0 local cp = `cp' + `st' else local cp 0 local c = `c' + 1 count if `y' == 1 & `p' >= `i' & `touse' scalar `NSENS' = r(N) scalar `SENS' = r(N)/`Y1' count if `y' == 0 & `p' < `i' & `touse' scalar `NSPEC' = r(N) scalar `SPEC' = r(N)/`Y0' scalar `CUTOFF' = `cp' scalar `CORRCLASS' = (`NSENS'+`NSPEC')/`TOT'*100 post `mcss' (`CUTOFF') (`SENS') (`SPEC') (`CORRCLASS') } } postclose `mcss' return scalar cutoff = `c' qui use mroc , clear gen double omspec = 1- spec qui su cclas return scalar maxcclas = r(max) /* compute area under ROC curve */ gen double carea = sum((spec-spec[_n-1])*(sens+sens[_n-1])/2) return scalar area = carea[_N] local area = return(area) label var sens "Sensitivity" label var spec "Specifity" label var omspec "1-Specifity" label var cutoff "Probability cutoff" label var cclass "Correctly classified" label var carea "Cumulative Area" /* display results */ format cutoff %4.3f format sens spec omspec cclass carea %5.4f l cutoff sens spec omspec cclass carea, sep(0) noi di _n in g "Number of observations " _col(45) " = " `Y1'+`Y0' noi di in g "Number of probability cutoffs (`ncut'+1) " _col(45) " = " return(cutoff) noi di in g "Area under ROC curve " _col(45) " = " %6.4f return(area) noi di in g "Highest value of correctly classified " _col(45) " = " %6.4f return(maxcclas) /* display ROC curve */ if "`graph'" != "" { format sens omspec %3.2f local area : di %6.4f return(area) local cutoff = _N local note = "Area under ROC curve (`cutoff' cutoffs) = `area'" local yttl : var label sens local xttl : var label omspec gr twoway (connected sens omspec , /// sort /// ylabel(0(.25)1, grid) /// ytitle(`"`yttl'"') /// xlabel(0(.25)1, grid) /// xtitle(`"`xttl'"') /// note(`"`note'"') /// legend(nodraw) ) /// (function y=x, /// clstyle(refline) /// range(`vspec') /// n(2) /// yvarlabel("Sensitivity") /// xvarlabel("1-Specifity") ) } /* save dataset */ if "`savedata'" != "" { if "`replace'" != "" { qui save "`savedata'", replace } else { qui save "`savedata'" } } capture erase mroc.dta end