*! version 0.1.0, Brent McSharry, 12july2013
*  1. boxpanel
*  2. iterate_marker
program boxpanel, sortpreserve
version 10.1
syntax varlist(min=2 max=2 numeric) [if] [in][, TRACKby(varname) JOINmedian barw(real 0)]
/*
Nick Cox Stata journal article http://www.stata-journal.com/sjpdf.html?articlenum=gr0039
correction acknowledged http://www.stata.com/statalist/archive/2013-03/msg00917.html
*/
marksample touse
local yvar:word 1 of `varlist'
local timeCat:word 2 of `varlist'
tempvar median upq loq iqr upper lower
qui {
	egen `median' = median(`yvar') if `touse', by(`timeCat')
	egen `upq' = pctile(`yvar') if `touse', p(75) by(`timeCat')
	egen `loq' = pctile(`yvar') if `touse', p(25)  by(`timeCat')
	gen `iqr' = `upq' - `loq'
	egen `upper' = max(`yvar' / (`yvar' < `upq' + 1.5 * `iqr')) if `touse', by(`timeCat')
	egen `lower' = min(`yvar' / (`yvar' > `loq' - 1.5 * `iqr')) if `touse', by(`timeCat')
	if ("`trackby'"!="") {
		tempvar outlierGroup outlier
		tempname outlierLab
		egen int `outlier'=group(`trackby') if `touse' & !inrange(`yvar', `lower', `upper'), lname(`outlierLab')
		/*
		This code extends the line to the observation before and after the outlier:
		bysort `trackby' (`timeCat'):replace `outlier'=`outlier'[_n+1] if _n!=_N & missing(`outlier') & !missing(`outlier'[_n+1])
		gsort +`trackby' -`timeCat'
		by `trackby':replace `outlier'=`outlier'[_n+1] if _n!=0 & missing(`outlier') & !missing(`outlier'[_n+1])
		*/
		sum `outlier',meanonly
		if (r(N)>0) {
			local outlierMax `r(max)'
			gen `outlierGroup'=.
			bysort `trackby' (`timeCat'):replace `outlierGroup' = sum(!mi(`outlier') & (_n==_N | !mi(`outlier'[_n+1])) & (_n==0 | mi(`outlier'[_n-1])))
			replace `outlierGroup'=. if mi(`outlier') | (_n==_N | mi(`outlier'[_n+1]) & (_n==0 | mi(`outlier'[_n-1])))
			// noi li `trackby' `yvar' `timeCat' `outlier' `outlierGroup'
			forvalues i=1(1)`outlierMax' {
				iterate_marker `i'
				local outlierPlot `outlierPlot' ( scatter `yvar' `timeCat' if `outlier'==`i' & !inrange(`yvar', `lower', `upper'), mfcolor(`r(mfcolor)') mlcolor(`r(mlcolor)') mlwidth(medthick)  ms(O))
				local lcolor `r(mlcolor)'
				local lpattern `r(lpattern)'
				local ilab:label `outlierLab' `i'
				local legendOpt `legendOpt' label(`i' `ilab')
				local legendOrder `legendOrder' `i'
				sum `outlierGroup' if `outlier'==`i', meanonly
				if (r(N) > 0){
					forvalues grp = 1(1)`r(max)' {
						local outlierLine `outlierLine' ( line `yvar' `timeCat' if `outlier'==`i' & `outlierGroup'==`grp', lcolor(`lcolor') lpattern(`lpattern') lwidth(vvthin) )
					}
				}
				local ++i
			}
		}
		local legendOpt `legendOpt' order(`legendOrder') pos(3) cols(1)
		sort `timeCat'
		label drop `outlierLab'
	}
	else {
		local outlierPlot ( scatter `yvar' `timeCat' if !inrange(`yvar', `lower', `upper'), ms(Oh) )
		local legendOpt off
	}
}
if ("`joinmedian'" != "") {
	local medianLine ( line `median' `timeCat' )
}
if (`barw' == 0) {
	minimum_gap `timeCat'
	local barw = 0.5 * `r(min)'
}
twoway `outlierPlot' /*
	*/ ( rbar `median' `upq' `timeCat', pstyle(p1) blc(gs15) bfc(gs8) barw(`barw') ) /*
	*/ ( rbar `median' `loq' `timeCat', pstyle(p1) blc(gs15) bfc(gs8) barw(`barw') ) /*
	*/  `medianLine' /*
	*/ ( rspike `upq' `upper' `timeCat', pstyle(p1) ) /*
	*/ ( rspike `loq' `lower' `timeCat', pstyle(p1) ) /*
	*/ ( rcap `upper' `upper' `timeCat', pstyle(p1) msize(*2) ) /*
	*/ ( rcap `lower' `lower' `timeCat', pstyle(p1) msize(*2) ) /*
	*/ `outlierLine' /*
	*/, legend(`legendOpt')

end

*! version 0.1.0, Brent McSharry, 12july2013
*create different appearing markers and lines
program define iterate_marker, rclass
version 10.1
args iterator
confirm integer number `iterator'
/* http://www.ats.ucla.edu/STAT/stata/faq/showmark.htm */
local colors gs6 navy maroon forest_green dkorange teal cranberry lavender khaki sienna emidblue emerald brown erose gold bluishgray
local patterns solid dash longdash dot longdash_dot dash_dot shortdash shortdash_dot
local colorCount 16 //:word count `colors'
local patternCount 8 // wordcount("`patterns'")
local linep 128 // = `colorCount' * `patternCount'
local colorp 256 // = `colorCount' * `colorCount'
if (`iterator'>`colorp') {
	di as text "Warning: exceeded maximum number of color combinations - markers will not be unique."
	local iterator = mod(`iterator', `colorp')
	if (`iterator' == 0) {
		local `iterator' `colorCount'
	}
}
if (`iterator' > `linep'){
	di as text "Warning: exceeded maximum number of line pattern/color combinations - lines will not be unique."
	local lpi = mod(`iterator', `linep')
	if (`lpi' == 0) {
		local `lpi' `patternCount'
	}
}
else {
	local lpi `iterator'
}
local mfi = floor(`iterator'/`colorCount')
local mli = `iterator' - (`mfi' * `colorCount')
local ++mfi
local lpi = floor(`lpi'/`colorCount') + 1
return local mfcolor = word("`colors'", `mfi')
return local mlcolor = word("`colors'", `mli')
return local lpattern = word("`patterns'", `lpi')
end

*! version 0.1.0, Brent McSharry, 12july2013
*work out the minimum difference between a numeric ordered variable
program define minimum_gap, rclass
version 10.1
syntax varname(numeric)
qui {
	levelsof `varlist', local(cats)
	local min .
	foreach l of local cats {
		if ("`lastval'"!="") {
		local gap = `l' - `lastval'
			if (`min' > `gap') {
				local min `gap'
			}
		}
		local lastval `l'
	}
	return scalar min = `min'
}
end