rvectorizationplotmath

How to vectorize R plotmath constructions


In this example, I want to construct expressions to vectorize the labelling of three ellipses in the following fig with expressions for the size of the ellipse, but each expression involves data values to be interpolated. I've looked at related queries, e.g., Alternative to Vector of Plotmath Expressions but these don't help.

enter image description here

Here is the code that produces the figure, but I had to construct lab1, lab2, lab3 as separate expressions. I'll show what I tried to vectorize it following, but that didn't work.

library(car)       # for ellipse
library(heplots)   # for label.ellipse

ybar <- c(0, 0)
S <- matrix(c(1, .5, .5, 2), 2, 2)
rownames(S) <- colnames(S) <- c("y1", "y2")

levels <- c(0.50, 0.68, 0.95)
# ellipse sizes
c <- qchisq(levels, df = 2) |> round(2) |> print()

# Vectorize me!
lab1 <- bquote(paste("c =", chi[2]^2, "(", .(levels[1]), ") =", .(c[1])))
lab2 <- bquote(paste("c =", chi[2]^2, "(", .(levels[2]), ") =", .(c[2])))
lab3 <- bquote(paste("c =", chi[2]^2, "(", .(levels[3]), ") =", .(c[3])))

e1 <- ellipse(ybar, S, radius=qchisq(levels[1], 2), 
        col = "blue", fill=TRUE, fill.alpha = 0.5,
        add=FALSE, 
        xlim=c(-8, 8), ylim=c(-9.5, 9.5), 
        asp=1, grid = FALSE,
        xlab = expression(y[1]), 
        ylab = expression(y[2]),
        cex.lab = 1.5)
label.ellipse(e1, label = lab1, label.pos = "S",
              cex = 1.2)

e2 <- ellipse(ybar, S, radius=qchisq(levels[2], 2), 
        col="blue", fill=TRUE, fill.alpha=0.3)
label.ellipse(e2, label = lab2, label.pos = "N",
              cex = 1.2)

e3 <- ellipse(ybar, S, radius=qchisq(levels[3], 2), 
        col="blue", fill=TRUE, fill.alpha=0.1)
label.ellipse(e3, label = lab3, label.pos = "N",
              cex = 1.2)

My initial thought was that I could just use:

lab <- c(
  bquote(paste("c =", chi^2, "(", .(levels[1]), ") =", .(c[1]))),
  bquote(paste("c =", chi^2, "(", .(levels[2]), ") =", .(c[2]))),
  bquote(paste("c =", chi^2, "(", .(levels[3]), ") =", .(c[3])))
)

and then refer to lab[1], lab[2] ... in the calls to heplots::label.ellipse(), but that produced no labels.

Edit: I accepted the answer by @r2evans as sufficient for my immediate needs in this example. However, this uses Map() or loop equivalents, to vectorize from the "outside" rather than internally, as can typically be done using paste() or glue(). This was really what I was hoping for.

I'd be grateful if anyone can find an alternative solution for this and similar cases, perhaps using latex2exp or something else.

In general, the documentation and tutorials I've found for plotmath in R using expression(), bquote(), paste() in various combinations is limited to simple cases.


Solution

  • We can use Map for this:

    cc <- c(1.39, 2.28, 5.99)
    levels <- c(0.5, 0.68, 0.95)
    labs <- Map(function(a,b) bquote(paste("c = ", chi[2]^2, "(", .(a), ") = ", .(b))),
                levels, cc)
    
    plot(1, 1, type = "n")
    text(1, 1.2, labs[[1]])
    text(1, 1.0, labs[[2]])
    text(1, 0.8, labs[[3]])
    

    base R plot with three bquote labels

    If you are not familiar with Map, it is similar in concept to lapply and sapply, in that it iterates over each element of its argument. The difference is that lapply works on one vector or list, and Map operates on one or more vectors or lists.

    If lapply(1:3, fun) is "unrolled" to something like

    list(
      fun(1),
      fun(2),
      fun(3)
    )
    

    then the equivalent is Map(fun, 1:3) (identical). It can go one step further, though, with Map(fun, 1:3, 11:13, 21:23) unrolling to

    list(
      fun(1, 11, 21),
      fun(2, 12, 22),
      fun(3, 13, 23)
    )
    

    A literal for loop might be

    labs <- vector("list", length(levels))
    for (i in seq_along(levels)) {
      labs[[i]] <- bquote(paste("c = ", chi[2]^2, "(", .(levels[i]), ") = ", .(cc[i])))
    }
    

    (which is another answer to your question, though I'm not as fond of it tbh :-)