--- title: "LaTeX math in base R graphics" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{LaTeX math in base R graphics} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", dpi = 300, dev = "png" ) ``` Turn on `latex_options(device_math = TRUE)` and any plot label written with `$...$` is typeset as LaTeX. The rest of your plotting code stays the same. ## A first plot ```{r first, fig.height=3.8, fig.width=6, out.width="75%"} library(gridmicrotex) library(grid) latex_options(device_math = TRUE) plot(1:10, (1:10)^2, main = r"(Slope $\hat{\beta}_1 = \sum_{i=1}^{n} x_i^2$)", xlab = "Cost $5-$10 per unit", ylab = r"($\frac{y}{2}$)") text(3, 80, r"($\int_0^\infty e^{-x^2}\,dx$)", col = "steelblue") ``` The title, y-axis label and annotation are typeset. The x-axis label has no math in it, so it is drawn as written. In R Markdown, set the option in the setup chunk. If you turn it off, do so in a later chunk than the plot, or the plot shows the literal `$...$`. ## More than plotmath Tables, display-style operators and other LaTeX work in any label. The coefficient table below is drawn by an ordinary `text()` call: ```{r beyond, fig.height=4.6, fig.width=6.5} set.seed(42) x <- seq(0, 3, length.out = 60) y <- 2 + 1.4 * x^2 + rnorm(60, sd = 1.1) fit <- lm(y ~ poly(x, 2, raw = TRUE)) cf <- signif(coef(summary(fit)), 3) title_eq <- r"($$\hat{y} = \beta_0 + \beta_1 x + \beta_2 x^2$$)" # Make room for the tall title h <- latex_dims(title_eq, gp = gpar(fontsize = par("ps")))$height top <- ceiling(convertHeight(h, "bigpts", TRUE) / (par("cin")[2] * 72 * par("mex"))) + 2 par(mar = c(4.5, 5.5, top, 2)) plot(x, y, pch = 19, col = "grey45", cex = 0.7, ylim = c(0, 22), main = title_eq, xlab = r"($x$)", ylab = r"($y = f(x) + \varepsilon$)") lines(x, predict(fit), col = "#B22222", lwd = 2) # Build the table as a single line of text tab <- sprintf(paste0( r"(\begin{array}{|l|r|r|}\hline)", r"(\rowcolor{#E8F0FE}\text{Term} & \hat{\beta} & \text{SE}\\\hline)", r"(\beta_0 & %s & %s\\)", r"(\beta_1 & %s & %s\\)", r"(\beta_2 & %s & %s\\\hline\end{array})"), cf[1, 1], cf[1, 2], cf[2, 1], cf[2, 2], cf[3, 1], cf[3, 2]) text(0.05, 18, tab, adj = c(0, 0.5), cex = 0.75) text(2.95, 1.2, r"($\varepsilon \sim \mathcal{N}(0, \sigma^2)$)", adj = c(1, 0), cex = 0.95, col = "#B22222") ``` Two tips from this example: - Keep a formula on one line. A label containing a newline is not recognised as math. - Anchor a tall label by its centre (`adj = c(0, 0.5)`) and leave room around it; see [Tall formulas](#tall-formulas). ## Other packages The option works for any plot drawn on an R graphics device: base plot methods such as `plot(lm)`, `hist()` and `legend()`, lattice, grid, and ggplot2 (axis titles, `geom_text()`, facet strips, legend keys). It does not reach plotly or rgl's `text3d()` labels. For ggplot2, `geom_latex()` and `element_latex()` are often the better choice; see `vignette("ggplot2-integration")`. ## When is a label treated as math? A label is typeset only when every `$` in it is closed and each `$...$` pair holds something that looks like math. Everyday dollar signs are left alone: | Label | Rendered as | |:--|:--| | `"Revenue ($)"` | text | | `"Price is $5 today"` | text | | `"Cost $5-$10"` | text | | `"Budget $1,000 to $5,000 with $x$ shown"` | text | | `"Histogram of df$a_b - df$c_d"` | text | | `"$x$"` | math | | `"$x^2$"` | math | | `"$y = 2x + 1$"` | math | | `"Slope $\\hat{\\beta}_1$"` | math | | `"\\(\\alpha\\)"` | math | `$$...$$`, `\(...\)` and `\[...\]` also work. Write `\$` for a literal dollar sign in a label that also has math. ## Tall formulas {#tall-formulas} Widths are measured correctly, but heights are not: R sizes margins, titles and `legend()` boxes for one line of text, so a tall formula can overflow them. Measure it with `latex_dims()` and widen the margin: ```{r margin} h <- latex_dims(r"($\frac{a}{b}$)", gp = gpar(fontsize = par("ps")))$height bp <- convertHeight(h, "bigpts", TRUE) # Lines of margin needed bp / (par("cin")[2] * 72 * par("mex")) ``` Round that up and add it to the matching side of `par(mar = )`. ## Limitations - The option applies to every plot in the session, including plots from other packages. Labels without math are not changed. - Math is drawn as outlines, so it cannot be selected in a PDF or SVG. - Bold comes from `\textbf{}`, not from the plot's font face. - `\includegraphics` is skipped, and rounded box corners are square. - A label that cannot be read is drawn as plain text, without a warning. - It does not work together with `showtext::showtext_auto()`. - `expression()` labels are not affected. - corrplot reads a label starting with `$` itself; start it with something else. ```{r off, include = FALSE} latex_options(device_math = FALSE) ```