Setup
Advantages of ob_label
over ob_latex
The ob_label
function uses ggtext::geom_richtext
to create labels. It’s primary advantage is that it is simple and renders quickly. Wherever possible, it is the recommended way to create labels. It understands basic markdown formatting (e.g., italics, bolding, subscripts, and superscripts) as well as some HTML tags (e.g., span
and img
).
Advantages of ob_latex
over ob_label
If something more elaborate is needed than italics, bolding, subscripts, and superscripts, we can use LaTeX instead. The ob_latex
function can place an image of a LaTeX equation in a ggplot diagram.
For example, suppose I want to label a latent variable’s variance with the symbol . This symbol would be difficult to render in pure HTML, so we can render it in LaTeX instead.
ggdiagram(font_family = "Roboto Condensed") +
{l <- ob_circle(label = ob_label("*e*", size = 48))} +
{lv <- ob_variance(l)} +
ob_latex(tex = "\\sigma_e^2",
center = lv@midpoint(),
width = .4)
If we want the symbol to be in the same font as the rest of the figure, we can trick LaTeX into giving us any font we have installed on our system. I often use Roboto Condensed:
ggdiagram(font_family = "Roboto Condensed") +
{l <- ob_circle(label = ob_label("*e*", size = 48))} +
{lv <- ob_variance(l)} +
ob_latex(tex = r"(\text{\emph{σ}}_{\text{\emph{e}}}^{\text{2}})",
center = lv@midpoint(),
width = .4,
family = "Roboto Condensed")
If you need an equation in a plot that requires something other than a 1:1 aspect ratio, you can set the aspect ratio of the equation to be the same as the aspect ratio as the plot.
mu <- 50
sigma <- 10
ratio <- (4 * sigma) / dnorm(mu, mu, sigma)
ggplot() +
coord_fixed(ratio = ratio) +
theme_classic(base_family = "Roboto Condensed") +
theme(axis.line = element_blank(), axis.title.x = element_markdown()) +
stat_function(
fun = \(x) dnorm(x, mean = mu, sd = sigma),
geom = "area",
n = 1000,
fill = "dodgerblue",
alpha = .5
) +
scale_x_continuous(NULL,
breaks = mu + seq(-4 * sigma, 4 * sigma, sigma),
limits = mu + c(-4 * sigma, 4 * sigma, sigma)) +
scale_y_continuous(
NULL,
breaks = NULL,
limits = c(0, dnorm(mu, mu, sigma)),
expand = expansion()
) +
ob_latex(
r"(f(x) =
\frac{1}{\sigma\sqrt{2\pi}}
e^{-\frac{1}{2}
\left(\frac{x-\mu}{\sigma}\right)^2})",
width = sigma * 3,
aspect_ratio = ratio,
border = 1,
filename = "zscore",
density = 600
) |>
place(ob_point(mu + sigma * .7, dnorm(mu + sigma * .7, mu, sigma)),
where = "right",
sep = 3) +
ob_label(label = paste0("*μ* = ", mu),
ob_point(mu, 0),
vjust = 0,
fill = NA,
color = "gray30") +
connect(
{p_mu <- ob_point(x = mu, y = dnorm(mu + sigma, mu, sigma))},
{p_sigma <- p_mu + ob_point(sigma, 0)},
label = ob_label(
paste0("*σ* = ", sigma),
fill = NA,
vjust = 0,
color = "gray30"
)
)
Text Color and Background Fill Color
The text color is black by default. It can be set to any color via the color
property.
The background color of the LaTeX expression will be white by default. If the LaTeX expression is placed inside an object with a filled background, you might want to give the expression the same background fill color.
ggdiagram() +
ob_circle(fill = "dodgerblue4", color = NA) +
ob_latex(
"A+B+C=D",
center = ob_point(),
color = "white",
fill = "dodgerblue4",
density = 900,
width = 1.5
)
Of course, you can always manipulate text color via LaTeX.
ggdiagram() +
ob_circle() +
ob_latex("Y={\\color[HTML]{CD69C9} X_1^2} + {\\color[HTML]{228B22} X_2^2}")
As a convenience, the latex-color
function will surround the expression with the right LaTeX expression to change the color.
ggdiagram() +
ob_circle() +
ob_latex(paste0("Y=",
latex_color("X_1^2", color = "orchid3"),
"+",
latex_color("X_2^2", color = "forestgreen")))
Rotation
The LaTeX expression can be rotated by setting the angle
property.
ggdiagram(theme_function = ggplot2::theme_minimal, font_size = 20) +
{s <- connect(ob_point(), ob_point(3,4))} +
ob_latex("\\left\\lVert\\vec{x}\\right\\rVert=5",
center = s@midpoint(),
height = .35,
density = 900,
angle = s@line@angle,
vjust = -.1) +
ob_latex(
paste0("\\vec{x}=", s@p2@auto_label),
vjust = 1.2,
hjust = 1.3,
center = s@p2,
height = .3,
density = 600
)
Image quality
The default density for ob_latex
images is 300 dots per inch. If a small expression is displayed as a large image, it will appear pixelated.
Setting the density to a higher value will usually create a better image.
Higher densities are not always better, however. In addition to using more memory and rendering more slowly, images with very high densities will sometimes appear blurry or pixelated.
How does ob_latex
work?
The ob_latex
function works through these steps:
- Create a .tex file with content based on the LaTeX standalone package.
- Create a .pdf file via the
tinytex::xelatex
function, if tinytex is available. Otherwise, use xelatex via a shell command. - Import the .pdf file as a raster bitmap via the
magick::image_read_pdf
function. - Store the raster bitmap in the
ob_latex@image
slot.
When rendered in ggplot2, the bitmap is displayed via ggplot2::annotation_raster
.