You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

270 lines
8.0 KiB
Plaintext

*! Date : 19 May 2009
*! Version : 1.12
*! Authors : Adrian Mander
*! Email : adrian.mander@mrc-bsu.cam.ac.uk
*! Description : Bland-Altman plots with trend adjustment
/*
17/5/06 v1.3 add-in the middle line
6/10/06 v1.4 Handle multiple data points at the same point by using frequency option
Also add Scatter option just to add options to the scatter part of the plot
2/9/07 v1.5 Changed version from 8 to 9.2
11/12/07 v1.6 Extended the shaded area
14/12/07 v1.7 Add limits of agreement into the info option
11/2/08 v1.8 BUGs fixed and extended the shading to the xlimits
17/4/08 v1.9 BUG fix.. the info limits were the wrong way round!
9/5/08 v1.10 Allowing shading to extend beyond data to any limit you like
11/5/08 v1.11 Added the range to be displayed for the averages
19/5/2009 v1.12 Changed email address
11/6/12 v1.13 Added extra option for titles and number of decimal places in display
*/
prog def batplot, rclass
version 9.2
syntax varlist (min=2 max=2) [if] [in] [,NOtrend INFO VALabel(varname) MOPTIONS(string asis) XLABel(numlist) SHADING(numlist min=2 max=2) SCatter(string asis) NOGraph DP(int 2) *]
local gopt "`options'"
if "`moptions'"~="" local mopt `"moptions(`moptions')"'
preserve
tempvar touse
mark `touse' `if' `in'
markout `touse' `m1' `m2'
qui keep if `touse'
if "`xlabel'"~="" {
local glab `"xlabel(`xlabel')"'
local i 0
foreach xv of local xlabel {
if `i++'==0 local sxmin "`xv'"
local sxmax "`xv'"
}
local shade "shade(`sxmin' `sxmax')"
}
/* To stop the shading going the whole length of the x-axis */
if "`shading'"~="" {
local i 0
foreach s of local shading {
if `i++'==0 local smin "`s'"
else local smax "`s'"
}
if `smin'<`smax' local shade "shade(`smin' `smax')"
else local shade "shade(`smax' `smin')"
}
/* NOW do the trend version */
if "`valabel'"~="" local add "val(`valabel')"
_calctrend `varlist', `gopt' `notrend' `info' `add' `mopt' sc(`scatter') `glab' `shade' `nograph' dp(`dp')
return local mean = "`r(mean)'"
return local b0 = "`r(b0)'"
return local b1 = "`r(b1)'"
return local c0 = "`r(c0)'"
return local c1 = "`r(c1)'"
return local eqn = "`r(eqn)'"
return local upper = "`r(upper)'"
return local lower = "`r(lower)'"
restore
end
/* calculate trend and do a BA plot with trend */
prog def _calctrend,rclass
syntax [varlist] [,NOTREND INFO VALabel(varname) MOPTIONS(string asis) SCatter(string asis) shade(numlist) NOGraph DP(int 2) *]
local xopt "`options'"
if "`shade'"~="" {
local i 0
foreach s of local shade {
if `i++'==0 local a "`s'"
local b "`s'"
}
local sxmin = `a'
local sxmax = `b'
}
local i 1
foreach var of varlist `varlist' {
local v`i++' "`var'"
}
tempvar av diff
qui gen `av' = (`v1' + `v2')/2
qui gen `diff' = `v1' - `v2'
local ytit "Difference (`v1'-`v2')"
local xtit "Average of `v1' and `v2'"
lab var `diff' "Diff"
lab var `av' "Mean"
if "`notrend'"~="" {
qui summ `diff'
local xbar = `r(mean)'
local sd = `r(Var)'^.5
local n = `r(N)'
local se = `sd'/`n'^.5
local t = invttail(`n'-1, .95)
local lrr = `xbar' - invnorm(0.975)*`sd' /* 95% lower limit of agreement */
local urr = `xbar' + invnorm(0.975)*`sd' /* 95% upper limit of agreement */
local mrr = `xbar' /* 95% mean agreement */
di "{text}Mean difference = {res}`mrr'"
di "{text}Limits of agreement = ({res}`lrr'{text},{res}`urr'{text})"
local min = `r(min)'
local max = `r(max)'
local lcb = `xbar' - `t'*`se'
local ucb = `xbar' + `t'*`se'
qui summ `av'
local xmin = `r(min)'
local xmax = `r(max)'
local a : di %5.3f `xmin'
local b : di %5.3f `xmax'
local range = "Averages lie between `a' and `b'"
di "{text}Averages lie between {res} `a' {text}and {res}`b'"
qui corr `av' `diff'
local r = `r(rho)'
local n = `r(N)'
local sig = ttail(`n'-2, `r'*((`n'-2)/(1-`r'^2))^.5)
tempvar uy ly my
/* The bit to extend the shade */
local obs =`c(N)'+2
qui set obs `obs'
if "`sxmin'"~="" qui replace `av'=`sxmin' in `obs--'
if "`sxmax'"~="" qui replace `av'=`sxmax' in `obs'
qui gen `uy' = `urr'
qui gen `ly' = `lrr'
qui gen `my' = `mrr'
sort `av'
if "`info'"~="" {
local te1:di %6.3f `mrr'
local te2:di %6.3f `lrr'
local te3:di %6.3f `urr'
qui count if (`diff'>`urr' | `diff'<`lrr' ) & `diff'~=.
local nout = `r(N)'
qui count if `diff'~=.
local n = `r(N)'
local pctout : di %6.2f `nout'/`n'*100
local xopt `"`xopt' subtitle("`nout'/`n' = `pctout'% outside the limits of agreement" "Mean difference `te1'" "95% limits of agreement (`te2',`te3')" "`range'") "'
}
if "`valabel'"~="" {
tempvar label
qui gen `label' = ""
cap confirm string variable `valabel'
if _rc~=0 qui replace `label' = string(`valabel') if `diff'>`urr' | `diff'<`lrr'
else qui replace `label' = `valabel' if `diff'>`urr' | `diff'<`lrr'
local scatteropt "mlabel(`label') note(Points outside limits labelled by `valabel')"
}
tempvar freq
qui bysort `diff' `av':gen `freq'=_N
local fopt "[fw=`freq']"
if index("`scatter'","jitter")~=0 local fopt ""
if "`nograph'"=="" twoway (rarea `uy' `ly' `av', bc(gs13) sort) (scatter `diff' `av' `fopt', m(o) `scatteropt' `scatter' `moptions' ) (line `my' `av',lp(dash) sort ) , ////
legend(off) ytitle(`ytit') xtitle(`xtit') xlabel(`xmin' `xmax') `xopt'
return local lower = `lrr'
return local upper = `urr'
return local mean = `mrr'
}
else {
qui reg `diff' `av'
local b1 = _b[`av']
local b0 = _b[_cons]
local sd = `e(rmse)'
qui predict resid , resid
qui gen absresid = abs(resid)
/* Analysis of the residuals */
qui reg absresid `av'
local c0 = _b[_cons]
local c1=_b[`av']
qui su `diff'
local max = `r(max)'
local min = `r(min)'
qui su `av'
local xmax = r(max)
local xmin = r(min)
local max: di %5.3f `xmax'
local min: di %5.3f `xmin'
tempvar y uy ly x
/* The bit to extend the shade */
local obs =`c(N)'+2
qui set obs `obs'
if "`sxmin'"~="" qui replace `av'=`sxmin' in `obs--'
if "`sxmax'"~="" qui replace `av'=`sxmax' in `obs'
sort `av'
qui gen `y' = `b0'+`b1'*`av'
qui gen `uy' = `b0'+`b1'*`av' + 2.46*(`c0'+`c1'*`av')
qui gen `ly' = `b0'+`b1'*`av' - 2.46*(`c0'+`c1'*`av')
if "`info'"~="" {
qui count if (`diff'>`uy' | `diff'<`ly') & `diff'~=.
local nout = `r(N)'
qui count if `diff'~=.
local n = `r(N)'
local mdp = `dp'+3
local mdp1 = `dp'+4
local pctout : di %`mdp1'.`dp'f `nout'/`n'*100
local te0 : di %`mdp'.`dp'f `b0'
local te1 : di %`mdp'.`dp'f `b1'
local te2 : di %`mdp'.`dp'f `c0'
local te3 : di %`mdp'.`dp'f `c1'
local xopt `"`xopt' subtitle("`nout'/`n' = `pctout'% outside the limits of agreement" "Mean Diff = `te0'+ `te1'*Average " "Limits +/- 2.46*(`te2' + `te3'*Average) ") "'
}
if "`valabel'"~="" {
tempvar label
qui gen `label' = ""
cap confirm string variable `valabel'
if _rc~=0 qui replace `label' = string(`valabel') if `diff'>`uy' | `diff'<`ly'
else qui replace `label' = `valabel' if `diff'>`uy' | `diff'<`ly'
local scatteropt "mlabel(`label') note(Points outside limits labelled by `valabel')"
}
tempvar freq
qui bysort `diff' `av':gen `freq'=_N
local fopt "[fw=`freq']"
if index("`scatter'","jitter")~=0 local fopt ""
if "`nograph'"=="" {
twoway (rarea `uy' `ly' `av', bc(gs13) sort)(scatter `diff' `av' `fopt', `scatteropt' `scatter') /*
*/(line `y' `av', lp(dash) sort) , legend(off) ytitle(`ytit') xtitle(`xtit') xlabel(`xmin' `xmax') `xopt'
}
return local b0 = `b0'
return local b1 = `b1'
return local c0 = `c0'
return local c1 = `c1'
return local eqn = "`b0'+`b1'*av"
return local upper = "`b0'+`b1'*Average + 2.46*(`c0'+`c1'*Average)"
return local lower = "`b0'+`b1'*Average - 2.46*(`c0'+`c1'*Average)"
}
end