*! 1.0.0 Ariel Linden 02Oct2024 program define csumra, rclass version 11.0 /* obtain settings */ syntax varlist(min=2 max=2 numeric) [if] [in] , /// RISK(varname) /// LIMit(numlist max=1) /// [ ODDS(real 2) /// Wt(varname) /// REPLace /// NOgraph * /// ] marksample touse qui count if `touse' if r(N) == 0 error 2000 local N = r(N) qui replace `touse' = -`touse' // parse yvar and xvar tokenize `varlist' local yvar `1' local xvar `2' // verification that yvar is binary and coded as 0 or 1 capture assert inlist(`yvar', 0, 1) if `touse' if _rc { di as err "`yvar' must be coded as either 0 or 1" exit 450 } // verification that riskscore values are between 0 and 1 qui sum `risk' if `touse', meanonly if r(min) < 0 | r(max) > 1 { di as err "`risk' must only contain values between 0 and 1" exit 198 } // error if odds == 1 if `odds' == 1 { di as err "the odds cannot be set to 1 because it won't detect a process change" exit 198 } // odds cannot be negative if `odds' <= 0 { di as err "the odds must be greater than 0" exit 198 } // replace previously generated variables by csumra if "`replace'" != "" { local csumravars : char _dta[_csumravars] if "`csumravars'" != "" { foreach v of local csumravars { capture drop `v' } } } // weighting if "`wt'" == "" { tempvar _ws _wf qui gen `_ws' = log((1)/(1 - `risk' + `odds' * `risk')) if `touse' qui gen `_wf' = log(`odds' / ((1 - `risk' + `odds' * `risk') * 1)) if `touse' qui gen _wt = cond(y,`_wf',`_ws') if `touse' } else { qui gen _wt = `wt' if `touse' } // generate cusum qui gen _ct = 0 if `touse' sort `touse' `xvar' // odds > 1 indicates process deterioration if `odds' > 1 { qui replace _ct in 1 = max(0, _ct[1] + _wt[1]) if `touse' forvalues ii = 2/`N' { qui replace _ct in `ii' = max(0, _ct[`ii'-1] + _wt[`ii']) if `touse' } qui gen _signal = cond(_ct > `limit', 1, 0) if `touse' } // odds < 1 indicates process improvement else if `odds' < 1 { qui replace _ct in 1 = min(0, _ct[1] - _wt[1]) if `touse' forvalues ii = 2/`N' { qui replace _ct in `ii' = min(0, _ct[`ii'-1] - _wt[`ii']) if `touse' } qui gen _signal = cond(_ct < `limit', 1, 0) if `touse' } // keep track of variables created by csum local csumravars _wt _ct _signal char def _dta[_csumravars] "`csumravars'" // graph it if "`nograph'" == "" { local roundlimit = round(`limit',.001) twoway(connected _ct `xvar' if `touse', mcol(black) msize(vsmall) lcolor(black)) /// (connected _ct `xvar' if _signal==1 & `touse', mcol(orange) msize(vsmall) lcolor(orange)), yline(`limit') /// ytitle("Risk Adjusted Cusum (`1')") note("control limit: `roundlimit'") legend(off) `options' } end