Use R to Write LaTeX Code in R Markdown Documents

Technical reports written in R Markdown usually include mathematical symbols. R Markdown addresses that by supporting the inclussion of raw LaTeX code. Depending on the desired output format, the code gets rendered by some LaTeX engine. This feature is very convenient.

The issue here is that writing LaTeX code can be cumbersome and the result is usually hard to read. Although the use of newcommand can alleviate that, in practice people use it infrequently. The idea behind this package is that if you’re writing an R Markdown report, you’re already using R, so you might as well use it to write your LaTeX code as well.

Example

This is best explained through an example. Consider the following equation, encountered in Riemannian geometry:

\[ \frac{d}{dt} \left \langle V,W \right \rangle = \left \langle \frac{DV}{dt}, W \right \rangle + \left \langle V, \frac{DW}{dt} \right \rangle \]

The straightforward way to code that is:

\frac{d}{dt} \left \langle V,W \right \rangle = \left \langle \frac{DV}{dt}, W \right \rangle + \left \langle V, \frac{DW}{dt} \right \rangle

If you were to use latexSymb, you would proceed as follows instead. First you would create objects of class latex_symb to represent the vector fields:

library(latexSymb)
## Loading required package: purrr
## 
## Attaching package: 'latexSymb'
## The following objects are masked from 'package:base':
## 
##     *, +, -, /, ^
vf1 <- lsymb("V")
vf2 <- lsymb("W")

Then you would write R functions to represent the mathematical concepts being used: the inner product, the covariant derivative and the ordinary derivative:

inner <- function(x,y) ang(lsymb(x, ",", y))
cov.der <- function(x) lsymb("D", x)/"dt"
ddt <- function(x) lsymb("d", x)/"dt"

Then you would put everything in an equation environment:

lenv("equation", 
    lsymb(
          ddt(inner(vf1, vf2)), 
          "=", 
          inner(cov.der(vf1), vf2) + inner(vf1, cov.der(vf2))
    )
) |> cat()

\[\begin{equation}\frac{ d \langle V , W \rangle }{ dt } = \langle \frac{ D V }{ dt } , W \rangle + \langle V , \frac{ D W }{ dt } \rangle\end{equation}\]

Notice that you need to cat instead of print to avoid double backslashes.

Unpacking

Let us unpack all of this.

Notice first that in the definition of cov.der an important feature is being used. Namely, the / function is overwritten by latexSymb so that if either of its arguments are objects of class latex_symb, the output is another object of that class that uses frac as you would expect. This is also done with +, -, * and ^. Additionally, under adds support for the use of subscripts.

Also, in the numerator of cov.der, lsymb("D", x) concatenates the string "D" and the object x, and wraps the result in a new latex_symb object.

Further, there’s the use of the latexSymb function ang, which provides the dynamic enclosing for the inner product, thus avoiding the need to write left and right. The functions br, sqbr and pths do the same for braces, square brackets and parentheses respectively.

Finally, there is the function lenv that spares the user from writing begin and end. The second argument is a list or a vector, whose components represent different lines. So, for instance, we could have written also

lenv("align*", 
     c(
       lsymb(ddt(inner(vf1, vf2)), "&=\\\\"),
       lsymb("&=", inner(cov.der(vf1), vf2) + inner(vf1, cov.der(vf2)))
     )
) |> cat()

\[\begin{align*}\frac{ d \langle V , W \rangle }{ dt } &=\\&= \langle \frac{ D V }{ dt } , W \rangle + \langle V , \frac{ D W }{ dt } \rangle\end{align*}\]

latex_symb also exposes il, which surrounds expressions in dollar signs, for inline math environments.

Discussion

On the whole, latexSymb may not significantly reduce the amount of code users have to write compared to raw LaTeX. But I do think it makes it more readable and hence less prone to errors. Users can mostly write what they mean, not what they want to see.

Additionally, latexSymb is not limited to RMarkdown. Actually, it probably makes more sense in .Rtex documents. In any case, any format that knit can handle will do.

Finally, for very complicated equations, R’s piping should make things even more readable.