Skip to contents

This document illustrates the new features of etable, the tool to export estimation tables in fixest, in version 0.10.2.


Version 0.10.2 comes with majors changes in the Latex engine of etable. The most notable one is the ability to transform Latex tables into PNG files and display them either in RStudio’s viewer, either directly in HTML markdown documents (like all the tables in this document).

However, it requires that the following software are installed and work properly on the user-side:

  • pdflatex (shipped in most Latex distributions) or the R package tinytex (very likely already installed),
  • imagemagick (to convert images) and ghostscipt (to convert PDF files), or the R package pdftools.

Now we’re good to start. Let’s first define a global dictionary: it will rename the variables and will also include pre-defined notes that can be fetched from etable.

# Let's define a dictionary and set it globally.
# We also define notes, not just variable names.
# (The function 'dsb()' is like 'glue()' and is not important here.)
dict = c("(Intercept)" = "Constant", 
         Petal.Length = "Petal length", Petal.Width = "Petal width",
         Sepal.Length = "Sepal length", Sepal.Width = "Sepal width",
         note1 = dsb("*Notes*: This is a note that illustrates how to access notes ",
                     "from the dictionary."),
         source = "*Sources*: Somewhere from the net.")

Then let’s define a few global options for the table. The philosophy is to set all the style components common across tables only once and for all thanks to setFixest_etable. The goal is, to increase maintainability, to use only table-specific arguments in later calls to etable. Hence, if the table style has to be changed, only one modification is needed (in setFixest_etable), and etable calls stay the same.

# The style of the table
my_style = style.tex("aer", model.format = "(i)")

# markdown = TRUE is only useful in Rmarkdown documents
setFixest_etable(style.tex = my_style, markdown = TRUE, page.width = "a4")

The arguments markdown and page.width will be explained in the next section. And now the estimation we’ll use throughout this document.

nm = names(iris)
est = feols(.[nm[1]] ~ .[nm[2:4]], iris, fsplit = ~Species)

Displaying tables as PNG

One major improvement is the ability to generate PNG snapshots of Latex tables (I wish to thank Or Avishay-Rizi for the suggestion!). It works very intuitively: first a standalone Latex document is compiled to generate a PDF containing only the table, then imagemagick is used to convert the PDF into a PNG file. Note that image generation is not instantaneous because of the Latex compilation (but output can be cached, as explained later). These images are then accessible with the arguments view and markdown.

Argument view

In any call of etable (i.e. tex = TRUE is not required), on can use view = TRUE to display the table snapshot in Rstudio’s viewer. The table will fit the viewer’s window, being as large as possible without overflowing horizontally nor vertically.

This feature is, ironically, very hard to display in this document since there’s no viewer: so to see it at work, you should try it! Using the current example, just run etable(est, view = TRUE).

Note that if the same tables are to be displayed several times, it can be useful to cache the PNG outputs to avoid the compilation run-time. To enable caching, use setFixest_etable(view.cache = TRUE). The images are then saved in a temporary directory that the algorithm tries to find even across sessions – hence it enables long-term caching.

Argument markdown

This is a peculiar option that works only within Rmarkdown documents and is ignored otherwise. If markdown = TRUE the output becomes always a Latex table, even when tex = FALSE (default). Second, and most importantly, the output within the Rmarkdown document becomes contingent to the type of file generated. If the document is to be a PDF, then the code of a regular Latex table is returned. If the document is not a PDF (especially if it is an HTML file), then:

  1. the PNG of the table is generated and saved in images/etable/, so that successive calls don’t need to re-generate the PNGs (i.e. this is caching),
  2. the generated image is inserted in the document in an <img> tag,
  3. that <img> tag is inserted in a <div> container of class etable.

This ensures that the tables are the same whether the output is a PDF or an HTML document. Since the table is embedded in a <div> container, you can even add some custom CSS to further customize how it looks.

All the images of the tables in this document are generated using markdown = TRUE, set globally in setFixest_etable.

Argument page.width

As mentioned, the tables are embedded in a standalone Latex document. The main consequence of this is that the table fits the PDF file completely, without any margin on any side. This means that whatever the width of the table (be it two or twenty columns), it will always take 100% of the width of the PDF.

While this property is fine when displaying on the viewer (since we usually want to see the full table), it makes the tables look odd and inconsistent in an HTML document. Indeed, since tables of two columns will take the same width as table of twenty columns, the font will look extra large for the former and extra small for the latter. That’s not something we usually want in a document.

Hence, here comes the page.width argument. This argument sets the width and side margins of the page in which will be inserted the table. The goal of this argument is to mimic the placement of the table in a real PDF document (i.e. in your article). This ensures that the PNG will look as in the PDF. For instance, page.width = "a4" will set an A4 page width and 2cm side margins on both sides. This argument can be customized at will: e.g. 8.5in, 1.1in will lead to a page width of 8.5 inches with 1.1 inch side margins (the only constraint is that the unit of the width should be the same as the unit of the margin).

When page.width is set up, all tables in an HTML document will be consistent: the font will be the same across tables. On top of that, it also enables a couple of specific features. First, if the table is too narrow, you can use a tabularx table (with the argument tabluar) and the table will be as large as the “text” width (not the HTML text but the hypothetical PDF text). Second, if the table overflows, you can adjust it with the argument adjustbox to make it fit the text width.

New Latex-escaping mechanism: Markdown markup and makecell support

Now almost any user-added text supports markdown markup for italics/bold/bold-italics with *, ** and ***. Further, there is native support for makecell: simply using "\n" enables it. Here’s an example:

etable(est, headers = .("\n\n Short header" = 2, "*Very* \n **long** \n ***header***" = 2))

Support for threeparttable notes

There is native support for the Latex package threeparttable. This option can be set via the function style.tex(). Further (and this is also new), you can access notes via the global dictionary to avoid repetitions:

       style.tex = style.tex(tpt = TRUE, notes.tpt.intro = "\\footnotesize"),
       notes = c("note1", "source"))

The table notes now are adjusted to the table width thanks to threeparttable and the notes are accessed directly with their keys. The argument notes.tpt.intro inserts Latex code right before the first \\item of threeparttable: in this case, it sets the font to footnotesize.

In general, it is advised to set the value of tpt and the notes.tpt.intro globally. Then if one wants to change the value of notes.tpt.intro, instead of typing style.tex = style.tex(notes.tpt.intro = "stuff"), the first element of notes will replace notes.tpt.intro if it starts with an "@":

# Setting up tpt globally
my_style = style.tex(tpt = TRUE, 
                     notes.tpt.intro = "\\footnotesize")
setFixest_etable(style.tex = my_style)

# Below is identical to: 
#  etable(est, 
#         style.tex = style.tex(notes.tpt.intro = "\\Large"),
#         notes = "These notes are large.")
etable(est, notes = c("@\\Large", "These notes are large."))

Support for adjustbox

Now there is native support for the Latex package adjustbox. This nests the table into an adjustbox environment which will resize the image of the table at the appropriate dimensions, as provided by the user. This is especially useful for tables that overflow but can also be used to force a small table to fit a particular size.

By default if adjustbox = TRUE, the adjustbox environment is created with the options width = 1\\textwidth, center. This argument can be equal to a number, in which case it will be treated as the desired text width. Otherwise it accepts any character string.

Let’s have an example with a large table that would otherwise overflow:

# mvsw == 'multiverse' stepwise (thanks to Resul Umit's suggestion)
est_many = feols(.[nm[1]] ~ mvsw(.[, nm[2:4]]), iris)
etable(est_many, adjustbox = 1.1)

Highlighting coefficients

Highlighting coefficients is often very useful, especially in presentations. There are now three main ways, natively implemented, to highlight the coefficients:

  • using a frame,
  • coloring the cells,
  • giving a custom style to the coefficients.

The first two can be accessed via the highlight argument. The last is implemented in the argument.


By default, the highlight argument superimposes a frame around the coefficients of interest. This is done thanks to some tickz magic that I barely understand found in tex stack exchange.

Anyway, back to the argument. The syntax is "options" = "coefficients location". The coefficient location can be expressed in several ways:

  • the coefficient name: the full row is selected. Ex: Petal.L will select the Petal.Length row.
  • the coefficient name followed by @ and column ranges: in the coefficient row, only selects the given columns. Ex: Petal.L@1,3-4 will select, in the row Petal.Length, the columns 1, 3 and 4.
  • a vector of two coefficient cells: selects the full range from top-left to bottom right. Ex: c("Petal.L@2", "Petal.W@3") will select a range from the second column of Petal.Length to the third column of Petal.Width.

Let’s have an example illustrating the three ways to select the coefficients and some options. We also increase the row heights with arraystretch to facilitate the highlighting:

etable(est, arraystretch = 1.5,
       highlight = .("Sepal@1", 
                     "cyan4, square" = "Petal.L@3-4",
                     "thick5, sep8, darkgreen!90, se" = "Petal.W"))