`etable`: new features in `fixest` 0.10.2
Laurent Bergé
2024-06-11
Source:vignettes/etable_new_features.Rmd
etable_new_features.Rmd
This document illustrates the new features of etable
,
the tool to export estimation tables in fixest
, in version
0.10.2.
Preamble
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
.
library(fixest)
# 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.")
setFixest_dict(dict)
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.
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:
- 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), - the generated image is inserted in the document in an
<img>
tag, - that
<img>
tag is inserted in a<div>
container of classetable
.
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:
etable(est,
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 coef.style
argument.
Frame
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 thePetal.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 rowPetal.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 ofPetal.Length
to the third column ofPetal.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"))
There are five main options:
-
se
: whether the standard-error should be included, -
sep0
tosep9
: the separation between the coefficients and the frame (default issep3
), -
thick1
tothick6
: the thickness of the line of the frame (default isthick6
), -
square
: whether the frame box should have square corners, -
color!alpha
: an R color followed by an (optional) alpha value. It is important to emphasize that it should be a valid R color (not Latex). The color is then translated into Latex and is guaranteed to work. The error handling is done in R to avoid problems appearing at the Latex compilation time which would be hard to debug.
Row colors
Instead of highlighting coefficients with a frame, row color
highlighting can be used. It is monitored with the same argument
highlight
and in fact you only have to add the option
rowcol
to make it work. Let’s replicate the previous
example with row color:
etable(est, arraystretch = 1.5,
highlight = .(rowcol = "Sepal@1",
"rowcol, cyan4!70" = "Petal.L@3-4",
"rowcol, darkgreen!40, se" = "Petal.W"))
As opposed to the previous example, we only have increased the
transparency of the colors with the color!alpha
syntax.
Contrary to the frame, there are no options for the row coloring
apart from the color itself and se
to include the
standard-errors.
Style
Using the same syntax as before to select the coefficients,
it is possible to completely customize the coefficient cells. The syntax
is coef.style = .("style" = "coefficients location")
with
style equal to Latex code containing the tag :coef:
which
will be replaced with the coefficient value. Alternatively, using the
tag :coef_se:
will apply the style both to the coefficient
and the standard-error.
Here is the replication of the previous example with another highlighting:
etable(est, coef.style = .(":coef:$\\bigstar$" = "Sepal@1",
"**:coef:**" = "Petal.L@3-4",
"\\color{BrickRed} :coef_se:" = "Petal.W"))
CSS and co.
As already said, the image of the table is embedded in a
<div>
container whose default class is
etable
. You can set the class of that
<div>
manually with the argument
div.class
. And do things like that:
/* This is CSS code */
.etable_box {
border: 2pt solid;
border-radius: 5px;
box-shadow: 12px 12px 2px 1px rgba(0, 0, 255, .2);
margin: 0 20% 2em 20%;
padding: 1em;
}
etable(est, div.class = "etable_box", page.width = "fit",
title = "CSS illustration.")
I’m not sure it should be done, but at least it’s possible!