有没有办法在 R 的函数中使用两个“..."语句? [英] Is there a way to use two '...' statements in a function in R?

查看:26
本文介绍了有没有办法在 R 的函数中使用两个“..."语句?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个同时调用 plot()legend() 的函数,如果用户可以指定一些额外的参数是理想的然后传递给 plot()legend().我知道我可以使用 ... 为两个函数之一实现这个:

I want to write a function that calls both plot() and legend() and it would be ideal if the user could specify a number of additional arguments that are then passed through to either plot() or legend(). I know I can achieve this for one of the two functions using ...:

foo.plot <- function(x,y,...) {
    plot(x,y,...)
    legend("bottomleft", "bar", pch=1)
}

foo.plot(1,1, xaxt = "n")

这会将 xaxt = "n" 传递给绘图.但是有没有办法例如通过例如title = "legend"legend() 调用而不在函数头中预先指定参数?

This passes xaxt = "n" to plot. But is there a way for example to pass e.g. title = "legend" to the legend() call without prespecifying the arguments in the function header?

从接受的答案中更新:我认为 VitoshKa 的方式是实现我想要的最优雅的方式.但是,在它按我想要的方式工作之前,我必须解决一些小问题.

Update from the accepted answer: I thought that VitoshKa's way was the most elegant to accomplish what I wanted. However, there were some minor issues that I had to get around with until it worked as I wanted.

首先,我检查了哪些参数要传递给legend,哪些参数要传递给plot.为此的第一步是查看 legend 的哪些参数是 legend 独有的,而不是 plot 和/或 par 的一部分:

At first, I checked which of the parameters I want to pass to legend and which to plot. First step to this end was to see which arguments of legend are unique to legend and not part of plot and/or par:

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 的输出.

I use dput() here, because the line plot.args <- c(names(formals(plot.default)), names(par())) always calls a new empty plot which I did not want. So, I used the output of dput in the following function.

接下来,我必须处理重叠的参数(通过 dput(largs.all[(largs.all %in% pargs.all)]) 获取它们).对于某些人来说,这是微不足道的(例如,xy),而其他人则被传递给这两个函数(例如,pch).但是,在我的实际应用中,我什至使用了其他策略(例如,adj 的不同变量名称,但在本示例中未实现).

Next, I had to deal with the overlapping arguments (get them via dput(largs.all[(largs.all %in% pargs.all)])). For some this was trivial (e.g., x, y) others get passed to both functions (e.g., pch). But, in my real application I even use other strategies (e.g., different variable names for adj, but not implemented in this example).

最后,do.call 函数必须以两种方式进行更改.首先,what 部分(即被调用的函数)需要是一个字符(即,'plot' 而不是 plot).并且参数列表的构造必须略有不同.

Finally, the do.call function had to be changed in two ways. First, the what part (i.e., called functions) needs to be a character (i.e., 'plot' instead of plot). And the argument list must be constructed slightly different.

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 被传递给 plotlegendtitle 只传递给 legendylim 仅用于 plot.

In this example, pch is passed to both plot and legend, title is only passed to legend, and ylim only to plot.

更新 2 基于 Gavin Simpson 的评论(另请参阅 Vitoshka 回答中的评论):
(i) 没错.
(ii) 它总是可以是一个字符.但是如果你有一个与函数同名的变量,那么你需要在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'))
在第一种情况下,列表包含两个元素 xlimxlim1xlim2(每个都是标量),在后一种情况下,列表只有xlim (这是长度为 2 的向量,这是我想要的).

(iii) You can use c(x = 1, y = 1, list()) and it works fine. However, what I really did (not in the example I gave but in my real function) is the following: c(x = 1, y = 1, xlim = c(0, 2), list(bla='foo'))
Please compare this with: c(list(x = 1, y = 1, xlim = c(0, 2)), list(bla='foo'))
In the first case, the list contains two elements xlim, xlim1 and xlim2 (each a scalar), in the latter case the list has only xlim (which is vector of length 2, which is what I wanted).

所以,对于我的例子,你的所有观点都是正确的.但是,对于我的真实函数(具有更多变量),我遇到了这些问题并想在这里记录它们.抱歉不准确.

So, you are right in all your points for my example. But, for my real function (with a lot more variables), I encountered these problems and wanted to document them here. Sorry for being imprecise.

推荐答案

一种自动方式:

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 必须从 lname 中过滤,以避免在 legend 调用中重复,以防用户提供pch",但您明白了.Carl W 于 2012 年 1 月do.call"仅适用于引号中的函数,如 Henrik 的更新.我在这里编辑它以避免将来混淆.

pch must be filtered from the lnames to avoid duplication in the legend call in case the user supplies 'pch', but you got the idea. Edited Jan 2012 by Carl W: "do.call" only works with the functions in quotes, as in the updates by Henrik. I edited it here to avoid future confusion.

这篇关于有没有办法在 R 的函数中使用两个“..."语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆