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.
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.
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]])
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 :-)