******************************************************************************** * allcatplot ******************************************************************************** /* The allcatplot Stata program does not plot all or any cat(s) on a graph. Instead, it ensures that all predefined response categories for a variable, including those that were not selected, are included in a graph. Traditional plotting methods omit such unselected categories, potentially skewing interpretation. allcatplot uses the predefined value labels to identify and label such omitted response options and add them to the graph as zero-height bars. Additionaly, it also supports custom lists, labels and graph customization directly through the program syntax. This program is especially useful for surveys and assessments utilizing likert scales or other structured response options, where it offers a holistic view of all potential responses in the graph, including those that weren't selected, thereby ensuring a comprehensive understanding of the full range of response options. Additionally, allcatplot can be used to do the opposite of what its name suggests: exclude specific categories from the graph for clarity or analytical purposes. For instance, in a dataset with varied selections across options A, B, C, and D for a specific variable, the program can be configured to display only the responses for options A, B, and C (or any other combination of the four in any order of your choice). This doesn't eliminate the data for option D but merely hides it from the graph, with the displayed bar heights accurately reflecting the frequencies or percentages of responses for the displayed options as per the total count. */ cap prog drop allcatplot program allcatplot version 11.2 syntax varlist (max=1) /// [if] /// [in], /// [Over(string asis)] /// [List(string asis)] /// [RElabel(string asis)] /// [Freq] /// [Sort] /// [Title(string asis)] /// [Missing] /// [Recast(string asis)] /// [Graphopts(string asis)] qui { ******************************************************************************** * Section 1: Standard set-up with details of program dependencies and temp files ******************************************************************************** /// this program requires the elabel program to search through predefined response categories cap which elabel if _rc == 111{ display as err " This module requires the 'elabel' package to search through predefined response categories." display as err " You need to run:" display as err " ssc install elabel, replace" exit 198 } /// and it requires splitvallabels to format long value labels in graphs cap which splitvallabels if _rc == 111{ display as err " This module requires the 'splitvallabels' package to format long value labels." display as err " You need to run:" display as err " ssc install splitvallabels, replace" exit 198 } /// by using preserve and restore, if the ado file aborts, the original data will be automatically restored /// advice provided by Kit Baum preserve /// locals for N and missing obs /// the locals for the number of observations and missing have to be calculated prior to using marksample as marksample correctly drops all missing values of varlist count if !(missing(`varlist')) local N `r(N)' count if missing(`varlist') local miss `r(N)' /// sample indicator for maping the [IF] condition marksample touse, strok keep if `touse' == 1 /// this bit of code ensures that our program works with string variables as well ds `varlist', has(type string) local string_var `r(varlist)' if "`string_var'" != "" { encode `varlist', gen (`varlist'e) drop `varlist' rename `varlist'e `varlist' } if "`over'" != "" { ds `over', has(type string) local string_var `r(varlist)' if "`string_var'" != "" { encode `over', gen (`over'e) drop `over' rename `over'e `over' } } ******************************************************************************** * Section 2: Setting locals that will be used later or in our graph ******************************************************************************** /// create locals for var label and value label local var_lab : variable label `varlist' /// if there is no var label, use the var name if "`var_lab'" == "" { local var_lab `varlist' } local val_lab: value label `varlist' /// allow for various graph styles with vertical bars as default local graph_style bar if "`recast'" != "" { local graph_style `recast' } /// the graph defauls to a percentage graph if "`freq'" == "" { local graph_dist "percent" local graph_range "ytitle(Percent) ylabel(0(20)100)" local graph_decimal "%9.1f" } /// but works effectively for frequency distributions as well if "`freq'" != "" { local graph_dist "_freq" local graph_range "ytitle(Frequency)" local graph_decimal "%9.0f" } /// allow for sorting later if "`sort'" != "" { local sort "sort(1) descending" } /// or default to the original order of the list provided by the user. Note: order_var is generated later. if "`sort'" == "" { local sort "sort(order_var)" } /// default title is the var label (or var name) if "`title'" == "" { local title "`var_lab'" } /// missing observations are added as a subtitle (if requested) if "`missing'" == "" { local subtitle "Number of observations = `N'" } if "`missing'" != "" { local subtitle "Number of observations = `N' and missing observations = `miss'" } ******************************************************************************** * Section 3: Which values will be mapped? ******************************************************************************** /// now, we move on to the values that will be mapped. if the user has requested for a default list of values, that request will be prioritized and will already be stored as a local called 'list' /// if the user has not requested for a customized list if "`list'" == "" { /// check to see if the varlist has a value label attached and populate the list local with all possible values of the variable if "`val_lab'" != "" { elabel list `val_lab' local list = "`r(values)'" } /// in the absence of value labels and a customized list, default to values in the dataset if "`val_lab'" == "" { levelsof `varlist', local(list) } } /// if there is no value label attached to the variable, create a temporary one for the purposes of this program if "`val_lab'" == "" { local val_lab allcatplot_lab } ******************************************************************************** * Section 4: Relabelling directly through the program syntax ******************************************************************************** /// if the user has requested specific labels if "`relabel'" != "" { global call "" local list_len : word count `list' local lab_length : word count `relabel' /// check if lab_length is greater than list_len and drop redundant labels if (`lab_length' > `list_len') { local lab_length `list_len' } /// loop through each label and corresponding list value forval i = 1/`lab_length' { /// Extract the ith label and list value local current_list = word("`list'", `i') local current_label = subinstr(word("`relabel'", `i'), "_", " ", .) /// Populate the global call with the list and corresponding labels global call "${call} `current_list' "`current_label'"" } label define `val_lab' $call, modify label values `varlist' `val_lab' macro drop call } ******************************************************************************** * Section 5: Simple graph with no disaggregation by another variable ******************************************************************************** if "`over'" == "" { contract `varlist', percent(percent) foreach i of numlist `list' { /// this additional step is necessary because the varlist may have negative values and the negative sign (-) isn't allowed as part of a var name in Stata. Identified as a potential issue by Prabhmeet Kaur. /// this step changes the negative signs to underscores when the variables are generated local varname temp_v`i' if `i' < 0 { local varname = subinstr("`varname'", "-", "_", .) } gen `varname' = 0 replace `varname' = `graph_dist' if `varlist' == `i' } collapse (sum) temp_v* * Reshape gen id = _n reshape long temp_v, i(id) j(val_lab) string /// adding the negative signs back replace val_lab = subinstr(val_lab, "_", "-", .) destring val_lab, replace label values val_lab `val_lab' /// when you reshape, Stata ordes the values in ascending order rather than the structure of the temporary var and so we need a fix /// this bit of code ensures that the original order as requested by the user is used for the graph gen order_var = . local i = 1 foreach val in `list' { replace order_var = `i' if val_lab == `val' local i = `i' + 1 } /// splitvallabels ensures that values don't clash with each other but move to separate lines splitvallabels val_lab, length(9) nobreak recode graph `graph_style' (asis) temp_v, over(val_lab, `sort' label(labsize(small)) relabel(`r(relabel)')) /// title("`title'", size(medsmall)) /// subtitle("`subtitle'", size(small)) /// blabel(bar, format(`graph_decimal')) `graph_range' `graphopts' } ******************************************************************************** * Section 6: With disaggregation ******************************************************************************** if "`over'" != "" { tempfile loop save `loop', replace /// there may be no value label lists at all (even if there is a local for it) cap label save `val_lab' using temp_va_lab, replace local ov_lab: value label `over' cap label save `ov_lab' using temp_ov_lab, replace levelsof `over', local(categories) drop _all tempfile append save `append', emptyok replace /// we follow the same steps as earlier but for each category of 'over' foreach cat of local categories { use `loop', clear keep if `over' == `cat' contract `varlist' `over', percent(percent) foreach i of numlist `list' { local varname temp_v`i' if `i' < 0 { local varname = subinstr("`varname'", "-", "_", .) } gen `varname' = 0 replace `varname' = `graph_dist' if `varlist' == `i' } collapse (sum) temp_v* (first) `over' append using `append' save `append', replace } use `append', clear * Reshape gen id = _n reshape long temp_v, i(id) j(val_lab) string replace val_lab = subinstr(val_lab, "_", "-", .) destring val_lab, replace cap run temp_ov_lab cap label values `over' `ov_lab' cap run temp_va_lab cap label values val_lab `val_lab' gen order_var = . local i = 1 foreach val in `list' { replace order_var = `i' if val_lab == `val' local i = `i' + 1 } splitvallabels val_lab, length(9) nobreak recode graph `graph_style' (asis) temp_v, over(`over') over(val_lab, `sort' label(labsize(small)) relabel(`r(relabel)')) /// asyvars /// title("`title'", size(medsmall)) /// subtitle("`subtitle'", size(small)) /// blabel(bar, format(`graph_decimal')) legend(pos(6) row(1)) `graph_range' `graphopts' cap erase temp_ov_lab.do cap erase temp_va_lab.do } restore } end