************************************ *!Author: Laura C Whiting *!Contact: support@surveydesign.com.au *!Date: 08 January 2024 *!Version: 2.25 ************************************ /* syntax is epitable3 command depvar xvar covars [if] [in] [weights], by(groupvars) [dp(integer) cilimiter(string) title(string) notes(multiple strings with double-quotes) ptrendvar(if it isn't xvar) aftercovars(if running a model like mixed effects) collection(name) export opcon *Other command options*] command is REQUIRED. This is the regression command that is going to be used to generate coefficients and p-values. For example, logistic or regress depvar is REQUIRED. This is your dependent variable xvar is REQUIRED. This is the variable of interest. Referred to in comments as xvar but appears in code as macro `varlist' covars is REQUIRED. These are your independent variable(s). by is REQUIRED. These are your group variables that xvar will be broken down into ptrendvar is optional. It will calculate the p for trend (collection c2) based on the variable you give here rather than xvar. Designed so you can apply median in each quartile for ptrend calculations dp is decimal points, default is 2 so only specify if you want more or less than 2 decimal places for coeff/ci's cilimiter is what sits between lower and upper bounds, default is hyphen - so only use if you want something other than a 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 aftercovars is REQUIRED for MIXED regressions, or any other regression where you need to put something additonal after the independent variables are given collection allows you to name the collection the table is output to. Default is table3. Give the replace option after the name to replace a collection already in memory. E.g. collection(table3c, replace) 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. opcon will run the contrast section (p for interaction) with the opposite variable type to the one given as xvar. For example, if you give i.xvar and opcon the contrast will run on c.xvar, and vice versa. Otherwise, contrast is run with the same prefix you gave xvar. You should be able to add other options that are accepted by the original command, but this has not been extensively tested. */ ************************************* program epitable3, nclass version 18.0 syntax anything [if/] [in] [fw aw pw iw], BY(varlist min=1 numeric) [ PREcmd(string) DP(integer 2) /// CIlimiter(string) TItle(string) NOtes(string asis) PTRendvar(varname numeric) /// AFTERcovars(string) COLLECTion(string) EXPORT OPCON *] //Separate out varlist from model gettoken cmd varlist : anything if "`cmd'" == "stcox" { gettoken xvar varlist : varlist local depvar = "" } else { gettoken depvar varlist : varlist gettoken xvar varlist : varlist } // if no cilimiter given apply default if "`cilimiter'" == "" { local cilimiter = "-" } //Check collection names c1 c2 and c3 are not in use quietly collect dir foreach item in c1 c2 c3 { if ustrpos("`s(collections)'", " `item'") > 0 | ustrpos("`s(collections)'", "`item' ") > 0 { display as error "Collection `item' already exists, using temporary collection name instead." //Use a tempname for the collection if c1 or c2 or c3 are already generated tempname `item' } else { local `item' = "`item'" } } //If no collection name given apply default if "`collection'" == "" { quietly collect dir //Check default doesn't already exist, if it does exit if ustrpos("`s(collections)'", "table3") > 0 { display as error `"Default table name "table3" is in use."' display as error "Please specify a table name with the collection({it:name}) option." exit 110 } local collection = "table3" } else { //If collection name given, check for replace option and apply tokenize "`collection'", parse(",") quietly collect dir if ustrpos("`s(collections)'", strtrim("`1'")) > 0 & strtrim("`3'") != "replace" { display as error "Collection name `1' currently in use." display as error "Please give the replace option if you wish to overwrite it." exit 110 } if strtrim("`3'") == "replace" { capture collect drop `1' } local collection = strtrim("`1'") } //Use temporary names for the collections //Set up any command-specific options given if "`options'" != "" { //Pick up and remove hidden "indent" option so it isn't applied to the model commands if ustrpos("`options'", "indent") > 0 { local options = subinstr("`options'", "indent", "", .) //Create hidden "indent" option for use later, if given local indent = "indent" local options = stritrim("`options'") } local options = ", " + "`options'" } //Check if varname given is factored (i.) or continuous(c.) if strpos("`xvar'", ".") == 0 { local prefix = "c." } else { tokenize "`xvar'", parse(".") local prefix `1'`2' local xvar `3' } //Set up if to work together with the if that is already needed if "`if'" != "" { local contif = "if " + "`if'" local if = "& " + "`if'" } //create the first collection, for the coefficients quietly collect create `c1' //This loops through each by and runs the cmd with depvar, covars, and i. or c. xvar. The collect prefix command allows for table building. foreach var of varlist `by' { quietly levelsof `var', local(level) foreach item in `level' { quietly collect, tags(`var'[`item']): `precmd' `cmd' `depvar' `prefix'`xvar' `varlist' if `var' == `item' `if' `in' `weight' `exp' `aftercovars' `options' } } //redefine varlist tag if xvar is continuous, in case of later contrast as non-continuous if "`prefix'" == "c." { local newxvar = "`xvar'C" quietly collect remap colname[`xvar'] = colname[`newxvar'] } else { local newxvar = "`xvar'" } // 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) // change border and font collect style cell, border(right, pattern(nil)) font(Times New Roman) // Set the layout for this collection. Generally not seen but here for reference quietly collect layout (`by') (colname[`newxvar']#result[coefci2]) ******************************* //Create the second collection, for the trend p-values. quietly collect create `c2' //Check if alternative variable requested for p-value calculation if "`ptrendvar'" == "" { local pvar = "`xvar'" } else { local pvar = "`ptrendvar'" } //This loops through each by and runs the cmd with depvar, covars, and c.xvar. The collect prefix command allows for table building. local cmdcount = 1 foreach var of varlist `by' { quietly levelsof `var', local(level) foreach item in `level' { quietly collect, tags(`var'[`item']): `precmd' `cmd' `depvar' c.`pvar' `varlist' if `var' == `item' `if' `in' `weight' `exp' `aftercovars' `options' capture collect remap cmdset[`cmdcount'] = cmdset[1] local ++cmdcount } } //Remapping xvar is necessary for layout changes later on, otherwise once collections are combined the p-values disappear quietly collect remap colname[`pvar'] = colname[`xvar'p] // apply p-value formatting collect style cell result[_r_p], nformat(%5.3f) minimum(0.001) // Show correct column header collect style header result[_r_p], title(hide) level(label) //Set the layout for this collection. Generally not seen but here for reference quietly collect layout (`by') (colname[`xvar'p]#result[_r_p]) ***************************** //Create the third collection, for the interaction p-values quietly collect create `c3' if "`opcon'" != "" { if "`prefix'" == "c." { local prefix = "i." } else if "`prefix'" == "i." { local prefix = "c." } else { local prefix = "`prefix'" } } //This loops through each by to run the cmd with depvar, covars, and the interaction between c.xvar and each by. P-values are collected through contrast. foreach var of varlist `by' { quietly `precmd' `cmd' `depvar' `prefix'`xvar'##`var' `varlist' `contif' `in' `weight' `exp' `aftercovars' `options' quietly collect: contrast `prefix'`xvar'#`var' if "`opcon'" != "" & "`prefix'" == "i." { local c3varlist = "`xvar'FV" quietly collect remap colname[`xvar'] = colname["`c3varlist'"], fortags(result[p]) } else { local c3varlist = "`xvar'" } } //This loops through and applies a new tag for each by that will be by[.t]. This is so that once collections are combined the interaction p-value for each by will appear alongside the p-values for each subgroup. forvalues i = 1/`: word count `by'' { quietly collect addtags `: word `i' of `by''[.t], fortags(cmdset[`i']#result[p]) local grouplabel = "`: word `i' of `by''" collect label levels `grouplabel' .t `"`: variable label `grouplabel''"', modify } // apply p-value formatting collect style cell result[p], nformat(%5.3f) minimum(0.001) // Show correct column header collect style header result[p], title(hide) level(label) // Sets the layout for this collection. Generally not seen but here for reference quietly collect layout (`by') (result[p]) ***************************************** // Combine the individual collections to create one table quietly collect combine `collection' = `c1' `c2' `c3' // Name the column header for subgroup p-values collect label values colname `xvar'p "p for trend", replace // Name the column header for interaction p-values collect label values result p "p for interaction", replace // Hide column header for p-value and instead show colname[`varlist'p] header as set above collect style header result[_r_p], level(hide) collect style header collection, level(hide) collect style header colname[`xvar'p], level(label) // Center column headers collect style column, nodelimiter dups(center) position(bottom) width(asis) //Set alignments for "tabbed" look as default if "`indent'" == "" { // Set row headers to right align for "tabbed" look collect style cell cell_type[row-header], halign(right) // Set row headers for all by[.t] levels to left align for "tabbed" look foreach var of varlist `by' { collect style cell `var'[.t]#cell_type[row-header], halign(left) } } // Re-attach label header if it is missing collect label levels colname `xvar' "`: variable label `xvar''" // Set layout so by[.t] appears at the top instead of the bottom of each table section foreach var of varlist `by' { quietly collect levelsof `var' local levels = "`s(levels)'" local newlevels : subinstr local levels ".t" "", all local newlevels = ".t `newlevels'" local layout = "`layout'" + " `var'[`newlevels']" } // The final layout for the combined collection. This is the only layout that most will see. collect layout (`layout') (colname[`newxvar']#result[coefci2]#collection[`c1'] colname[`xvar'p]#result[_r_p]#collection[`c2'] result[p]) // Apply table title if given if "`title'" != "" { collect title "`title'" collect style title, font(Times New Roman) } // Apply table note(s) if given if `"`notes'"' != "" { foreach item in `notes' { collect notes "`item'" collect style notes, font(Times New Roman) } } // Drop preceding collections, only table3 is needed for export collect drop `c1' `c2' `c3' // Set row headers for all levels of by except [.t] to add an indent - hidden option if "`indent'" != "" { collect style cell cell_type[row-header], halign(left) foreach var of varlist `by' { quietly collect levelsof `var' local levels = "`s(levels)'" local levcount : word count `levels' forvalues i = 1/`levcount' { quietly collect label list `var' if "`s(level`i')'" != ".t" { collect label levels `var' `s(level`i')' "\`=char(255)' \`=char(255)' `s(label`i')'", modify } } } } //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) //Add the table to the opened putdocx document putdocx collect } } end