************************************ *!Author: Laura C Whiting *!Contact: support@surveydesign.com.au *!Date: 08 January 2024 *!Version: 2.0 ************************************ /* syntax is epitable2 xvar, dp(integer) cilimiter(string) title(string) notes(multiple strings with double-quotes) include long export showp dp is number decimal places to display coefficients and confidence intervals, default is 2 cilimiter is what sits between lower and upper bounds, default is hyphen title will add a title to your table notes allows you to add one or more notes to your table. Each note must be enclosed in double-quotes, and notes should be separated from each other by a single space include will include all covariates in the table, default is to show xvar only long will change the table layout from wide to long, default is wide export will set up the table for immediate export to a word document through Stata's putdocx command. If the export option is given, a putdocx document is opened (if one was not already open) and the table is appended to the document. The export option does not save the putdocx document, so if you want to keep adding to it you can. showp will show all the p-values for the given xvar and any covariates (if shown). All p-values except trend are hidden by default. */ ************************************* program epitable2, nclass version 18.0 syntax varname(fv) [, DP(integer 2) CIlimiter(string) TItle(string) NOtes(string asis) INClude LONG EXPORT SHOWp] // if no cilimiter given apply default if "`cilimiter'" == "" { local cilimiter = "-" } //Check if varname given is factored (i.) or continuous(c.) if strpos("`varlist'", ".") == 0 { //Check the continuous xvar given was continuous in the model commands as well quietly collect levelsof colname if strpos(" `s(levels)'", " `varlist' ") == 0 { display as error "Your chosen xvar is not found in the preceding model commands." display as error "If your xvar is a factor variable, make sure to include the i. prefix." exit 111 } //Set the prefix for a continuous variable, as a continuous variable cannot show quartiles local prefix = "c." } else { //Set the prefix for a factor variable tokenize "`varlist'", parse(".") local prefix `1'`2' local varlist `3' //Check the factor xvar given was factored in the model commands as well quietly levelsof `varlist', local(fvlevels) local fvstart : word 1 of `fvlevels' quietly collect levelsof colname if strpos(" `s(levels)'"," `fvstart'.`varlist' ") == 0 { display as error "Your chosen xvar is not found in the preceding model commands." display as error "If your xvar is continuous no prefix is needed." exit 111 } } //Setup new column for trend p if factor variable xvar if "`prefix'" == "i." { local newcol = "`varlist'FV" //If variable is factor, model commands must be rerun with variable as continuous to get overall p-value rerun `prefix'`varlist' //levels of cmdset will double after rerun, we only want the first half, and the rerun sub-command returns these for us to use local cmdlevels = "`s(cmdlevels)'" //hide coefficients for the newcol level in colname only, as it contains the second set of models from rerun and we only want the p-values quietly collect remap result[_r_b] = result[hide], fortags(colname[`newcol']) quietly collect remap result[_r_ub] = result[hide], fortags(colname[`newcol']) quietly collect remap result[_r_lb] = result[hide], fortags(colname[`newcol']) } else { //If xvar is continuous, the newcol level in colname is set to model p-values instead local newcol = "`varlist'C" } // collect ci's into single level collect composite define myci = _r_lb _r_ub, delimiter("`cilimiter'") replace // apply brackets collect style cell result[myci], sformat("(%s)") // collect coeff and combined ci's into single level collect composite define coefci2 = _r_b myci, trim override replace // apply decimal places for coeff and ci's collect style cell result[coefci2], nformat(%9.`dp'f) // hide the level headers of dimension result collect style header result, level(hide) // centre columns collect style column, dups(center) // change border and font collect style cell, border(right, pattern(nil)) font(Times New Roman) // count number of models if "`prefix'" == "c." { //number of models will be number of cmdset levels for continuous xvar quietly collect levelsof cmdset local levels = "`s(levels)'" } else { //number of models will be the first half of cmdset for factor xvar, given by cmdlevels set previously local levels = "`cmdlevels'" } // apply row title "Model X" for each model foreach item in `levels' { quietly collect label values cmdset `item' "Model `item'" } //The final p-value column shows model p-value instead when xvar is continuous if "`prefix'" == "c." { // add colname tag for results[p] so a column header can be set quietly collect addtags colname[`newcol'], fortags(result[p]) //Convert result[p] to result[_r_p] so p-values appear in the same column/row quietly collect remap result[p] = result[_r_p], fortags(colname[`newcol']) collect label values colname `newcol' "Model", modify } else { //Re-label newcol as trend p-value for factor xvar collect label values colname `newcol' "Trend p-value", modify // was just "Model" so "p-value" would appear in result[_r_p], but this causes a crash so they are combined for now } //Label p-value and coefficient result levels appropriately collect label values result _r_p "p-value", modify collect label values result coefci2 "[Coef. (__LEVEL__%)]", modify // apply p-value formatting collect style cell result[_r_p], nformat(%5.3f) minimum(0.001) //Set table to include all covariates if asked if "`include'" != "" { //Collect all covariates quietly collect levelsof colname local collevels = "`s(levels)'" //Remove c1 c2 c3 c4 as these aren't covariates local collevels = subinstr("`collevels'", "c1 c2 c3 c4", "", .) //Remove newcol as we apply this separately elsewhere local collevels = subinstr("`collevels'", "`newcol'", "", .) //begin local vars to hold column order local vars = "[" //Add varlist categories in order if i.xvar given if "`prefix'" == "i." { quietly levelsof `varlist', local(level) foreach item in `level' { local collevels = subinstr("`collevels'", "`item'.`varlist'", "", .) local vars = "`vars'" + " `item'.`varlist'" } //Remove the continuous version of xvar so i.xvar is not shown twice local collevels = subinstr("`collevels'", "`varlist'", "", .) } //Add just varlist if xvar is continuous else { local collevels = subinstr("`collevels'", "`varlist'", "", .) local vars = "`vars'" + " `varlist'" } //Default is to show only trend p-values, so newcol has to come before covariates if "`showp'" == "" { local vars = "`vars'" + " `newcol']" //These are needed to set the table layout at the end, other covariates are held separately in vars2 local vars2 = "colname[`collevels']" local rhash = "#result[coefci2]" } //If all model p-values are asked for, place model p-values from newcol at the end else { local vars = "`vars'" + " `collevels' `newcol']" } //Remove any extraneous spaces local vars = stritrim("`vars'") //Style column headers to appear appropriately collect style column, nodelimiter dups(center) position(bottom) width(asis) } //Set table to include only xvar by default else { //Add varlist categories in order if i.xvar is given if "`prefix'" == "i." { quietly levelsof `varlist', local(level) local vars = "[" foreach item in `level' { local vars = "`vars'" + " `item'.`varlist'" } //Add the newcol level at the end so it appears on the right side of the table local vars = "`vars'" + " `newcol']" } //Add just varlist if xvar is continuous else { local vars = "[`varlist' `newcol']" } //Style column headers to appear appropriately collect style column, nodelimiter dups(center) position(bottom) width(asis) collect style cell cell_type[column-header], halign(center) } // hide all other model p-values unless asked to keep them if "`showp'" == "" { quietly collect remap result[_r_p] = result[hide], fortags(colname[`varlist']) //Remap vars2 if it exists, as remap command would run if vars2 was missing but apply to all, which breaks the table if "`vars2'" != "" { quietly collect remap result[_r_p] = result[hide], fortags(`vars2') } } // Set table layout as wide by default if "`long'" == "" { //Set layout quietly collect layout (cmdset) (colname`vars'#result[coefci2 _r_p] `vars2'`rhash') //show colname[`newcol'] header collect style header colname[`newcol'], level(label) //Set column headers and subheaders appropriately collect style column, nodelimiter dups(center) position(bottom) width(asis) if "`showp'" != "" { //show result headers and label collect style header result[_r_p], level(label) collect style header result[coefci2], level(label) //Set newcol label as trend if xvar is factor variable if "`prefix'" == "i." { collect label levels colname `newcol' "Trend", modify } } //Show result headings for continuous wide if "`prefix'" == "c." { collect style header result[_r_p], level(label) collect style header result[coefci2], level(label) } } //Set table layout as long if asked else { if "`prefix'" == "i." { quietly levelsof `varlist' local start : word 1 of `r(levels)' if "`showp'" == "" { //Convert newcol p-values to first level of xvar p-values so it shows in the top-level that has no coefficients quietly collect remap colname[`newcol'] = colname[`start'.`varlist'], fortags(result[_r_p]) quietly collect label levels result _r_p "Trend p-value", modify } else { //Do not set newcol p-values to first level of xvar if other p-values are also shown as it gets lost quietly collect label levels colname `newcol' "Trend", modify } } else { //If xvar is continuous and p's aren't shown, set the p-value for newcol as the norm and label headers appropriately if "`showp'" == "" { collect label levels result _r_p "Model p-value", modify quietly collect remap colname[`newcol'] = colname[`varlist'], fortags(result[_r_p]) } } //Set the layout as long-ways, vars 2 is separate as it could be empty quietly collect layout (colname`vars' `vars2') (cmdset#result[coefci2 _r_p]) //Set row-headers to stack style so they indent collect style row stack, nodelimiter nospacer indent length(.) wrapon(word) noabbreviate wrap(.) truncate(tail) //Show level labels for the results (column headers) collect style header result[coefci2], level(label) collect style header result[_r_p], level(label) if "`showp'" == "" { //If only trend p-values asked for then hide newcol header collect style header colname[`newcol'], level(hide) } else { //If all p-values asked for then show newcol header collect style header colname[`newcol'], level(label) } } //Show model labels as header collect style header cmdset, level(label) // Apply table title if given if "`title'" != "" { collect title "`title'" } // Apply table note(s) if given if `"`notes'"' != "" { foreach item in `notes' { collect notes "`item'" } } //Display the table in the results pane collect preview //If the export option is given, add the table to a putdocx document if "`export'" != "" { //Check if a putdocx document is already open capture quietly putdocx describe //If there is no current putdocx document, open one and set it up for table export if _rc != 0 { putdocx clear putdocx begin, landscape putdocx paragraph collect style putdocx, layout(autofitcontents) //Add the table to the opened putdocx document putdocx collect } //If there is a current putdocx document, append the table to the open document else { putdocx paragraph collect style putdocx, layout(autofitcontents) putdocx collect } } end //This program re-runs models with factor xvar //It is necessary as the xvar p-values are used as trend values, and are not generated when xvar is factor program rerun, sclass syntax varname(fv) //Set macros for original variable, pull prefix and variable separately local repvar = "`varlist'" tokenize "`varlist'", parse(".") local prefix `1'`2' local varlist `3' //Create a table containing only the command-line commands quietly collect layout (cmdset) (result[cmdline]) collect style header result, level(hide) collect style header cmdset, level(hide) collect style cell, halign(left) //Save the initial set of cmdset levels, as these are about to double and we need to remap the second set to the first set quietly collect levelsof cmdset local cmdlevels = "`s(levels)'" //Set tempfile for exported text table, tempname for Stata's file commands tempfile c_commands tempname commandfile //Export table as txt to temporary file quietly collect export `c_commands', as(txt) //Open exported table txt file as read in Stata file open `commandfile' using `c_commands', read //Read the first line and check for border, if border found read second line then begin file read `commandfile' line if strpos("`line'", "---") != 0 { file read `commandfile' line } //Set macro to count through iterations and use as cmdset match tag local count = 1 //While end of file not reached, loop through lines of text in the text table while r(eof) == 0 { //Remove any erroneous whilespace local cline = strtrim("`line'") local cline = stritrim("`cline'") //If a border is not encountered, sub i.xvar for xvar and run the command, collecting the results if "`cline'" != "" & strpos("`cline'", "---") == 0 { local cline = subinstr("`cline'", "`repvar'", "`varlist'", 1) quietly collect, tag(match[`count']): `cline' } local ++count file read `commandfile' line } //Close text table file once read file close `commandfile' //Remap the first set of models temporarily to avoid collect errors quietly collect remap cmdset[`cmdlevels'] = model[`cmdlevels'] //levelsof match were our tag for the second model set quietly collect levelsof match local levels = "`s(levels)'" foreach match of local levels { //For each model of the second set, remap the xvar to newcol so it can be added to the first set without overwritting anything quietly collect remap colname[`varlist'] = colname[`varlist'FV], fortags(match[`match']) //Remap xvar p-values from second set to first set quietly collect remap match[`match'] = model[`match'], fortags(result[_r_p] colname[`varlist'FV]) } //Get rid of original cmdset levels as they conflict quietly collect remap cmdset = hide //Set new model levels as cmdset levels quietly collect remap model = cmdset //Return original cmdset levels for use in the rest of epitable2 sreturn local cmdlevels = "`cmdlevels'" end