bquote: How to include an expression saved as a string object?
Asked Answered
O

2

6

My goal is to annotate a plot with the slope of a best-fit line and label the units of the slope, where the label is saved as a separate string object. I'm having trouble figuring out how to get bquote() to convert a string object into an expression, and combine it with other evaluated statements.

A demonstration:

# example data:
x <- c(1:20)                   # x units: time
y <-  x * rnorm(20, 10, 2)     # y units: length per time

unit.label <- "L%.%T^-2"       # label for slope of best fit line 
lm1 <- summary(lm(y ~ x))

plot(y ~ x)

The problem occurs when I try to annotate the plot. I can get bquote() to display the slope:

text(median(x), min(y), bquote(slope: .(round(lm1$coefficients[2], 2))) )

I can also get bquote() to show the slope's units:

plot(y ~ x)
text(median(x), min(y), bquote(.(parse(text = unit.label))) )

But I'm unable to combine the label and the slope into a single bquote() statement:

plot(y ~ x)
text(median(x), min(y), bquote(slope: .(round(lm1$coefficients[2], 2)) 
.(parse(text = unit.label))) ) 
# Error: unexpected symbol in "text(median(x), min(y), bquote(slope: 
# .(round(lm1$coefficients[2], 2)) ."

Using paste(), the unit label appears along with the slope, but the label isn't read as an expression:

plot(y ~ x)
text(median(x), min(y), bquote(slope: .(paste(round(lm1$coefficients[2], 2), 
as.expression(unit.label))))
)

Where am I going wrong? Is it a simple syntax problem in my bquote command? Thanks for any suggestions!

Overriding answered 9/6, 2014 at 23:7 Comment(1)
+1 for following the post guidelines and using a reproducible example. Welcome to Stack Overflow! Good question.Their
M
6

1) parse char string Create the character string desired (ensuring that it represents an expressoin that is syntactically valid in R) and then parse it. Here main_s is the character string:

fm <- lm(y ~ x)

main_s <- paste("slope:", round(coef(fm)[2], 2), "~", unit.label)
plot(0, main = parse(text = main_s))

The statement setting main_s could alternately be replaced with the following sprintf statement which is arguably more readable:

main_s <- sprintf("slope: %.2f ~ %s", coef(fm)[2], unit.label)

2) bquote The above is probably the most straight-forward way to handle this but to use bquote try this where unit.label_c is a call object and fm is as defined above:

unit.label_c <- parse(text = unit.label)[[1]]
plot(0, main = bquote(slope: .(round(coef(fm)[2], 2)) ~ .(unit.label_c)))

In either case we get this:

screenshot

UPODATE Added (2). Also some improvements and corrections.

Miller answered 9/6, 2014 at 23:42 Comment(0)
R
4

I like to build the expression first using substitute and never storing parts of the expression as character vectors. This will work

plotlabel = substitute( slope~L%.%T^-2, 
    list(slope=round(lm1$coefficients[2], 2)))
plot(y ~ x)
text(median(x), min(y), plotlabel)

But as to why bquote did not work is an interesting question. Clearly the two types of objects you are trying to replace are different classes. In the case of the slope value, that's a basic character, in the case if the unit.label, you're calling parse which returns an expression. To me it seems tricky to get expressions into bquote. It's odd that it works all by itself.

bquote(.(parse(text = unit.label))) #works -> expresion
bquote(.(parse(text = unit.label))+1) #doesn't work -> call

The former returns an "expression", the latter returns a "call". Really we'd like the second one to return an expression as well, but it's trying to add "+1" to the expression already returned by parse and that just doesn't seem to work nicely.

Residue answered 10/6, 2014 at 1:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.