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

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?

Related

Plot a normal distribution in R with specific parameters

I'd like to plot something like this:
plot(dnorm(mean=2),from=-3,to=3)
But it doesn't work as if you do:
plot(dnorm,from=-3,to=3)
what is the problem?
The answer you received from #r2evans is excellent. You might also want to consider learning ggplot, as in the long run it will likely make your life much easier. In that case, you can use stat_function which will plot the results of an arbitrary function along a grid of the x variable. It accepts arguments to the function as a list.
library(ggplot2)
ggplot(data = data.frame(x=c(-3,3)), aes(x = x)) +
stat_function(fun = dnorm, args = list(mean = 2))
curve(dnorm(x, mean = 2), from = -3, to = 3)
The curve function looks for the xname= variable (defaults to x) in the function call, so in dnorm(x, mean=2), it is not referencing an x in the calling environment, it is a placeholder for curve to use for iterated values.
The reason plot(dnorm, ...) works as it does is because there exists graphics::plot.function, since dnorm in that case is a function. When you try plot(dnorm(mean=2)), the dnorm(mean=2) is no longer a function, it is a call ... that happens to fail because it requires x (its first argument) be provided.
Incidentally, plot.function calls curve(...), so other than being a convenience function, there is very little reason to use plot(dnorm, ...) over curve(dnorm(x), ...) other than perhaps a little code-golf. The biggest advantage to curve is that it lets you control arbitrary arguments to the dnorm() function, whereas plot.function does not.

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.

How can you use ggplot to superimpose many plots of related functions in an automatic way?

I have a family of functions that are all the same except for one adjustable parameter, and I want to plot all these functions on one set of axes all superimposed on one another. For instance, this could be sin(n*x), with various values of n, say 1:30, and I don't want to have to type out each command individually -- I figure there should be some way to do it programatically.
library(ggplot2)
define trig functions as a function of frequency: sin(x), sin(2x), sin(3x) etc.
trigf <- function(i)(function(x)(sin(i*x)))
Superimpose two function plots -- this works manually of course
ggplot(data.frame(x=c(0,pi)), aes(x)) + stat_function(fun=trigf(1)) + stat_function(fun=trigf(2))
now try to generalize -- my idea was to make a list of the stat_functions using lapply
plotTrigf <- lapply(1:5, function(i)(stat_function(fun=function(x)(sin(i*x))) ))
try using the elements of the list manually but it doesn't really work -- only the i=5 plot is shown and I'm not sure why when that's not what I referenced
ggplot(data.frame(x=c(0,pi)), aes(x)) +plotTrigf[[1]] + plotTrigf[[2]]
I Thought this Reduce might handle the 'generalized sum' to add to a ggplot() but it doesn't work -- it complains of a non-numeric argument to binary operator
Reduce("+", plotTrigf)
So I'm kind of stuck both in executing this strategy, or perhaps there's some other way to do this.
Are you using version R <3.2? The problem is that you actually need to evaluate your i parameter in your lapply call. Right now it's being left as a promise and not getting evaulated till you try to plot and at that point i has the last value it had in the lapply loop which is 5. Use:
plotTrigf <- lapply(1:5, function(i) {force(i);stat_function(fun=function(x)(sin(i*x))) })
You can't just add stat_function calls together, even without Reduce() you get the error
stat_function(fun=sin) + stat_function(fun=cos)
# Error in stat_function(fun = sin) + stat_function(fun = cos) :
# non-numeric argument to binary operator
You need to add them to a ggplot object. You can do this with Reduce() if you just specify the init= parameter
Reduce("+", plotTrigf, ggplot(data.frame(x=c(0,pi)), aes(x)))
And actually the special + operator for ggplot objects allows you to add a list of objects so you don't even need the Reduce at all (see code for ggplot2:::add_ggplot)
ggplot(data.frame(x=c(0,pi)), aes(x)) + plotTrigf
The final result is
You need to use force in order to make sure the parameter is being evaluated at the right time. It's a very useful technique and a common source of confusion in loops, you should read about it in Hadley's book http://adv-r.had.co.nz/Functions.html
To solve your question: you just need to add force(i) when defining all the plots, inside the lapply function, before making the call to stat_function. Then you can use Reduce or any other method to combine them. Here's a way to combine the plots using lapply (note that I'm using the <<- operator which is discouraged)
p <- ggplot(data.frame(x=c(0,pi)), aes(x))
lapply(plotTrigf, function(x) {
p <<- p + x
return()
})

Change of colors in compare.matrix command in r

I'm trying to change the colors for the compare.matrix command in r, but the error is always the same:
Error in image.default(x = mids, y = mids, z = mdata, col = c(heat.colors(10)[10:1]), :
formal argument "col" matched by multiple actual arguments
My code is very simple:
compare.matrix(current,ech_b1,nbins=40)
and some of my attempts are:
compare.matrix(current,ech_b1,nbins=40,col=c(grey.colors(5)))
compare.matrix(current,ech_b1,nbins=40,col=c(grey.colors(10)[10:1]))
Assuming you're using compare.matrix() from the SDMTools package, the color arguments appear to be hard-coded into the function, so you'll need to redefine the function in order to make them flexible:
# this shows you the code in the console
SDMTools::compare.matrix
function(x,y,nbins,...){
#---- preceding code snipped ----#
suppressWarnings(image(x=mids, y=mids, z=mdata, col=c(heat.colors(10)[10:1]),...))
#overlay contours
contour(x=mids, y=mids, z=mdata, col="black", lty="solid", add=TRUE,...)
}
So you can make a new one like so, but bummer, there are two functions using the ellipsis that have a col argument predefined. If you'll only be using extra args to image() and not to contour(), this is cheap and easy.
my.compare.matrix <- function(x,y,nbins,...){
#---- preceding code snipped ----#
suppressWarnings(image(x=mids, y=mids, z=mdata,...))
#overlay contours
contour(x=mids, y=mids, z=mdata, col="black", lty="solid", add=TRUE)
}
If, however, you want to use ... for both internal calls, then the only way I know of to avoid confusion about redundant argument names is to do something like:
my.compare.matrix <- function(x,y,nbins,
image.args = list(col=c(heat.colors(10)[10:1])),
contour.args = list(col="black", lty="solid")){
#---- preceding code snipped ----#
contour.args[[x]] <- contour.args[[y]] <- image.args[[x]] <- image.args[[y]] <- mids
contour.args[[z]] <- image.args[[z]] <- mdata
suppressWarnings(do.call(image, image.args))
#overlay contours
do.call(contour, contour.args)
}
Decomposing this change: instead of ... make a named list of arguments, where the previous hard codes are now defaults. You can then change these items by renaming them in the list or adding to the list. This could be more elegant on the user side, but it gets the job done. Both of the above modifications are untested, but should get you there, and this is all prefaced by my above comment. There may be some other problem that cannot be detected by SO Samaritans because you didn't specify the package or the data.

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)

Resources