{"id":1420,"date":"2023-04-20T12:55:24","date_gmt":"2023-04-20T16:55:24","guid":{"rendered":"https:\/\/blog.uvm.edu\/tbplante\/?p=1420"},"modified":"2023-06-01T10:15:17","modified_gmt":"2023-06-01T14:15:17","slug":"making-a-table-in-stata-for-regression-results-and-other-output-using-frames","status":"publish","type":"post","link":"https:\/\/blog.uvm.edu\/tbplante\/2023\/04\/20\/making-a-table-in-stata-for-regression-results-and-other-output-using-frames\/","title":{"rendered":"Making a table in Stata for regression results (and other output) using frames"},"content":{"rendered":"\n<p>Frames were introduced in Stata 16 and are handy for (a) storing\/manipulating multiple datasets simultaneously and (b) building datasets on the fly. I&#8217;ve had good luck making a table using frames. This strategy includes (1) making a new frame with as many columns as you need, specifying they are long strings (strL), and printing the top row, (2) running regressions or whatnot, (3) saving components of the regression as local macros that print as text, (4) making &#8220;display&#8221; macros for each column, (5) saving those &#8220;display&#8221; local macros to the new frame, repeating steps 2-5 as many times as needed, and (6) exporting the new frame as an Excel file. <\/p>\n\n\n\n<p>Stata has recently introduced &#8220;tables&#8221; and &#8220;collect&#8221; features that allow you to do similar things. I find those features overly confusing, so I&#8217;ve developed this approach. I hope you find it useful! Here&#8217;s what we are trying to make:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blog.uvm.edu\/tbplante\/files\/2023\/04\/image.png\" alt=\"\" class=\"wp-image-1421\" width=\"510\" height=\"203\" srcset=\"https:\/\/blog.uvm.edu\/tbplante\/files\/2023\/04\/image.png 642w, https:\/\/blog.uvm.edu\/tbplante\/files\/2023\/04\/image-300x119.png 300w\" sizes=\"auto, (max-width: 510px) 100vw, 510px\" \/><\/figure>\n\n\n\n<p>Note: these details depend on use of concepts discussed on <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.uvm.edu\/tbplante\/2019\/10\/30\/working-with-stata-regression-results-matrix-matrices-macros-oh-my\/\" target=\"_blank\">this other post<\/a> (&#8220;return list&#8221; &#8220;ereturn list&#8221; &#8220;matrix list r(table)&#8221;, local macros, etc.). Make sure to familiarize yourself with that post. <\/p>\n\n\n\n<p>Problems you might run into here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>This uses local macros, which disappear when a do file stops running. You need to run the entire do file from top to bottom every time, not line by line. <\/li>\n\n\n\n<li>Stata&#8217;s -frames post- functionality is finicky. It expects <em>exactly<\/em> as many posted variables as there are variables in the new frame, and that each posted format matches the predefined format on the frame&#8217;s variable. <\/li>\n\n\n\n<li>Three forward slashes (&#8220;\/\/\/&#8221;) extends things across lines. Two forward slashes (&#8220;\/\/&#8221;) allows commenting but does not continue to the next line. Check your double and triple forward slashes.<\/li>\n\n\n\n<li>Stata can&#8217;t write to an open excel file. Close the excel file before re-running. <\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Code<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>* Reset frames and reload data\nframes reset\nsysuse auto, clear\n*\n* STEP 1 - Make a new frame with long strings\n*\n* Make new frame called \"table\" and \n* define all columns\/variables as being strings.\n* Name all variables as col#, starting with col1.\n* You can extend this list as long as you'd like.\nframe create table \/\/\/ &lt;--TRIPLE SLASH\nstrL col1 \/\/\/ name \nstrL col2  \/\/\/ n\nstrL col3 \/\/\/ beta\nstrL col4 \/\/\/ 95% CI\nstrL col5 \/\/ &lt;-- DOUBLE SLASH, P-value \n* If you want to add more col#s, make sure you change \n* the last double slashes to triple slashes, all new \n* col#s should have triple slashes except the last,\n* which should have double (or no) slashes\n*\n* Prove to yourself that you have made a new frame\n* called &quot;table&quot; in addition to the &quot;default&quot; one\n* with the auto dataset. \nframes dir\n* You could switch to the new table frame with \n* the &quot;cwf table&quot; command, if interested. To switch\n* back, type &quot;cwf default&quot;. \n*\n* STEP 1B - print out your first row\n* \nframe post table \/\/\/ &lt;--TRIPLE SLASH\n(&quot;Variable name&quot;) \/\/\/ col1\n(&quot;N&quot;) \/\/\/ col2\n(&quot;Beta&quot;) \/\/\/ col3\n(&quot;95% CI&quot;) \/\/\/ col4\n(&quot;P-value&quot;) \/\/ &lt;--DOUBLE SLASH col5 \n*\n* You can repeat this step as many times as you want \n* to add as many rows of custom text as needed. \n* Note that if you want a blank column, you need\n* to still include the quotations within the parentheses.\n* eg, if you didn&#039;t want the first column to have \n* &quot;variable name&quot;, you&#039;d put this instead:\n* strL (&quot;&quot;) \/\/\/ col1\n*\n* If you wanted to flip over to the table frame and \n* see what&#039;s there, you&#039;d type:\n*cwf table \n*bro\n*\n* ...and to flip back to the default frame, type:\n*cwf default\n*\n* STEP 2 - run your regression and look where the \n* coefficients of interest are stored \n* and\n* STEP 3 - saving components of regression as local\n* macros\n*\nregress price weight\n*\nereturn list\n* The N is here under e(N), save that as a local macro\nlocal n1_1 = e(N)\n*\nmatrix list r(table)\n* The betas is at &#091;1,1], the 95% thresholds\n* are at &#091;5,1] and &#091;6,1], the se is\n* at &#091;2,1], and the p-value is at &#091;4,1].\n* We weren&#039;t planning on using se in this table,\n* but we&#039;ll grab it anyway in case we change our minds\nlocal beta1_1 = r(table)&#091;1,1]\nlocal ll1_1 = r(table)&#091;5,1]\nlocal ul1_1 = r(table)&#091;6,1]\nlocal se1_1 = r(table)&#091;2,1]\nlocal p1_1 = r(table)&#091;4,1]\n*\n* STEP 4 - Making &quot;display&quot; macros\n* \n* We are going to use local macros to grab things\n* by column with a &quot;display&quot; commmand. Note that \n* column 1 is name, which we are going to call &quot;all&quot; here. \n* You could easily grab the variable name here with \n* the &quot;local lab:&quot; command detailed here:\n* https:\/\/www.stata.com\/statalist\/archive\/2002-11\/msg00087.html\n* or: \n*local label_name: variable label foreign \n* ...then call `label_name&#039; instead of &quot;all&quot;\n* \n* Now this is very important, we are not going to just\n* capture these variables as local macros, we are going\n* to capture a DISPLAY command followed by how we\n* want the text to be rendered in the table. \n* Immediately after defining the local macro, we are going\n* to call the macro so we can see what it will render \n* as in the table. This is what it will look like:\n*local col1 di &quot;All&quot;\n*`col1&#039;\n*\n* IF YOU HAVE A BLANK CELL IN THIS ROW OF YOUR TABLE, \n* USE TWO EMPTY QUOTATION MARKS AFTER THE &quot;di&quot; COMMAND, eg: \n* local col1 di &quot;&quot;\n* \n* now we will grab all pieces of the first row, \n* column-by-column, at the same time:\nlocal col1 di &quot;All&quot; \/\/ name\n`col1&#039;\nlocal col2 di `n1_1&#039; \/\/ n\n`col2&#039;\nlocal col3 di %4.2f `beta1_1&#039; \/\/ betas\n`col3&#039;\nlocal col4 di %4.2f `ll1_1&#039; &quot; to &quot; %4.2f `ul1_1&#039; \/\/ 95% ci\n`col4&#039;\nlocal col5 di %4.3f `p1_1&#039; \/\/ p-value\n`col5&#039; \n*\n* note for the P-value, you can get much fancier in \n* formatting, see my script on how to do that here: \n* https:\/\/blog.uvm.edu\/tbplante\/2022\/10\/26\/formatting-p-values-for-stata-output\/\n*\n* STEP 5 - Posting the display macros to the table frame\n* \n* Now post all columns row-by-row to the table frame\nframe post table \/\/\/\n(&quot;`: `col1&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col2&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col3&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col4&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col5&#039;&#039;&quot;) \/\/ &lt;-- DOUBLE SLASH\n*\n* Bonus! Insert a text row\n*\nframe post table \/\/\/ &lt;--TRIPLE SLASH\n(&quot;By domestic vs. foreign status&quot;) \/\/\/ col1\n(&quot;&quot;) \/\/\/ col2\n(&quot;&quot;) \/\/\/ col3\n(&quot;&quot;) \/\/\/ col4\n(&quot;&quot;) \/\/ &lt;--DOUBLE SLASH col5 \n* Now repeat by foreign status\n* domestic\nregress price weight if foreign ==0 \nereturn list\nlocal n1_1 = e(N)\n*\nmatrix list r(table)\nlocal beta1_1 = r(table)&#091;1,1]\nlocal ll1_1 = r(table)&#091;5,1]\nlocal ul1_1 = r(table)&#091;6,1]\nlocal se1_1 = r(table)&#091;2,1]\nlocal p1_1 = r(table)&#091;4,1]\n*\n* note: you could automate col1 with the following command,\n* which would grabe the label from the foreign==0 value of\n* foreign:\n*local label_name: label foreign 0\n* ... then call `label_name&#039; in the &quot;local col1 di&quot;. \nlocal col1 di &quot;  Domestic&quot; \/\/ name, with 2 space indent\n`col1&#039;\nlocal col2 di `n1_1&#039; \/\/ n\n`col2&#039;\nlocal col3 di %4.2f `beta1_1&#039; \/\/ betas\n`col3&#039;\nlocal col4 di %4.2f `ll1_1&#039; &quot; to &quot; %4.2f `ul1_1&#039; \/\/ 95% ci\n`col4&#039;\nlocal col5 di %4.3f `p1_1&#039; \/\/ p-value\n`col5&#039; \n*\nframe post table \/\/\/\n(&quot;`: `col1&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col2&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col3&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col4&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col5&#039;&#039;&quot;) \/\/ &lt;-- DOUBLE SLASH\n*\n* foreign\nregress price weight if foreign ==1\nereturn list\nlocal n1_1 = e(N)\n*\nmatrix list r(table)\nlocal beta1_1 = r(table)&#091;1,1]\nlocal ll1_1 = r(table)&#091;5,1]\nlocal ul1_1 = r(table)&#091;6,1]\nlocal se1_1 = r(table)&#091;2,1]\nlocal p1_1 = r(table)&#091;4,1]\n*\nlocal col1 di &quot;  Foreign&quot; \/\/ name, with 2 space indent\n`col1&#039;\nlocal col2 di `n1_1&#039; \/\/ n\n`col2&#039;\nlocal col3 di %4.2f `beta1_1&#039; \/\/ betas\n`col3&#039;\nlocal col4 di %4.2f `ll1_1&#039; &quot; to &quot; %4.2f `ul1_1&#039; \/\/ 95% ci\n`col4&#039;\nlocal col5 di %4.3f `p1_1&#039; \/\/ p-value\n`col5&#039; \n*\nframe post table \/\/\/\n(&quot;`: `col1&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col2&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col3&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col4&#039;&#039;&quot;) \/\/\/\n(&quot;`: `col5&#039;&#039;&quot;) \/\/ &lt;-- DOUBLE SLASH\n*\n* STEP 6 - export the table frame as an excel file\n* \n* This is the present working directory, where this excel\n* file will be saved if you don&#039;t specify a directory:\npwd\n*\n* Switch to the table frame and take a look at it:\ncwf table\nbro \n* Now export it to excel\nexport excel using &quot;table.xlsx&quot;, replace\n*\n* That&#039;s it!<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Frames were introduced in Stata 16 and are handy for (a) storing\/manipulating multiple datasets simultaneously and (b) building datasets on the fly. I&#8217;ve had good luck making a table using frames. This strategy includes (1) making a new frame with as many columns as you need, specifying they are long strings (strL), and printing the &hellip; <a href=\"https:\/\/blog.uvm.edu\/tbplante\/2023\/04\/20\/making-a-table-in-stata-for-regression-results-and-other-output-using-frames\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Making a table in Stata for regression results (and other output) using frames<\/span><\/a><\/p>\n","protected":false},"author":4473,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[502558,477491],"tags":[222041,696684,703448,703447,502556,703446],"class_list":["post-1420","post","type-post","status-publish","format-standard","hentry","category-epidemiology-and-biostatistics","category-stata-code","tag-epidemiology","tag-frames","tag-output-table","tag-regression-table","tag-stata","tag-table"],"_links":{"self":[{"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/posts\/1420","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/users\/4473"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/comments?post=1420"}],"version-history":[{"count":4,"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/posts\/1420\/revisions"}],"predecessor-version":[{"id":1443,"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/posts\/1420\/revisions\/1443"}],"wp:attachment":[{"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/media?parent=1420"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/categories?post=1420"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.uvm.edu\/tbplante\/wp-json\/wp\/v2\/tags?post=1420"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}