有没有办法在R中的函数中使用两个“…”语句?
我想写一个调用plot()
和legend()
的函数,如果用户可以指定一些额外的参数,然后传递给plot()
或legend()
,这将是理想的。 我知道我可以实现这两个function之一使用...
:
foo.plot <- function(x,y,...) { plot(x,y,...) legend("bottomleft", "bar", pch=1) } foo.plot(1,1, xaxt = "n")
这通过xaxt = "n"
来绘制。 但是有没有办法例如传递title = "legend"
给legend()
调用,而不需要预先指定函数头中的参数?
从接受的答案更新:我认为,VitoshKa的方式是最高雅的完成我想要的。 然而,我还有一些小问题需要解决,直到按照我的意愿工作。
起初,我检查了哪些参数要传递给legend
,哪些要plot
。 为此,第一步是查看legend
哪些参数是legend
唯一的,而不是图和/或参数的一部分:
legend.args <- names(formals(legend)) plot.args <- c(names(formals(plot.default)), names(par())) dput(legend.args[!(legend.args %in% plot.args)])
我在这里使用dput()
,因为plot.args <- c(names(formals(plot.default)), names(par()))
总是调用一个我不想要的新的空图。 所以,我在下面的函数中使用了dput
的输出。
接下来,我必须处理重叠的参数(通过dput(largs.all[(largs.all %in% pargs.all)])
)获取它们。 对于一些这是微不足道的(例如, x
, y
)其他人得到传递给两个函数(例如, pch
)。 但是,在我的真实应用程序中,我甚至使用其他策略(例如,不同的variables名称为adj
,但在本例中没有实现)。
最后, do.call
函数必须以两种方式进行更改。 首先,什么部分(即所谓的function)需要是一个angular色(即'plot'
而不是plot
)。 而参数列表必须构造稍微不同。
foo.plot <- function(x,y,...) { leg.args.unique <- c("legend", "fill", "border", "angle", "density", "box.lwd", "box.lty", "box.col", "pt.bg", "pt.cex", "pt.lwd", "xjust", "yjust", "x.intersp", "y.intersp", "text.width", "text.col", "merge", "trace", "plot", "ncol", "horiz", "title", "inset", "title.col", "title.adj") leg.args.all <- c(leg.args.unique, "col", "lty", "lwd", "pch", "bty", "bg", "cex", "adj", "xpd") dots <- list(...) do.call('plot', c(list(x = x, y = x), dots[!(names(dots) %in% leg.args.unique)])) do.call('legend', c(list("bottomleft", "bar"), dots[names(dots) %in% leg.args.all])) } foo.plot(1,1,pch = 4, title = "legendary", ylim = c(0, 5))
在这个例子中, pch
传递给plot
和legend
, title
只传递给legend
,而ylim
只传递给plot
。
更新2根据Gavin Simpson的评论(另见Vitoshka的回答):
(i)这是正确的。
(二)它可以永远是一个字符。 但是如果你有一个和函数同名的variables,那么你需要引用do.call
的函数名:
min.plot <- function(x,y,plot=TRUE) if(plot == TRUE) do.call(plot, list(x = x, y = y)) min.plot(1,1) Error in do.call(plot, list(x = x, y = y)) : 'what' must be a character string or a function
(iii)你可以使用c(x = 1, y = 1, list())
,它工作正常。 但是,我真正做的(不是在我给出的例子中,而是在我的实际函数中)如下: c(x = 1, y = 1, xlim = c(0, 2), list(bla='foo'))
请将它与下列内容进行比较: c(list(x = 1, y = 1, xlim = c(0, 2)), list(bla='foo'))
在第一种情况下,列表包含两个元素xlim
, xlim1
和xlim2
(每个标量),在后一种情况下,列表只有xlim
(这是长度为2的向量,这是我想要的)。
所以,你们对我的榜样是正确的。 但是,对于我的实际function(有更多的variables),我遇到了这些问题,并希望在这里logging它们。 抱歉不准确。
一种自动方式:
foo.plot <- function(x,y,...) { lnames <- names(formals(legend)) pnames <- c(names(formals(plot.default)), names(par())) dots <- list(...) do.call('plot', c(list(x = x, y = x), dots[names(dots) %in% pnames])) do.call('legend', c("bottomleft", "bar", pch = 1, dots[names(dots) %in% lnames])) }
在用户提供“pch”的情况下,必须从lnames中过滤pch以避免legend
调用中的重复,但是你明白了。 Carl W 2012年1月编辑:“do.call”仅适用于引号中的函数,就像Henrik的更新一样。 我在这里编辑它,以避免将来的混乱。
这些事情变得棘手,没有在你的函数中指定额外的参数是不容易的解决scheme。 如果你在plot
和legend
调用中遇到了legend
特定参数,你最终会得到警告。 例如,用:
foo.plot <- function(x,y,...) { plot(x,y,...) legend("bottomleft", "bar", pch = 1, ...) }
您会收到以下警告:
> foo.plot(1, 1, xjust = 0.5) Warning messages: 1: In plot.window(...) : "xjust" is not a graphical parameter 2: In plot.xy(xy, type, ...) : "xjust" is not a graphical parameter 3: In axis(side = side, at = at, labels = labels, ...) : "xjust" is not a graphical parameter 4: In axis(side = side, at = at, labels = labels, ...) : "xjust" is not a graphical parameter 5: In box(...) : "xjust" is not a graphical parameter 6: In title(...) : "xjust" is not a graphical parameter
有问题绕过这个问题,请参阅plot.default
和它的本地函数定义为函数,如axis
, box
等函数包装,你会像localPlot()
包装,内联函数和调用,而不是直接plot()
直接。
bar.plot <- function(x, y, pch = 1, ...) { localPlot <- function(..., legend, fill, border, angle, density, xjust, yjust, x.intersp, y.intersp, text.width, text.col, merge, trace, plot = TRUE, ncol, horiz, title, inset, title.col, box.lwd, box.lty, box.col, pt.bg, pt.cex, pt.lwd) plot(...) localPlot(x, y, pch = pch, ...) legend(x = "bottomleft", legend = "bar", pch = pch, ...) }
(相当为什么'plot'
参数需要一个默认是超出我的,但它不会工作,没有给它默认的TRUE
。)
现在这个工作没有任何警告:
bar.plot(1, 1, xjust = 0.5, title = "foobar", pch = 3)
如何处理像bty
这样的graphics参数将取决于你 – bty
将影响bty
types和图例框types。 还要注意,我处理'pch'
不同,因为如果有人在bar.plot()
调用中使用了这个参数,那么你将会在图例中使用不同的字符,并且会得到一个关于'pch'
匹配两次。
正如你所看到的,这开始变得非常棘手。
Joris的答案提供了一个有趣的解决scheme,我lme()
这样的函数中的控制列表参数。 这里是我的版本的Joris的答案实现这个控制列表想法的想法:
la.args <- function(x = "bottomleft", legend = "bar", pch = 1, ...) c(list(x = x, legend = legend, pch = pch), list(...)) foo.plot <- function(x,y, legend.args = la.args(), ...) { plot(x, y, ...) do.call(legend, legend.args) }
就像这样,使用Jori的第二个示例调用,适当修改:
foo.plot(1,1, xaxt = "n", legend.args=la.args(bg = "yellow", title = "legend"))
在设置la.args()
函数时,你可以像你想的那样完整 – 在这里我只设置Joris设置的参数的默认值,并且连接其他的参数。 如果la.args()
包含所有具有默认值的图例参数,会更容易。
一种方法是使用参数列表和do.call
结合使用。 这不是最美丽的解决scheme,但它确实有效。
foo.plot <- function(x,y,legend.args,...) { la <- list( x="bottomleft", legend="bar", pch=1 ) if (!missing(legend.args)) la <- c(la,legend.args) plot(x,y,...) do.call(legend,la) } foo.plot(1,1, xaxt = "n") foo.plot(1,1, xaxt = "n",legend.args=list(bg="yellow",title="legend"))
一个缺点是你不能在legend.args列表中指定例如pch = 2。 你可以用一些if子句来解决这个问题,我会把它留给你,以便进一步调整它。
编辑:看到加文辛普森的答案更好的版本这个想法。