*! v0.7.1  IRW  2jan2014

/************************************************************
v0.7.1  2jan2014
    clear option: graph command stored by -window push- not in F9
v0.7  25aug2011
    nopreserve changed to clear & performance improved
v0.6  25aug2011
    new varlabel() option allows any option for specifying the look of categorical axis labels - see help cat_axis_label_options
v0.5  16jun2011  new label option; var now numeric & labelled
For help file or SJ article:
    how to spot a monotone pattern (with example?)
    give example from DYD with secondary outcomes
        -misspattern APQ_0 LDQ_0 AUDIT_0 CORE_0- is neat
        -misspattern  total*-
        -misspattern  APQ_* LDQ_* AUDIT_* CORE_*, novarsort- for more complex pattern
************************************************************/

prog def misspattern
* Display missing data patterns for a whole data set
version 10

// PARSE
syntax [varlist] [if] [in], [novarsort noidsort REVerse MISScolor(string) OBScolor(string) clear debug INDIVSname(string) label varlabel(string) *]
marksample touse, novarlist
if "`misscolor'"=="" local misscolor black
if "`obscolor'" =="" local obscolor gray
if "`indivsname'"=="" local indivsname individuals

// ANALYSE
preserve
* Keep relevant data
qui keep if `touse'
if "`varlist'"=="" local varlist _all
unab all : `varlist'
keep `varlist'
local nobs = _N

* Create missing value indicators
tempvar miss misssum one
tempname varlab
gen `misssum' = 0
local i 0
foreach var in `varlist' {
    local ++i
    if "`label'"=="label" local varlabel`i' : var label `var'
    if "`varlabel`i''"=="" local varlabel`i' `var'
    label def `varlab' `i' "`varlabel`i''", add
    gen `miss'`i' = mi(`var')
    qui replace `misssum' = `misssum' + mi(`var')
}
gen `one'=1

* Get individuals in chosen order
if "`idsort'"!="noidsort" {
    sort `misssum' `miss'*
    if "`debug'"=="debug" di "Before collapse 1"
    if "`debug'"=="debug" l
    qui collapse (sum) `one', by(`misssum' `miss'*)
    if "`debug'"=="debug" di "After collapse 1"
    if "`debug'"=="debug" l
    drop `misssum'
}
else keep `miss'* `one'

* Label and count patterns
gen pattern = _n // determines order of individuals
local npatterns = _N
di as result `npatterns' as text " patterns found"
order pattern `miss'* `one'
qui reshape long `miss', i(pattern) j(var)
rename `miss' m
rename `one' count
sort var pattern
egen varmiss = sum(m*count), by(var)

* Add an initial non-missing pattern with count 0
* (so that first bar represents observed for all variables)
local n = _N
qui expand 2 if pattern==1
qui replace count = 0 if _n>`n'
qui replace pattern = 0 if _n>`n'
qui replace m = 0 if _n>`n'
sort var pattern

* Collapse into bars of same missingness
gen first = pattern==0 | m!=m[_n-1]
qui by var: gen bar = sum(first)
if "`debug'"=="debug" di "Before collapse 2"
if "`debug'"=="debug" l, sepby(var)
qui collapse (min) pattern (sum) count (mean) m varmiss, by(var bar)
rename pattern firstpattern // only for debugging
label val var `varlab'
if "`debug'"=="debug" di "After collapse 2"
if "`debug'"=="debug" l, sepby(var)

* Prepare bar colours
summ bar, meanonly
local nbars = r(max)
di as result `nbars' as text " bars used"
forvalues i=1/`nbars' {
    if mod(`i',2)==1 local baropt `baropt' bar(`i', col(`obscolor'))
    if mod(`i',2)==0 local baropt `baropt' bar(`i', col(`misscolor'))
}

// DRAW GRAPH
if "`varsort'" != "novarsort" local sortopt sort(varmiss)
if "`varlabel'" != "" local varlabelopt label(`varlabel')
local graphcmd graph hbar (sum) count, ///
    over(bar) over(var, gap(0) `sortopt' `reverse' `varlabelopt') ///
    asyvars stack `baropt' ytitle(`nobs' `indivsname') ///
    legend(order(1 2) label(1 "Observed") label(2 "Missing")) ///
    `options' yscale(range(0 `nobs')) ylabel(none) outergap(0)
capture noisily `graphcmd'
if _rc==1003 {
    di as error "Sorry - too many bars are needed: " _c
    if "`idsort'"=="noidsort" di as error "try without the noidsort option"
    else di as error "try using fewer variables"
}
if _rc exit _rc

// OPTIONALLY KEEP RESULTS IN MEMORY
if "`clear'" == "clear" {
    window push `graphcmd'
    di "Graphed data are in memory"
    di "Page-up to load graph command"
    restore, not
}

end