*! _equaids_tabstars: private copy of tabstars 1.1.0, shipped with equaids *! Generated by tools/make_equaids_tabstars.py from the tabstars source: do *! not edit; every program, Mata function and global is renamed so that this *! copy never collides with an installed tabstars or with duvm's copy. Use the *! tabstars package itself for the full command and its help. program define _equaids_tabstars, rclass version 14.2 gettoken first : 0, parse(" ,") if "`first'" == "export" { gettoken first 0 : 0, parse(" ,") _eqts_export `0' exit } if "`first'" == "version" { * for the packages that depend on _equaids_tabstars: they test r(version) di as txt "_equaids_tabstars " as res "1.1.0" return local version "1.1.0" exit } syntax name(name=est id="matrix of estimates") [, SE(name) Pvalue(name) /// ROWLabels(string asis) COLLabels(string asis) ROWTitle(string) /// Title(string) TABnumber(string) ONEtab DEC(integer 3) /// LEVels(numlist >0 <1) SYMbols(string) MAXlen(integer 32) /// WIDth(integer 0) DF(integer 0) PAREN SETitle(string) noLEGend noSTars SEStars /// INDent(integer 2) LEFT EXPort(string) REPlace noDISplay NOWrap COMPact HLines(integer 2) COLGroups(string asis) noPREamble] confirm matrix `est' if "`se'" != "" { confirm matrix `se' if rowsof(`se') != rowsof(`est') | colsof(`se') != colsof(`est') { di as err "se() must have the dimensions of `est'" exit 503 } } if "`pvalue'" != "" { confirm matrix `pvalue' if rowsof(`pvalue') != rowsof(`est') | colsof(`pvalue') != colsof(`est') { di as err "pvalue() must have the dimensions of `est'" exit 503 } } if "`levels'" == "" local levels "0.10 0.05 0.01" local nlev : word count `levels' * levels are listed with their symbols, from the weakest to the strongest local prev 1 foreach l of local levels { if `l' >= `prev' { di as err "levels() must decrease: list them from the largest p-value to the smallest" exit 198 } local prev `l' } if "`symbols'" == "" { local symbols "" forvalues i = 1/`nlev' { local symbols `symbols' `=`i'*"*"' } } local nsym : word count `symbols' if `nsym' != `nlev' { di as err "symbols() must have one symbol per level" exit 198 } if `width' <= 0 local width = c(linesize) local ls0 = c(linesize) if "`nowrap'" != "" { * one block up to Stata's widest line; the Results window is widened for the display local width 255 set linesize 255 } if "`setitle'" == "" local setitle "standard errors" if "`onetab'" == "" & "`se'" == "" & "`pvalue'" == "" local onetab "onetab" * ---- export: a one-shot file, or the session opened by -_equaids_tabstars export begin- ---- local xtype "" local xfile "" local xsession 0 if "`export'" != "" { if "$EQUAIDS_TS_FILE" != "" { di as err "an export session is open (_equaids_tabstars export end closes it); export() cannot be used inside it" exit 198 } _eqts_xtype `export' local xtype "`s(type)'" local xfile "`s(file)'" capture confirm file "`xfile'" if _rc == 0 & "`replace'" == "" { di as err "file `xfile' already exists; specify replace" exit 602 } _eqts_export begin using "`xfile'", replace `preamble' local xsession 1 } else if "$EQUAIDS_TS_FILE" != "" { local xtype "$EQUAIDS_TS_TYPE" local xfile "$EQUAIDS_TS_FILE" * a Word session whose document has gone (putdocx clear, or an earlier * error) cannot be resumed: drop it rather than fail on every call if "`xtype'" == "docx" { capture putdocx describe if _rc { _eqts_export clear di as err "the Word export session on `xfile' was lost (its document no longer exists); the session has been dropped, reopen it with _equaids_tabstars export begin" exit 198 } } } mata: _eqts_main("`est'", "`se'", "`pvalue'", `"`rowlabels'"', `"`collabels'"', /// `"`rowtitle'"', `"`title'"', "`tabnumber'", "`onetab'" != "", `dec', /// "`levels'", `"`symbols'"', `maxlen', `width', `df', "`paren'" != "", /// `"`setitle'"', "`legend'" == "", "`stars'" == "", "`sestars'" != "", /// `indent', "`left'" != "", "`display'" == "", "`xtype'", "`xfile'", "`compact'" != "", `hlines', `"`colgroups'"') set linesize `ls0' if "`xtype'" == "xlsx" | "`xtype'" == "docx" _eqts_office "`xtype'" "`xfile'" if `xsession' _eqts_export end return local title `"`title'"' return local tabnumber "`tabnumber'" return scalar rows = rowsof(`est') return scalar cols = colsof(`est') return local levels "`levels'" return local symbols `"`symbols'"' if "`xfile'" != "" return local export "`xfile'" end * --------------------------------------------------------------------------- * the type of an export file from its extension program define _eqts_xtype, sclass local f `"`0'"' local f : subinstr local f `"""' "", all local ext = lower(substr("`f'", strrpos("`f'", ".") + 1, .)) if !inlist("`ext'", "tex", "csv", "md", "xlsx", "docx") { di as err "export: the file must end in .tex, .csv, .md, .xlsx or .docx" exit 198 } sreturn local type "`ext'" sreturn local file "`f'" end * --------------------------------------------------------------------------- * _equaids_tabstars export begin using file [, replace] / _equaids_tabstars export end program define _eqts_export gettoken sub 0 : 0, parse(" ,") if "`sub'" == "begin" { syntax using/ [, REPlace noPREamble] if "$EQUAIDS_TS_FILE" != "" { di as err "an export session is already open on $EQUAIDS_TS_FILE; close it with _equaids_tabstars export end, or drop it with _equaids_tabstars export clear" exit 198 } _eqts_xtype `using' local type "`s(type)'" local file "`s(file)'" capture confirm file "`file'" if _rc == 0 & "`replace'" == "" { di as err "file `file' already exists; specify replace" exit 602 } if "`type'" == "docx" { putdocx clear putdocx begin } else if "`type'" == "xlsx" { qui putexcel set "`file'", replace global EQUAIDS_TS_ROW 1 } else { mata: _eqts_newfile("`file'", "`type'", "`preamble'" == "") } global EQUAIDS_TS_FILE "`file'" global EQUAIDS_TS_TYPE "`type'" global EQUAIDS_TS_N 0 global EQUAIDS_TS_PRE = ("`preamble'" == "") exit } if "`sub'" == "end" { if "$EQUAIDS_TS_FILE" == "" { di as err "no export session is open" exit 198 } local type "$EQUAIDS_TS_TYPE" local file "$EQUAIDS_TS_FILE" local n $EQUAIDS_TS_N if "`type'" == "docx" capture qui putdocx save "`file'", replace else if "`type'" == "xlsx" capture putexcel clear else capture mata: _eqts_endfile("`file'", "`type'", $EQUAIDS_TS_PRE) local rc = _rc * the session is over whether or not the file could be written; * otherwise a failed save (file open in Word) would leave a dead * session that breaks every later call macro drop EQUAIDS_TS_FILE EQUAIDS_TS_TYPE EQUAIDS_TS_N EQUAIDS_TS_ROW EQUAIDS_TS_PRE if `rc' { di as err "could not write `file' (is it open in another application?); the export session has been dropped" exit `rc' } * a clickable link to the file (absolute path, opened by the application of its type) local full "`file'" if !strpos("`full'", ":") & !inlist(substr("`full'", 1, 1), "/", "\") { local full "`c(pwd)'/`full'" } local full : subinstr local full "\" "/", all di as txt "(" as res "`n'" as txt " table(s) written to " `"{browse "`full'":`file'}"' as txt ")" exit } if "`sub'" == "clear" { * abandon the session without writing anything if "$EQUAIDS_TS_TYPE" == "docx" capture putdocx clear else if "$EQUAIDS_TS_TYPE" == "xlsx" capture putexcel clear else if "$EQUAIDS_TS_FILE" != "" capture erase "$EQUAIDS_TS_FILE" macro drop EQUAIDS_TS_FILE EQUAIDS_TS_TYPE EQUAIDS_TS_N EQUAIDS_TS_ROW EQUAIDS_TS_PRE exit } di as err "_equaids_tabstars export begin using file [, replace] / _equaids_tabstars export end / _equaids_tabstars export clear" exit 198 end * --------------------------------------------------------------------------- * Excel and Word: the cells were left in locals by Mata (ts_*), one or two tables program define _eqts_office args type file local ntab = $EQTS_NTAB forvalues t = 1/`ntab' { local R = ${EQTS_R`t'} local C = ${EQTS_C`t'} local head `"${EQTS_HEAD`t'}"' local rowtitle `"${EQTS_ROWTITLE}"' local hasS = ${EQTS_HASS`t'} if "`type'" == "xlsx" { * Excel: a starred value stays a NUMBER (usable in formulas); the * stars are shown by a custom number format, 0.000\*\*\* (a * backslash prints the next character), and the missing symbols * are replaced by _* (a blank as wide as *) so that the decimal * points stay aligned in the right-aligned column local r0 = $EQUAIDS_TS_ROW local sw = ${EQTS_SW`t'} local sch "${EQTS_SCH`t'}" if `"`head'"' != "" { qui putexcel A`r0' = `"`head'"', bold local ++r0 } if $EQTS_HASG { forvalues j = 1/`C' { if `"${EQTS_G`j'}"' != "" { _eqts_xlcol `=`j' + 1' local c1 `r(col)' _eqts_xlcol `=`j' + ${EQTS_GN`j'}' local c2 `r(col)' if "`c1'" == "`c2'" qui putexcel `c1'`r0' = `"${EQTS_G`j'}"', bold hcenter else qui putexcel `c1'`r0':`c2'`r0' = `"${EQTS_G`j'}"', bold hcenter merge } } local ++r0 } local cmd `"A`r0' = `"`rowtitle'"'"' forvalues j = 1/`C' { _eqts_xlcol `=`j' + 1' local cmd `"`cmd' `r(col)'`r0' = `"${EQTS_CL`j'}"'"' } qui putexcel `cmd', bold right forvalues i = 1/`R' { local r = `r0' + `i' qui putexcel A`r' = `"${EQTS_RL`i'}"' * numeric cells grouped by star string (one format per call); * cells that are not numbers ("." or "(0.023)") go as text local kinds "" local txt "" forvalues j = 1/`C' { local v `"${EQTS_VAL`t'_`i'_`j'}"' local st `"${EQTS_STAR`t'_`i'_`j'}"' _eqts_xlcol `=`j' + 1' capture confirm number `v' if _rc | "`v'" == "." local txt `"`txt' `r(col)'`r' = `"`v'`st'"'"' else { local k = strlen("`st'") local cells`k' `"`cells`k'' `r(col)'`r' = `v'"' local kinds : list kinds | k local d = strpos("`v'", ".") if `d' local nd = strlen("`v'") - `d' else local nd 0 } } if `"`txt'"' != "" qui putexcel `txt', right foreach k of local kinds { if `nd' local nfmt = "0." + `nd' * "0" else local nfmt "0" if `k' local nfmt = "`nfmt'" + `k' * ("\" + "`sch'") if `sw' - `k' > 0 local nfmt = "`nfmt'" + (`sw' - `k') * "_`sch'" qui putexcel `cells`k'', right nformat("`nfmt'") local cells`k' "" } } local r = `r0' + `R' + 1 if `"${EQTS_LEG`t'}"' != "" { qui putexcel A`r' = `"${EQTS_LEG`t'}"', italic local ++r } global EQUAIDS_TS_ROW = `r' + 1 } else { * Word: value and stars in one right-aligned cell local tb tb${EQUAIDS_TS_N}_`t' if `"`head'"' != "" putdocx paragraph if `"`head'"' != "" putdocx text (`"`head'"'), bold local hg = $EQTS_HASG qui putdocx table `tb' = (`=`R'+1+`hg'', `=`C'+1'), border(all, nil) border(top) border(bottom) if `hg' { * merged from the right: a colspan renumbers the cells to its right forvalues j = `C'(-1)1 { if `"${EQTS_G`j'}"' != "" { local span = ${EQTS_GN`j'} if `span' > 1 qui putdocx table `tb'(1, `=`j'+1') = (`"${EQTS_G`j'}"'), bold halign(center) colspan(`span') else qui putdocx table `tb'(1, `=`j'+1') = (`"${EQTS_G`j'}"'), bold halign(center) } } } qui putdocx table `tb'(`=1+`hg'', 1) = (`"`rowtitle'"') forvalues j = 1/`C' { qui putdocx table `tb'(`=1+`hg'', `=`j'+1') = (`"${EQTS_CL`j'}"'), bold halign(center) } qui putdocx table `tb'(`=1+`hg'', .), border(bottom) forvalues i = 1/`R' { qui putdocx table `tb'(`=`i'+1+`hg'', 1) = (`"${EQTS_RL`i'}"') forvalues j = 1/`C' { qui putdocx table `tb'(`=`i'+1+`hg'', `=`j'+1') = (`"${EQTS_VAL`t'_`i'_`j'}${EQTS_STAR`t'_`i'_`j'}"'), halign(right) } } if `"${EQTS_LEG`t'}"' != "" { putdocx paragraph putdocx text (`"${EQTS_LEG`t'}"'), italic } } } macro drop EQTS_* end * Excel column letters for a 1-based index program define _eqts_xlcol, rclass args n local s "" while `n' > 0 { local m = mod(`n' - 1, 26) local s = char(65 + `m') + "`s'" local n = int((`n' - 1) / 26) } return local col "`s'" end version 14.2 mata: mata set matastrict on // --------------------------------------------------------------------------- // labels: from the option if given, else from the matrix stripes; truncated string colvector _eqts_labels(string scalar opt, string matrix stripe, real scalar n, real scalar maxlen) { string colvector L string rowvector tk real scalar i L = J(n, 1, "") if (opt != "") { tk = tokens(opt) for (i = 1; i <= min((n, cols(tk))); i++) L[i] = tk[i] for (i = cols(tk) + 1; i <= n; i++) L[i] = strofreal(i) } else { for (i = 1; i <= n; i++) { L[i] = stripe[i, 2] if (stripe[i, 1] != "" & stripe[i, 1] != "_") L[i] = stripe[i, 1] + ":" + stripe[i, 2] if (L[i] == "") L[i] = strofreal(i) } } for (i = 1; i <= n; i++) if (strlen(L[i]) > maxlen) L[i] = substr(L[i], 1, maxlen - 1) + "~" return(L) } // stars from a p-value: the symbol of the strongest level the p-value beats string scalar _eqts_star(real scalar p, real rowvector lev, string rowvector sym) { real scalar k, best string scalar s if (p >= .) return("") s = ""; best = 1 for (k = 1; k <= cols(lev); k++) { if (p < lev[k] & lev[k] <= best) { s = sym[k] best = lev[k] } } return(s) } string scalar _eqts_center(string scalar s, real scalar w) { real scalar l if (strlen(s) >= w) return(s) l = floor((w - strlen(s)) / 2) return(l * " " + s + (w - strlen(s) - l) * " ") } // the column groups: "title" k "title" k ... -> gid (C x 1) and gnames void _eqts_groups(string scalar spec, real scalar C, real colvector gid, string colvector gnames) { string rowvector tk real scalar i, k, n, j gid = J(C, 1, 0); gnames = J(0, 1, "") if (spec == "") return tk = tokens(spec) if (mod(cols(tk), 2) != 0) { errprintf("colgroups(): pairs of a title and a number of columns expected\n") exit(198) } j = 0 for (i = 1; i <= cols(tk); i = i + 2) { n = strtoreal(tk[i+1]) if (n >= . | n < 1) { errprintf("colgroups(): %s is not a number of columns\n", tk[i+1]) exit(198) } if (j + n > C) { errprintf("colgroups(): the groups cover more than the %g columns\n", C) exit(198) } gnames = gnames \ tk[i] for (k = 1; k <= n; k++) gid[j + k] = rows(gnames) j = j + n } } string scalar _eqts_just(string scalar s, real scalar w, real scalar left) { if (strlen(s) >= w) return(s) if (left) return(s + (w - strlen(s)) * " ") return((w - strlen(s)) * " " + s) } // a column label broken into at most nl pieces of about t characters, at "_" // or blanks when possible; pieces are returned bottom-aligned (nl x 1) string colvector _eqts_split(string scalar lab, real scalar t, real scalar nl) { string colvector out, pieces string scalar rest, piece real scalar k, cut, p, n if (strlen(lab) <= t | nl <= 1) { out = J(nl, 1, "") out[nl] = lab return(out) } t = max((t, ceil(strlen(lab) / nl))) // wide enough to hold it in nl lines // break at separators; if that takes more than nl pieces, widen the column // one character at a time until it fits n = nl + 1 while (n > nl) { pieces = J(0, 1, "") rest = lab while (strlen(rest) > t) { cut = t // the rightmost separator within the width, however short the // first piece ("p_" / "wheat" rather than "p_whea" / "t") for (p = t; p >= 2; p--) { if (substr(rest, p, 1) == "_" | substr(rest, p, 1) == " ") { cut = p break } } piece = substr(rest, 1, cut) pieces = pieces \ strtrim(piece) rest = strtrim(substr(rest, cut + 1, .)) } if (rest != "") pieces = pieces \ rest n = rows(pieces) if (n > nl) t = t + 1 } out = J(nl, 1, "") for (k = 1; k <= n; k++) out[nl - n + k] = pieces[k] return(out) } // one table on screen: values, stars (empty matrix = none), labels void _eqts_print(string matrix V, string matrix S, string colvector rl, string colvector cl, string scalar rowtitle, string scalar head, real scalar width, real scalar indent, real scalar leftalign, real scalar hlines, real colvector gid, string colvector gnames) { real scalar R, C, wr, j, i, c0, c1, tot, hasS, rest, k, g, bw, jj real colvector wv, ws, w string scalar pad, line, cell, gt string matrix H R = rows(V); C = cols(V) hasS = (rows(S) > 0) pad = indent * " " wr = max((strlen(rl) \ strlen(rowtitle))) wv = J(C, 1, 0); ws = J(C, 1, 0) H = J(hlines, C, "") for (j = 1; j <= C; j++) { wv[j] = max(strlen(V[., j])) if (hasS) ws[j] = max(strlen(S[., j])) // a label just a little wider than the column (up to 2 characters), or // a short one-word label (no _ or blank to break at, 12 characters at // most), widens the column rather than being broken over two lines if (strlen(cl[j]) > wv[j] + ws[j]) { if (strlen(cl[j]) <= wv[j] + ws[j] + 2) wv[j] = strlen(cl[j]) - ws[j] else if (strlen(cl[j]) <= 12 & !strpos(cl[j], "_") & !strpos(cl[j], " ")) wv[j] = strlen(cl[j]) - ws[j] } // a long label is written on up to hlines header lines H[., j] = _eqts_split(cl[j], wv[j] + ws[j], hlines) if (max(strlen(H[., j])) > wv[j] + ws[j]) wv[j] = max(strlen(H[., j])) - ws[j] } w = wv + ws if (head != "") printf("{txt}%s\n", head) c0 = 1 while (c0 <= C) { tot = indent + wr + 2; c1 = c0 while (c1 <= C) { // & does not short-circuit in Mata if (tot + w[c1] + 1 > width) break tot = tot + w[c1] + 1 c1 = c1 + 1 } c1 = max((c1 - 1, c0)) // Stata's own rules: {hline} with the T, + and bottom-T junctions rest = sum(w[c0..c1] :+ 1) printf("{txt}%s{hline %g}{c TT}{hline %g}\n", pad, wr + 1, rest) // super-titles over groups of columns, centred, underlined if (rows(gid) == C) { if (any(gid[c0..c1])) { line = pad + wr * " " + " {c |}" j = c0 while (j <= c1) { if (gid[j] == 0) { line = line + " " + w[j] * " " j = j + 1 continue } g = gid[j]; bw = -1; jj = j while (jj <= c1) { if (gid[jj] != g) break bw = bw + w[jj] + 1 jj = jj + 1 } gt = gnames[g] if (strlen(gt) > bw) gt = substr(gt, 1, max((bw - 1, 1))) + "~" line = line + " " + _eqts_center(gt, bw) j = jj } printf("{txt}%s\n", line) line = pad + wr * " " + " {c |}" j = c0 while (j <= c1) { if (gid[j] == 0) { line = line + " " + w[j] * " " j = j + 1 continue } g = gid[j]; bw = -1; jj = j while (jj <= c1) { if (gid[jj] != g) break bw = bw + w[jj] + 1 jj = jj + 1 } line = line + " {hline " + strofreal(bw) + "}" j = jj } printf("{txt}%s\n", line) } } for (k = 1; k <= hlines; k++) { if (k < hlines & max(strlen(H[k, c0..c1])) == 0) continue // empty header line if (k == hlines) line = pad + _eqts_just(rowtitle, wr, leftalign) + " {c |}" else line = pad + wr * " " + " {c |}" for (j = c0; j <= c1; j++) line = line + " " + _eqts_just(H[k, j], w[j], 0) printf("{txt}%s\n", line) } printf("{txt}%s{hline %g}{c +}{hline %g}\n", pad, wr + 1, rest) for (i = 1; i <= R; i++) { line = pad + _eqts_just(rl[i], wr, leftalign) + " {c |}" for (j = c0; j <= c1; j++) { cell = _eqts_just(V[i, j], wv[j], 0) if (hasS) cell = cell + _eqts_just(S[i, j], ws[j], 1) line = line + " {res}" + cell + "{txt}" } printf("%s\n", line) } printf("{txt}%s{hline %g}{c BT}{hline %g}\n", pad, wr + 1, rest) c0 = c1 + 1 } } // --------------------------------------------------------------------------- // export files: text formats are written by Mata, Excel/Word by Stata from locals string scalar _eqts_tex(string scalar s) { string scalar t t = subinstr(s, "\", "\textbackslash ", .) t = subinstr(t, "_", "\_", .) t = subinstr(t, "%", "\%", .) t = subinstr(t, "&", "\&", .) t = subinstr(t, "#", "\#", .) t = subinstr(t, "~", "$\sim$", .) // < and > are not text characters in LaTeX (they print as inverted ! and ?) t = subinstr(t, "<", "$<$", .) t = subinstr(t, ">", "$>$", .) return(t) } void _eqts_newfile(string scalar f, string scalar type, real scalar pre) { real scalar fh unlink(f) fh = fopen(f, "w") if (type == "tex") { fput(fh, "% written by _equaids_tabstars") if (pre) { // the minimum so that the file compiles on its own; without it // (nopreamble) the file is a fragment to \input into a document // that loads booktabs fput(fh, "\documentclass[11pt]{article}") fput(fh, "\usepackage[margin=2cm]{geometry}") fput(fh, "\usepackage{booktabs}") fput(fh, "\usepackage{caption}") fput(fh, "\begin{document}") fput(fh, "") } else fput(fh, "% fragment: \input it into a document that loads \usepackage{booktabs} and \usepackage{caption}") } fclose(fh) } void _eqts_endfile(string scalar f, string scalar type, real scalar pre) { real scalar fh if (type == "tex" & pre) { fh = fopen(f, "a") fput(fh, "\end{document}") fclose(fh) } } // one table appended to a text file void _eqts_write(string scalar type, string scalar f, string matrix V, string matrix S, string colvector rl, string colvector cl, string scalar rowtitle, string scalar head, string scalar leg, real colvector gid, string colvector gnames, real scalar ownnum, real scalar compact) { real scalar fh, R, C, i, j, hasS, g, jj, n, hasG, sw string scalar line, cell, q, mid, longest R = rows(V); C = cols(V); hasS = (rows(S) > 0) hasG = (rows(gid) == C) if (hasG) hasG = any(gid) // LaTeX: a star field of fixed width, the missing symbols set in \phantom, // keeps the decimal points aligned in a right-aligned column (no package // needed); compact drops the padding, as on screen sw = 0; longest = "" if (hasS & !compact) { for (i = 1; i <= R; i++) for (j = 1; j <= C; j++) { if (strlen(S[i, j]) > sw) { sw = strlen(S[i, j]) longest = S[i, j] } } } fh = fopen(f, "a") if (type == "tex") { fput(fh, "") fput(fh, "\begin{table}[htbp]") fput(fh, "\centering") // with tabnumber() the head already reads "Table N: ...": an unnumbered // caption (caption package) avoids "Table 1: Table N: ..."; without it // LaTeX numbers the table itself if (head != "" & ownnum) fput(fh, "\caption*{" + _eqts_tex(head) + "}") else if (head != "") fput(fh, "\caption{" + _eqts_tex(head) + "}") fput(fh, "\begin{tabular}{l" + C * "r" + "}") fput(fh, "\toprule") if (hasG) { line = ""; mid = ""; j = 1 while (j <= C) { if (gid[j] == 0) { line = line + " & " j = j + 1 continue } g = gid[j]; n = 0; jj = j while (jj <= C) { if (gid[jj] != g) break n = n + 1 jj = jj + 1 } line = line + " & \multicolumn{" + strofreal(n) + "}{c}{" + _eqts_tex(gnames[g]) + "}" mid = mid + "\cmidrule(lr){" + strofreal(j + 1) + "-" + strofreal(jj) + "}" j = jj } fput(fh, line + " \\") fput(fh, mid) } line = _eqts_tex(rowtitle) for (j = 1; j <= C; j++) line = line + " & " + _eqts_tex(cl[j]) fput(fh, line + " \\") fput(fh, "\midrule") for (i = 1; i <= R; i++) { line = _eqts_tex(rl[i]) for (j = 1; j <= C; j++) { cell = V[i, j] if (hasS) { if (sw > 0) { cell = cell + "$^{" + S[i, j] if (strlen(S[i, j]) < sw) cell = cell + "\phantom{" + substr(longest, 1, sw - strlen(S[i, j])) + "}" cell = cell + "}$" } else if (S[i, j] != "") cell = cell + "$^{" + S[i, j] + "}$" } line = line + " & " + cell } fput(fh, line + " \\") } fput(fh, "\bottomrule") if (leg != "") fput(fh, "\multicolumn{" + strofreal(C + 1) + "}{l}{\footnotesize " + _eqts_tex(leg) + "} \\") fput(fh, "\end{tabular}") fput(fh, "\end{table}") } else if (type == "csv") { q = char(34) if (head != "") fput(fh, q + head + q) if (hasG) { line = q + q for (j = 1; j <= C; j++) { line = line + "," if (gid[j] > 0) { if (j == 1) line = line + q + gnames[gid[j]] + q else if (gid[j-1] != gid[j]) line = line + q + gnames[gid[j]] + q } } fput(fh, line) } line = q + rowtitle + q for (j = 1; j <= C; j++) line = line + "," + q + cl[j] + q fput(fh, line) for (i = 1; i <= R; i++) { line = q + rl[i] + q for (j = 1; j <= C; j++) { cell = V[i, j] if (hasS) cell = cell + S[i, j] line = line + "," + cell } fput(fh, line) } if (leg != "") fput(fh, q + leg + q) fput(fh, "") } else { // markdown fput(fh, "") if (head != "") fput(fh, "**" + head + "**") fput(fh, "") if (hasG) { line = "| |" for (j = 1; j <= C; j++) { cell = "" if (gid[j] > 0) { if (j == 1) cell = gnames[gid[j]] else if (gid[j-1] != gid[j]) cell = gnames[gid[j]] } line = line + " " + cell + " |" } fput(fh, line) } line = "| " + rowtitle + " |" for (j = 1; j <= C; j++) line = line + " " + cl[j] + " |" fput(fh, line) line = "|:---|" for (j = 1; j <= C; j++) line = line + "---:|" fput(fh, line) for (i = 1; i <= R; i++) { line = "| " + rl[i] + " |" for (j = 1; j <= C; j++) { cell = V[i, j] if (hasS) cell = cell + S[i, j] line = line + " " + cell + " |" } fput(fh, line) } if (leg != "") { fput(fh, "") fput(fh, leg) } } fclose(fh) } // leave the cells of a table in globals for putexcel/putdocx void _eqts_globals(real scalar t, string matrix V, string matrix S, string colvector rl, string colvector cl, string scalar rowtitle, string scalar head, real colvector gid, string colvector gnames) { real scalar R, C, i, j, jj, n, hasS, sw string scalar cell, sch R = rows(V); C = cols(V); hasS = (rows(S) > 0) st_global("EQTS_HASG", "0") if (rows(gid) == C) { if (any(gid)) { st_global("EQTS_HASG", "1") for (j = 1; j <= C; j++) { cell = ""; n = 0 if (gid[j] > 0) { if (j == 1) cell = gnames[gid[j]] else if (gid[j-1] != gid[j]) cell = gnames[gid[j]] } if (cell != "") { // EQTS_GN = number of columns the group title spans for (jj = j; jj <= C; jj++) { if (gid[jj] != gid[j]) break n = n + 1 } } st_global("EQTS_G" + strofreal(j), cell) st_global("EQTS_GN" + strofreal(j), strofreal(n)) } } } // EQTS_HASS: the table carries stars; EQTS_SW/EQTS_SCH: width of the // widest star string and its first character (Excel pads the shorter // ones with blanks of that width so that the decimals stay aligned) st_global("EQTS_HASS" + strofreal(t), strofreal(hasS)) sw = 0; sch = "" if (hasS) { for (i = 1; i <= R; i++) for (j = 1; j <= C; j++) { if (strlen(S[i, j]) > sw) { sw = strlen(S[i, j]) sch = substr(S[i, j], 1, 1) } } } st_global("EQTS_SW" + strofreal(t), strofreal(sw)) st_global("EQTS_SCH" + strofreal(t), sch) st_global("EQTS_R" + strofreal(t), strofreal(R)) st_global("EQTS_C" + strofreal(t), strofreal(C)) st_global("EQTS_HEAD" + strofreal(t), head) st_global("EQTS_ROWTITLE", rowtitle) for (i = 1; i <= R; i++) st_global("EQTS_RL" + strofreal(i), rl[i]) for (j = 1; j <= C; j++) st_global("EQTS_CL" + strofreal(j), cl[j]) for (i = 1; i <= R; i++) for (j = 1; j <= C; j++) { cell = "" if (hasS) cell = S[i, j] st_global("EQTS_VAL" + strofreal(t) + "_" + strofreal(i) + "_" + strofreal(j), V[i, j]) st_global("EQTS_STAR" + strofreal(t) + "_" + strofreal(i) + "_" + strofreal(j), cell) } } // --------------------------------------------------------------------------- void _eqts_main(string scalar estname, string scalar sename, string scalar pname, string scalar rowlabs, string scalar collabs, string scalar rowtitle, string scalar title, string scalar tabnum, real scalar onetab, real scalar dec, string scalar levstr, string scalar symstr, real scalar maxlen, real scalar width, real scalar df, real scalar paren, string scalar setitle, real scalar legend, real scalar dostars, real scalar sestars, real scalar indent, real scalar leftalign, real scalar show, string scalar xtype, string scalar xfile, real scalar compact, real scalar hlines, string scalar groups) { real colvector gid string colvector gnames real matrix E, SE, P, T real scalar R, C, i, j, hasSE, hasP, n real rowvector lev string rowvector sym string matrix V, S, W, Sb string colvector rl, cl string scalar fmt, head, headb, leg E = st_matrix(estname) R = rows(E); C = cols(E) hasSE = (sename != ""); hasP = (pname != "") if (hasSE) SE = st_matrix(sename) if (hasP) P = st_matrix(pname) rl = _eqts_labels(rowlabs, st_matrixrowstripe(estname), R, maxlen) cl = _eqts_labels(collabs, st_matrixcolstripe(estname), C, maxlen) _eqts_groups(groups, C, gid, gnames) lev = strtoreal(tokens(levstr)) sym = tokens(symstr) fmt = "%" + strofreal(dec + 8) + "." + strofreal(dec) + "f" V = J(R, C, "") for (i = 1; i <= R; i++) for (j = 1; j <= C; j++) { if (E[i, j] < .) V[i, j] = strtrim(sprintf(fmt, E[i, j])) else V[i, j] = "." } S = J(0, 0, "") if (dostars & (hasSE | hasP)) { S = J(R, C, "") if (!hasP) { T = E :/ SE if (df > 0) P = 2 :* ttail(df, abs(T)) else P = 2 :* (1 :- normal(abs(T))) } for (i = 1; i <= R; i++) for (j = 1; j <= C; j++) S[i, j] = _eqts_star(P[i, j], lev, sym) } head = "" if (tabnum != "") head = "Table " + tabnum + ": " + title else if (title != "") head = title leg = "" if (legend & rows(S) > 0) { for (j = 1; j <= cols(lev); j++) { if (j > 1) leg = leg + ", " leg = leg + sym[j] + " p<" + strtrim(sprintf("%6.2f", lev[j])) } } // the second table (standard errors or p-values) W = J(0, 0, ""); headb = ""; Sb = J(0, 0, "") if (!onetab) { W = J(R, C, "") for (i = 1; i <= R; i++) for (j = 1; j <= C; j++) { if (hasSE) { if (SE[i, j] < .) W[i, j] = strtrim(sprintf(fmt, SE[i, j])) else W[i, j] = "." } else { if (P[i, j] < .) W[i, j] = strtrim(sprintf(fmt, P[i, j])) else W[i, j] = "." } if (paren & W[i, j] != ".") W[i, j] = "(" + W[i, j] + ")" } if (hasSE) headb = " (" + setitle + ")" else headb = " (p-values)" if (tabnum != "") headb = "Table " + tabnum + "-b: " + title + headb else if (title != "") headb = title + headb else headb = "" if (sestars) Sb = S } // screen if (show) { if (compact & rows(S) > 0) { // stars glued to the value, no fixed-width star field: narrower columns _eqts_print(V :+ S, J(0, 0, ""), rl, cl, rowtitle, head, width, indent, leftalign, hlines, gid, gnames) } else _eqts_print(V, S, rl, cl, rowtitle, head, width, indent, leftalign, hlines, gid, gnames) // the legend sits right under each table that carries stars if (leg != "" & rows(S) > 0) printf("{txt}%s%s\n", indent * " ", leg) if (!onetab) { printf("\n") if (compact & rows(Sb) > 0) _eqts_print(W :+ Sb, J(0, 0, ""), rl, cl, rowtitle, headb, width, indent, leftalign, hlines, gid, gnames) else _eqts_print(W, Sb, rl, cl, rowtitle, headb, width, indent, leftalign, hlines, gid, gnames) if (leg != "" & rows(Sb) > 0) printf("{txt}%s%s\n", indent * " ", leg) } } // export if (xtype != "") { if (xtype == "tex" | xtype == "csv" | xtype == "md") { if (onetab) _eqts_write(xtype, xfile, V, S, rl, cl, rowtitle, head, leg, gid, gnames, tabnum != "", compact) else { _eqts_write(xtype, xfile, V, S, rl, cl, rowtitle, head, leg, gid, gnames, tabnum != "", compact) if (rows(Sb) > 0) _eqts_write(xtype, xfile, W, Sb, rl, cl, rowtitle, headb, leg, gid, gnames, tabnum != "", compact) else _eqts_write(xtype, xfile, W, Sb, rl, cl, rowtitle, headb, "", gid, gnames, tabnum != "", compact) } } else { _eqts_globals(1, V, S, rl, cl, rowtitle, head, gid, gnames) if (!onetab) _eqts_globals(2, W, Sb, rl, cl, rowtitle, headb, gid, gnames) if (onetab) st_global("EQTS_NTAB", "1") else st_global("EQTS_NTAB", "2") st_global("EQTS_LEG1", leg) if (rows(Sb) > 0) st_global("EQTS_LEG2", leg) else st_global("EQTS_LEG2", "") } n = strtoreal(st_global("EQUAIDS_TS_N")) if (n >= .) n = 0 st_global("EQUAIDS_TS_N", strofreal(n + 1)) } } end