*! 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
