代码之家  ›  专栏  ›  技术社区  ›  Henrik

有没有一种方法可以在R中的函数中使用两个“…”语句?

  •  35
  • Henrik  · 技术社区  · 14 年前

    我想编写一个函数来调用 plot() legend() 如果用户可以指定一些附加的参数,然后将这些参数传递给 标号() 图例() . 我知道我可以用 ... :

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

    这个通行证 xaxt = "n" 绘制。但是有没有一种方法可以通过例如 title = "legend" 图例() 调用而不预先指定函数头中的参数?


    从接受的答案更新: 我认为维托什卡的方式是实现我想要的最优雅的方式。然而,有些小问题我必须解决,直到它发挥我想要的作用。

    首先,我检查了要传递给哪个参数 legend 以及 plot . 到这一步的第一步是看看 传奇 是唯一的 传奇 不属于情节和/或段落:

    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 )但是,在我的实际应用中,我甚至使用其他策略(例如,不同变量名 adj ,但在此示例中未实现)。

    最后, do.call 函数必须以两种方式更改。首先,什么部分(即所谓的函数)需要是一个字符(即, '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 传递给两者 情节 传奇 , title 只传递给 传奇 ylim 只为 情节 .


    更新2基于Gavin Simpson的评论(另见Vitoshka回答中的评论):
    (一)没错。
    (ii)它可以是一个角色。但是,如果您有一个与函数同名的变量,那么您需要在 直拨电话 :

    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
    

    (三)可以使用 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的向量,这是我想要的)。

    所以,在我的例子中,你的所有观点都是正确的。但是,对于我的实际函数(有更多的变量),我遇到了这些问题,并希望将它们记录在这里。不好意思不精确。

    4 回复  |  直到 6 年前
        1
  •  21
  •   Carl Witthoft    12 年前

    自动方式:

    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,以避免在 legend 如果用户提供“PCH”,请致电,但您知道了。 Carl W于2012年1月编辑:“do.call”仅适用于引号中的函数,如Henrik的更新。我在这里编辑它是为了避免将来的混乱。

        2
  •  18
  •   Gavin Simpson    14 年前

    这些事情变得很棘手,如果不在函数中指定额外的参数,就没有简单的解决方案。如果你有 ... 在这两个 plot 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 例如,由你决定- BTY 将影响打印框类型和图例框类型。还要注意我已经处理好了 'pch' 不同的是,如果有人在 bar.plot() 调用,您将是i)在图例/绘图中使用不同的字符,您将收到有关 “PCH” 匹配两次。

    如你所见,这开始变得相当棘手…


    Joris的答案提供了一个有趣的解决方案,我的评论让我想起了在函数中的控制列表参数,比如 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设置的参数设置默认值,并连接其他参数。如果 参数() 包含所有具有默认值的图例参数。

        3
  •  6
  •   Joris Meys    14 年前

    一种方法是将参数列表与 do.call . 这不是最漂亮的解决方案,但确实有效。

    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列表中指定eg pch=2。你可以通过一些if条款来解决这个问题,我把它留给你来进一步处理。


    编辑:请参阅加文·辛普森的回答,了解这个想法的更好版本。

        4
  •  2
  •   moodymudskipper    6 年前

    我们可以建立一个 formalize 使任何函数与点兼容的函数:

    formalize <- function(f){
      # add ... to formals
      formals(f) <- c(formals(f), alist(...=))
      # release the ... in the local environment
      body(f)    <- substitute({x;y},list(x = quote(list2env(list(...))),y = body(f)))
      f
    }
    
    foo.plot <- function(x,y,...) {
      legend <- formalize(legend) # definition is changed locally
      plot(x,y,...)
      legend("bottomleft", "bar", pch = 1, ...)
    }    
    
    foo.plot(1,1, xaxt = "n", title = "legend")
    

    但它显示警告,因为Plot接收到的参数不是它所期望的。

    我们可以用 suppressWarnings 在plot调用上(但它会明显地抑制所有警告),或者我们可以设计另一个函数使 plot 函数(或任何函数)局部更能容忍点:

    casualize <- function(f){
      f_name <- as.character(substitute(f))
      f_ns   <- getNamespaceName(environment(f))
      body(f) <- substitute({
          # extract all args
          args <- as.list(match.call()[-1]);
          # relevant args only
          args <- args[intersect(names(formals()),names(args))]
          # call initial fun with relevant args
          do.call(getExportedValue(f_ns, f_name),args)})
      f
    }
    
    foo.plot <- function(x,y,...) {
      legend <- formalize(legend)
      plot   <- casualize(plot)
      plot(x,y,...)
      legend("bottomleft", "bar", pch = 1, ...)
    }
    
    foo.plot(1,1, xaxt = "n", title = "legend")
    

    现在它工作得很好!

    推荐文章