Error using bquote() for axis labelling - r

I'm experiencing a strange error when using the function bquote for axis labeling. The error is only occurring when applying the label (the greek symbol "mu") to the y-axis:
df <- data.frame(x=1:10, y=1:10)
plot(y~x, df, t="l", xlab=bquote(.("Size [")*mu*m*.("]"))) # works
plot(y~x, df, t="l", ylab=bquote(.("Size [")*mu*m*.("]"))) # doesn't work
# Error in plot.default(1:10, 1:10, ylab = "Size [" * mu * m * "]", xlab = quote("x"), : object 'mu' not found
I know I could use expression as an alternative in this case, but I'm trying to understand the error.

This is due to subtleties of evaluation rules and the specifics of the implementation of this plotting function.
Note that this does not occur when not using the formula interface
plot(df$x,df$y, type="l", ylab=bquote(.("Size [")*mu*m*.("]"))) #works as you expect
To see what is happening, examine the source
getAnywhere("plot.formula")
and you'll see the equivalent of this simplified example
plotex<-function(x,y,type="l",ylab,...) {
m=match.call(expand.dots = FALSE)
dots <- lapply(m$..., eval)
dots$xlab <- enquote(dots$xlab)
do.call(plot,c(list(x=x,y=y,type=type,ylab=ylab),dots))
}
The xlab argument is in ... and protected against evaluation with an explicit enquote. The ylab is a named parameter and its evaluation is forced by inclusion in the list provided to do.call.

Related

How to set x limits on varImpPlot

How can I change the x limits of a plot produced by varImpPlot from the randomForest package?
If I try
set.seed(4543)
data(mtcars)
mtcars.rf <- randomForest(mpg ~ ., data=mtcars, ntree=1000, keep.forest=FALSE,
importance=TRUE)
varImpPlot(mtcars.rf, scale=FALSE, type=1, xlim=c(0,15))
I get the following error:
Error in dotchart(imp[ord, i], xlab = colnames(imp)[i], ylab = "", main = if (nmeas == : formal argument "xlim" matched by multiple actual arguments".
This is because varImpPlot defines its own x limits, I think, but how could I get around this if I wanted to set the x limits myself (perhaps for consistency across plots)?
First I extracted the values using importance() (thanks to the suggestion from #dww)
impToPlot <- importance(mtcars.rf, scale=FALSE)
Then I plotted them using dotchart(), which allowed me to manually set the x limits (and any other plot features I'd like)
dotchart(sort(impToPlot[,1]), xlim=c(0,15), xlab="%IncMSE")

how do deparse & substitute work to allow access to an objects name?

My question is regarding the following code:
myfunc <- function(v1) {
deparse(substitute(v1))
}
myfunc(foo)
[1] "foo"
I typed in ?deparse and ?substitute into R and obtained the following:
deparse = Turn unevaluated expressions into character strings.
and
substitute = returns the parse tree for the (unevaluated) expression expr,
substituting any variables bound in env.
I don't seem to really understand this language. Would someone be able to simplify the technical aspect of these descriptions so that I could begin to appreciate how these two functions work together to allow us to do something cool like access the variable name of an object?
I struggle(d) with this too. The myplot() example from ?substitute is helpful. There, they define:
myplot <- function(x, y)
plot(x, y, xlab = deparse(substitute(x)),
ylab = deparse(substitute(y)))
calling
myplot(x=1:10, y = rnorm(10))
gives
whereas the alternative
x = 1:10
y = rnorm(10)
plot(x, y, xlab = x, ylab = y)
gives
Hopefully this shows what deparse(substitute()) is used for. In the plot version, the xlab and ylab arguments are the outputs of whatever was used to generate x and y. myplot knows to pass "character string versions of the actual arguments to the function" for xlab and ylab. (quotes from ?substitute)

R: why do.call plot come with y values

I am using do.call in building a function. but I met a problem like this:
z<-sample(1:3, 100, T)
y<-rnorm(100)
plot.list <- c(list(x=z,y=y))
do.call(plot, plot.list)
why the plot has included the y values?
Thanks!
This happens because plot.default attempts to create sensible labels for x and y if xlab and ylab are NULL
Using
deparse(substitute(y))
However, using do.call, the list is created, and the variable known as y is replaced by its contents thus this approach renders the label with the entire variable
foo <- function(y) deparse(substitute(y))
> do.call(foo, list(y=y))
[1] "c(1.19006018249756, -0.50627079218304, -0.103312348822805, 0.395028889714542, "
[2] "1.25258077222837, 2.63766225444435, 0.260296813253391, 0.280839960496168, "
[3] "-1.47428483557887, -0.585934600873922, 0.879979238185618, -0.0871689899965067, "
..... <truncated>
Setting xlab and ylab explicitly is the way to go.
do.call(plot, list(x=z,y=y, xlab = "x",ylab="y"))
To use do.call you'd need to pass z and y as names so they don't get evaluated ahead of time.
do.call("plot", list(x=as.name("z"), y=as.name("y")))
But the code sure feels like it would be clearer if you set xlab and ylab explicitly as #mnel recommends.

How to combine do.call() plot() and expression()

I am getting an error when I try and combine using expression with do.call and plot.
x <- 1:10
y <- x^1.5
I can get the plot I want by using only the plot function:
plot(y~x,xlab=expression(paste("Concentration (",mu,"M)")))
However, I would like to implement my plot using do.call. I have a really long list of parameters stored as a list, p. However, when I try and pass the list to do.call I get the following error:
p <- list(xlab=expression(paste("Concentration (",mu,"M)")))
do.call(plot,c(y~x,p))
Error in paste("Concentration (", mu, "M)") :
object 'mu' not found
I also tried defining the formula explicitly in the args passed to do.call. ie. do.call(plot,c(formula=y~x,p)). I do not understand why I am getting the error - especially because the following does not give an error:
do.call(plot,c(0,p))
(and gives the desired mu character in the xaxis).
You can use alist rather then list
p <- alist(xlab=expression(paste("Concentration (",mu,"M)")))
do.call(plot,c(y~x,p))
do.call evaluates the parameters before running the function; try wrapping the expression in quote:
p <- list(xlab=quote(expression(paste("Concentration (",mu,"M)"))))
do.call("plot", c(y~x, p))
Setting quote=TRUE also works. It in effect prevents do.call() from evaluating the elements of args before it passes them to the function given by what.
x <- 1:10
y <- x^1.5
p <- list(xlab=expression(paste("Concentration (",mu,"M)",sep="")))
do.call(what = "plot", args = c(y ~ x, p), quote = TRUE)

How can I auto-title a plot with the R call that produced it?

R's plotting is great for data exploration, as it often has very intelligent defaults. For example, when plotting with a formula the labels for the plot axes are derived from the formula. In other words, the following two calls produce the same output:
plot(x~y)
plot(x~y, xlab="x", ylab="y")
Is there any way to get a similar "intelligent auto-title"?
For example, I would like to call
plot(x~y, main=<something>)
And produce the same output as calling
plot(x~y, main="plot(x~y)")
Where the <something> inserts the call used using some kind of introspection.
Is there a facility for doing this in R, either through some standard mechanism or an external package?
edit: One suggestion was to specify the formula as a string, and supply that as the argument to a formula() call as well as main. This is useful, but it misses out on parameters than can affect a plot, such as using subsets of data. To elaborate, I'd like
x<-c(1,2,3)
y<-c(1,2,3)
z<-c(0,0,1)
d<-data.frame(x,y,z)
plot(x~y, subset(d, z==0), main=<something>)
To have the same effect as
plot(x~y, subset(d, z==0), main="plot(x~y, subset(d, z==0))")
I don't think this can be done without writing a thin wrapper around plot(). The reason is that R evaluates "supplied arguments" in the evaluation frame of the calling function, in which there's no way to access the current function call (see here for details).
By contrast, "default arguments" are evaluated in the evaluation frame of the function, from where introspection is possible. Here are a couple of possibilities (differing just in whether you want "myPlot" or "plot" to appear in the title:
## Function that reports actual call to itself (i.e. 'myPlot()') in plot title.
myPlot <- function(x,...) {
cl <- deparse(sys.call())
plot(x, main=cl, ...)
}
## Function that 'lies' and says that plot() (rather than myPlot2()) called it.
myPlot2 <- function(x,...) {
cl <- sys.call()
cl[[1]] <- as.symbol("plot")
cl <- deparse(cl)
plot(x, main=cl, ...)
}
## Try them out
x <- 1:10
y <- 1:10
par(mfcol=c(1,2))
myPlot(x,y)
myPlot2(y~x)
Here's a more general solution:
plotCaller <- function(plotCall, ...) {
main <- deparse(substitute(plotCall))
main <- paste(main, collapse="\n")
eval(as.call(c(as.list(substitute(plotCall)), main=main, ...)))
}
## Try _it_ out
plotCaller(hist(rnorm(9999), breaks=100, col="red"))
library(lattice)
plotCaller(xyplot(rnorm(10)~1:10, pch=16))
## plotCaller will also pass through additional arguments, so they take effect
## without being displayed
plotCaller(xyplot(rnorm(10)~1:10), pch=16)
deparse will attempt to break deparsed lines if they get too long (the default is 60 characters). When it does this, it returns a vector of strings. plot methods assume that 'main' is a single string, so the line main <- paste(main, collapse='\n') deals with this by concatenating all the strings returned by deparse, joining them using \n.
Here is an example of where this is necessary:
plotCaller(hist(rnorm(9999), breaks=100, col="red", xlab="a rather long label",
ylab="yet another long label"))
Of course there is! Here ya go:
x = rnorm(100)
y = sin(x)
something = "y~x"
plot(formula(something),main=something)
You might be thinking of the functionality of match.call. However that only really works when called inside of a function, not passed in as an argument. You could create your wrapper function that would call match.call then pass everything else on to plot or use substitute to capture the call then modify it with the call before evaluating:
x <- runif(25)
y <- rnorm(25, x, .1)
myplot <- function(...) {
tmp <- match.call()
plot(..., main=deparse(tmp))
}
myplot( y~x )
myplot( y~x, xlim=c(-.25,1.25) )
## or
myplot2 <- function(FUN) {
tmp1 <- substitute(FUN)
tmp2 <- deparse(tmp1)
tmp3 <- as.list(tmp1)
tmp4 <- as.call(c(tmp3, main=tmp2))
eval(tmp4)
}
myplot2( plot(y~x) )
myplot2( plot(y~x, xlim=c(-.25,1.25) ) )

Resources