(Pre)为工厂和内部提供变量

时间:2014-06-24 01:17:23

标签: r

我正在阅读Hadley的Advanced R并尝试一些东西。我试图创建一个lazy闭包函数,该函数在其环境中使用提供的data.frame返回一个函数,并使用with并稍后能够提供其他函数参数。

lazy <- function(dataframe, x) {
    function(FUN, x, ...) {
        with(dataframe, FUN(x = x, ...))
    }
}

lz_factory <- lazy(mtcars, "mpg")

lz_factory(mean)
lz_factory(cor, y="hp")

所以我期待数据帧是函数环境的一部分(browser看看确认)。但是,变量名x不供应,当我使用y作为第一个cor参数时,我无法提供新变量FUN。这与为使用non-standard evaluation (NSE)的函数(with)提供字符有关。我希望哈德利叔叔为我感到骄傲,但我用evalparsesubstitute翻找错误。这意味着我不能完全了解R如何处理事情。我知道为什么它不起作用(NSE)但不知道如何使它起作用。这是错误:

> lz_factory(mean)
Error in FUN(x = x, ...) : argument "x" is missing, with no default

> lz_factory(cor, y="hp")
Error in is.data.frame(x) : argument "x" is missing, with no default

我认为我可以使用替补来解决它,因为Hadley用xyplot显示在这里,但这也是一个失败,如下所示:

lazy <- function(dataframe, default) {
    function(FUN, x, ...) {
        if (missing(x)) x <- default
        eval(substitute(with(dataframe, FUN(x, ...))))
    }
}

lz_factory <- lazy(mtcars, "mpg")

lz_factory(mean)
lz_factory(cor, y="hp")


> lz_factory(mean)
[1] NA
Warning message:
In mean.default("mpg") : argument is not numeric or logical: returning NA

> lz_factory(cor, y="hp")
Error in cor("mpg", y = "hp") : 'x' must be numeric

那么我怎样才能使这个惰性函数工作呢:

  1. 使用环境中包含的数据框生成自己的函数
  2. 如果需要,允许我提供x,如果不使用,则允许我使用最初提供的默认值
  3. 允许我将未知变量传递给lz_factory
  4. 创建的函数

    最理想的是,我希望此功能可以与with一起使用。如果那不可能,那么知道原因会很好。最后,如果不能使用with,我该如何使该功能运作?

2 个答案:

答案 0 :(得分:5)

这是@ MrFlick功能的略微简化版本:

lazy <- function(df, x_var = NULL) {
  x <- substitute(x_var)

  function(FUN, ...) {
    call <- substitute(FUN(...))
    if (is.null(call$x) && !is.null(x)) {
      call$x <- x
    }
    eval(call, df, parent.frame())
  }
}

关键是更好地利用substitute()的功能,并通过直接使用eval来避免with()

答案 1 :(得分:4)

这个功能怎么样

lazy <- function(dataframe, ...) {
    pdots <- substitute(list(...))
    if(is.null(names(pdots)) || names(pdots)[1]=="") {
        names(pdots)[2]<-"x"
    }
    function(FUN, ...) {
        dots <- substitute(list(...))[-1]
        if (is.null(dots$x)) {
            dots$x <- pdots$x
        }
        with(dataframe, do.call(FUN, as.list(dots)))
    }
}

这允许您在没有引号的mtcars中使用变量的名称。例如

lz_factory <- lazy(mtcars, mpg)
lz_factory(mean)
# [1] 20.09062
lz_factory(mean, x=hp)
# [1] 146.6875
lz_factory(cor, y=hp)
# [1] -0.7761684

这里我们使用额外的替换来确保我们得到延迟评估并允许您使用不带引号的变量名称。 with将负责评估表达式。我猜可能有办法简化这个,但至少看起来有效。