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

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)

Related

passing arguments to geom_point2 with mapply

My objective is pass lists as arguments to the function geom_point2 using lapply or analogously mapply. In similar situations, I had success passing a list (or lists) to geom_cladelabel as in:
mapply(function (x,y,z,w,v,u,t,s) geom_cladelabel(node=x, label=y,
align=F, etc. # Where x y z etc are lists.
Problem is related to the use of aes inside geom_point2. (not in geom_cladelabel):
In the case of geom_point2, the node info is inside aes, and I could't do it. Normally I do not get any error message, but it does not work.
The objective is to make this example work, but using mapply instead of writting geom_point2 two times.
# source("https://bioconductor.org/biocLite.R")
# biocLite("ggtree")
library(ggtree)
library(ape)
#standard code
newstree<-rtree(10)
node1<-getMRCA(newstree,c(1,2))
node2<-getMRCA(newstree,c(3,4))
ggtree(newstree)+
geom_point2(aes(subset=(node ==node1) ), fill="black", size=3, shape=23)+
geom_point2(aes(subset=(node ==node2) ), fill="green", size=3, shape=23)
#desire to substitute the geom_point2 layers with mapply or lapply:
#lapply(c(node1,node2), function (x) geom_point2(aes(subset=(node=x)))))
Here is a solution calling geom_point2 usig mapply:
library(ggtree)
ggtree(rtree(10)) +
mapply(function(x, y, z)
geom_point2(
aes_string(subset=paste("node ==", x)),
fill=y,
size=10,
shape=z
),
x=c(11,12),
y=c("green", "firebrick"),
z=c(23,24)
) +
geom_text2(aes(subset=!isTip, label=node))
The solution is in the aes_string(), which writes the value of x directly in the aesthetics. The default aes() does not pass on the value of x, but just the string "x". When plotting, ggtree then looks for a node called "x", and ends with an empty node list.
I guess this has to do with the variable being stored in the mapply-environment and not being passed on to the plot.
PS: Sorry for my too quick answer with do.call() earlier. It is useful, but off-topic here.

Error using bquote() for axis labelling

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.

Passing `...` to functions taking different arguments (including `...`)

I understand how to use ellipses when I only want one function to receive those arguments:
ok <- function(x, ...){
sum(x, ...)
}
x.in <- c(1:9, NA)
ok(x.in, na.rm=TRUE)
I start getting confused when some functions need only certain parts of .... I was thinking of using something like names(formals(cor)) to test for which arguments in ... to send where, but I don't see how to do this for sum or plot. In general, I want to write functions similar to the following:
yikes <- function(x, ...){
plot(x, ...)
sum(x, ...) + cor(x, ...)
}
x.in <- c(1:9, NA)
x.in.jitter <- jitter(x.in)
yikes(x.in, y=x.in.jitter, na.rm=TRUE, use="na.or.complete", type="o")
Ideally, yikes() would do the following:
plot(x=x.in, y=x.in.jitter, type="o")
sum(x.in, na.rm=TRUE) + cor(x=x.in, y=x.in.jitter, use="na.or.complete")
I suspect part of the solution may use match.call. How would I get a function like yikes() to work? Is this simply a bad idea?
Edit: The second link in the first comment goes a long way to answering this question given the situation where you know/ are willing to explicitly describe what parameters get passed to what functions. Using the arguments of functions called directly (e.g., cor, plot, sum in yikes), or indirectly (par via plot) to indicate what arguments supplied via ... should be used for a particular function is what I am searching for. I understand the case for cor, e.g.; but how would one do this for plot, without explicitly mentioning par or the arguments it takes?

how to pass an variable of an expression to curve() as its equation?

I've the following code:
e <- expression(x^2+3*x-3)
I want to draw the plot of the first derivative using R's symbolic derivate function D:
curve(D(e), from=0, to=10)
But then I get the following error:
Error in curve(expression(e), xname = "x", from = 0, to = 3000) :
'expr' must be a function, or a call or an expression containing 'x'
I tried to wrap D(e) in a call to eval(), but to no avail.
Trying a bit more:
substitute(expression(x^2+3*x-3), list(x=3))
results, as expected, in:
expression(3^2+3*3-3)
But:
substitute(e, list(x=3))
results in:
e
What is happening? How can I get this working?
It's a little clunky, but
eval(substitute(curve(y),list(y=D(e,"x"))))
seems to work. So does
do.call(curve,list(D(e,"x")))
functions are simpler to manipulate and test:
e <- expression(x^2+3*x-3)
de <- D(e, 'x')
fde <- function(x) eval(de)
curve(fde, from=0, to=10)

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