program manybars, rclass version 16.0 if regexm(`"`0'"', "over\s*\(.*\).*over\s*\(") { di as err "Option {cmd:over()} may not be specified more than once." di "List all grouping variables in a single {opt overvars()} option." exit 198 } syntax varname [if] [in], Xvars(varlist) /// [ Stat(string) /// Displayvalues(numlist integer) /// OVERvars(varlist) /// Horizontal /// GRAphopts(string) /// ADDtolegend(string) /// NEWLegend(string) /// VARlabel /// NOWarn ] *Set the warning flag on or off if "`nowarn'" == "nowarn" local warn 0 else local warn 1 if "`horizontal'"=="horizontal" local call "graph hbar" else local call "graph bar" *Name `y' and validate as numeric local y "`varlist'" confirm numeric variable `y' marksample touse, novarlist *Number of nonmissing obs in sample qui count if `touse' local N_orig = `r(N)' *Validate the over variables if "`overvars'" != "" { foreach v of local overvars { confirm numeric variable `v' } } *Check for missing values of y if `warn' { qui count if missing(`y') & `touse' if `r(N)' > 0 { if "`overvars'" != "" { local ngvars: word count `overvars' if `ngvars' == 1 { qui levelsof `overvars', local(glevels) foreach lev of local glevels { qui count if missing(`y') & `touse' & `overvars'==`lev' if `r(N)' > 0 { local glab: label (`overvars') `lev' if `"`glab'"' != "" local glabstr " (`glab')" else local glabstr "" di as txt "Note: {bf:`y'} has {bf:`r(N)'} missing observation(s) where {bf:`overvars'==`lev'}`glabstr'." di as txt "These observations will not be graphed." } } } else { di as txt "Note: {bf:`y'} has {bf:`r(N)'} missing observation(s)." di "These observations will not be graphed." } } else { di as txt "Note: {bf:`y'} has {bf:`r(N)'} missing observation(s)." di "These observations will not be graphed." } } } *Check for missing x variables & over variables if `warn' { foreach x of varlist `xvars' `overvars' { qui count if missing(`x') & `touse' if `r(N)' > 0 { di as txt "Note: {bf:`x'} has {bf:`r(N)'} missing observation(s)." di "These observations will not be graphed." } } } *Extend markout to variables with missing values on y or xvars markout `touse' `y' `xvars' `overvars' *If stat() is unspecified, set default yvar statistic to mean if "`stat'" == "" local stat "mean" *Parse stat into `stat' and `stat_op' gettoken stat stat_op: stat, parse(",") local stat = ustrtrim("`stat'") *Validate stat if "`stat'" != "mean" & /// "`stat'" != "median" & /// "`stat'" != "mode" & /// "`stat'" != "min" & /// "`stat'" != "max" & /// "`stat'" != "count" & /// "`stat'" != "iqr" & /// "`stat'" != "mad" & /// "`stat'" != "mdev" & /// "`stat'" != "skew" & /// "`stat'" != "kurt" & /// "`stat'" != "sd" & /// "`stat'" != "total" { di as err "Error: '`stat'' is not a valid statistic." di "See {help manybars:help manybars} for the list of allowed statistics." exit 119 } *Validate displayvalues local nxvars: word count `xvars' local ndv: word count `displayvalues' if `ndv' == 0 { // if no display values are specified forvalues i = 1/`nxvars' { local d`i' = 1 } } else if `ndv' == 1 { // if one display values is specified forvalues i = 1/`nxvars' { local d`i' = `displayvalues' } } else if `ndv' == `nxvars' { // if all display values are specified forvalues i = 1/`nxvars' { local d`i': word `i' of `displayvalues' } } else { di as err "Error: the number of {it:displayvalues} must be 0, 1, or the number of {it:xvars}." exit 1001 } *Validate xvars *Confirm that xvar==displayvalue has >0 observations tokenize "`xvars'" local i = 0 while "`1'" != "" { local ++i confirm numeric variable `1' *Make sure x is categorical *Make sure y is observed at the relevant value of x qui count if `touse' & `1'==`d`i'' if `r(N)' == 0 { di as err "Error: no observations of {bf:`y'} at {bf:`1'=`d`i''}." exit 2000 } macro shift } *Remove leading comma from stat_op local stat_op = usubinstr(`"`stat_op'"',","," ",1) *Build the overs graph option local overs "" foreach v of local overvars { local overs `"`overs' over(`v')"' } *Can't specify both addtolegend and newlegend if `"`addtolegend'"' != "" & `"`newlegend'"' != "" { di as err "Error: options addtolegend() and newlegend() may not be combined." exit 184 } *Remove leading comma from addtolegend local addtolegend = ustrtrim(`"`addtolegend'"') if strpos(`"`addtolegend'"', ",") == 1 { local addtolegend = usubinstr(`"`addtolegend'"',","," ",1) } *Remove leading comma from newlegend local newlegend = ustrtrim(`"`newlegend'"') if strpos(`"`newlegend'"', ",") == 1 { local newlegend = usubinstr(`"`newlegend'"',","," ",1) } *Remove leading comma from graphopts local graphopts = ustrtrim(`"`graphopts'"') if strpos(`"`graphopts'"', ",") == 1 { local graphopts = usubinstr(`"`graphopts'"',","," ",1) } *New temporary frame to preserve original data & frame local fr "__manybars__temp" capt frame drop `fr' qui pwf local oldframe "`r(currentframe)'" frame copy `r(currentframe)' `fr' capture { frame `fr' { *Limit to [if] [in] sample qui drop if !`touse' qui count local N = `r(N)' *Sort by overvars, if any if "`overvars'" != "" sort `overvars' *Confirm xvar==displayvalue still has >0 observations after sample restriction tokenize "`xvars'" local i = 0 while "`1'" != "" { local ++i qui count if `1'==`d`i'' if `r(N)' == 0 { di as err "Error: no observations of {bf:`y'} at {bf:`1'=`d`i''} in the specified sample." exit 2000 } macro shift } *Create display variables tokenize "`xvars'" local i = 0 local leg "" local disp_vars "" while "`1'" != "" { local ++i tempvar disp`i' local disp_vars "`disp_vars' `disp`i''" if "`overvars'" != "" { capture noisily egen `disp`i'' = `stat'(`y') /// if `touse' & `1'==`d`i'', by(`overvars') `stat_op' } else { capture noisily egen `disp`i'' = `stat'(`y') /// if `touse' & `1'==`d`i'' `stat_op' } if _rc { di as err "Error in {it:stat_options}." exit _rc } *Check for missing values of the stat if `warn' { qui summ `disp`i'' if `1'==`d`i'' & `touse' if "`r(mean)'" == "" { di as txt "Warning: no values of {bf:`y'} at {bf:`1'==`d`i''}." } } *Legend labels if "`varlabel'" == "varlabel" { local lab: variable label `1' if "`lab'" == "" { local lab = "`1'" // if no var label, use varname } } else { local lab: label (`1') `d`i'' // if not varlabel, use value label } lab var `disp`i'' "`lab'" local leg `"`leg' label(`i' "`lab'")"' macro shift } *Replace with newlegend if specified if `"`newlegend'"' != "" local legend `"legend(`newlegend')"' else if `"`legend'"' == "" local legend `"legend(`leg' `addtolegend')"' *Draw the graph capt noisily `call' `disp_vars', ti(`stat' of `y') /// yti(`stat' of `y') `overs' `graphopts' `legend' if _rc { di as err "Invalid graph syntax. Check the help file section on {help manybars##graph_options:Graph and Legend Options}." exit _rc } } // end -frame- } // end -capture- *Change back to previous frame and clean up local rc = _rc capt frame drop `fr' frame change `oldframe' if `rc' { exit `rc' } *Return return scalar n_xvars = `nxvars' return scalar N_missing = `N_orig' - `N' return scalar N_graphed = `N' return local cmd = "manybars" if "`stat_op'" == "" return local stat = "`stat'" else return local stat = "`stat', `stat_op'" return local overvars = "`overvars'" return local xvars = "`xvars'" return local yvar = "`y'" end