gpt4 book ai didi

r - 在 R 中使用 lapply 或 with 的用户定义函数中的非标准评估

转载 作者:行者123 更新时间:2023-12-04 15:45:55 26 4
gpt4 key购买 nike

我围绕 ftable 编写了一个包装器,因为我需要为许多变量计算具有频率和百分比的平面表。由于类“公式”的 ftable 方法使用非标准评估,包装器依赖于 do.callmatch.call 来允许使用ftablesubset 参数(更多细节在 my previous question 中)。

mytable <- function(...) {
do.call(what = ftable,
args = as.list(x = match.call()[-1]))
# etc
}

但是,我不能将此包装器与 lapplywith 一起使用:

# example 1: error with "lapply"
lapply(X = warpbreaks[c("breaks",
"wool",
"tension")],
FUN = mytable,
row.vars = 1)

Error in (function (x, ...) : object 'X' not found

# example 2: error with "with"
with(data = warpbreaks[warpbreaks$tension == "L", ],
expr = mytable(wool))

Error in (function (x, ...) : object 'wool' not found

这些错误似乎是由于 match.call 没有在正确的环境中进行评估。

由于此问题与 my previous one 密切相关,这里是我的问题的总结:

  • 带有 do.callmatch.call 的包装器不能与 lapplywith 一起使用。
  • 没有do.callmatch.call 的包装器不能使用ftablesubset 参数。

总结一下我的问题:

  • 我如何编写一个包装器,既允许使用 ftablesubset 参数,又可以与 lapply 一起使用与?我想避免使用 lapplywith,但我希望了解并纠正这些错误以提高我对 R 的了解。
  • lapply 的错误是否与来自 ?lapply 的以下注释有关?

    For historical reasons, the calls created by lapply are unevaluated, and code has been written (e.g., bquote) that relies on this. This means that the recorded call is always of the form FUN(X[[i]], ...), with i replaced by the current (integer or double) index. This is not normally a problem, but it can be if FUN uses sys.call or match.call or if it is a primitive function that makes use of the call. This means that it is often safer to call primitive functions with a wrapper, so that e.g. lapply(ll, function(x) is.numeric(x)) is required to ensure that method dispatch for is.numeric occurs correctly.

最佳答案

match.calllapply 一起使用的问题在于 match.call 返回的是 literal 调用传递进去,没有任何解释。为了看看发生了什么,让我们创建一个更简单的函数,它可以准确显示您的函数如何解释传递给它的参数:

match_call_fun <- function(...) {
call = as.list(match.call()[-1])
print(call)
}

当我们直接调用它时,match.call 正确地获取参数并将它们放入我们可以与 do.call 一起使用的列表中:

match_call_fun(iris['Species'], 9)

[[1]]
iris["Species"]

[[2]]
[1] 9

但请注意当我们使用 lapply 时会发生什么(我只包含了内部 print 语句的输出):

lapply('Species', function(x) match_call_fun(iris[x], 9))

[[1]]
iris[x]

[[2]]
[1] 9

由于 match.call 获取传递给它的literal 参数,它接收 iris[x],而不是正确解释的 我们想要的 iris['Species']。当我们使用 do.call 将这些参数传递给 ftable 时,它会在当前环境中查找对象 x ,然后返回一个错误它找不到它。我们需要解释

如您所见,添加 envir = parent.frame() 可以解决问题。这是因为,添加该参数会告诉 do.call 评估父框架中的 iris[x],这是 lapply 中的匿名函数其中 x 具有正确的含义。为了解实际效果,让我们创建另一个简单函数,该函数使用 do.call 从 3 个不同的环境级别打印 ls:

z <- function(...) {
print(do.call(ls, list()))
print(do.call(ls, list(), envir = parent.frame()))
print(do.call(ls, list(), envir = parent.frame(2)))
}

当我们从全局环境调用 z() 时,我们看到函数内部的空环境,然后是全局环境:

z()

character(0) # Interior function environment
[1] "match_call_fun" "y" "z" # GlobalEnv
[1] "match_call_fun" "y" "z" # GlobalEnv

但是当我们从lapply内部调用时,我们看到parent.frame上一层是lapply中的匿名函数:

lapply(1, z)

character(0) # Interior function environment
[1] "FUN" "i" "X" # lapply
[1] "match_call_fun" "y" "z" # GlobalEnv

因此,通过添加 envir = parent.frame()do.call 知道在 中评估 iris[x] >lapply 环境,它知道 x 实际上是 'Species',并且正确评估。

mytable_envir <- function(...) {
tab <- do.call(what = ftable,
args = as.list(match.call()[-1]),
envir = parent.frame())
prop <- prop.table(x = tab,
margin = 2) * 100
bind <- cbind(as.matrix(x = tab),
as.matrix(x = prop))
margin <- addmargins(A = bind,
margin = 1)
round(x = margin,
digits = 1)
}



# This works!
lapply(X = c("breaks","wool","tension"),
FUN = function(x) mytable_envir(warpbreaks[x],row.vars = 1))

至于为什么添加 envir = parent.frame() 会有所不同,因为这似乎是默认选项。我不是 100% 确定,但我的猜测是,当使用默认参数时,parent.frame 会在 do.call 中计算> 函数,返回运行 do.call 的环境。然而,我们正在做的是调用 parent.frame outside do.call,这意味着它返回比默认版本高一级.

这是一个将 parent.frame() 作为默认值的测试函数:

fun <- function(y=parent.frame()) {
print(y)
print(parent.frame())
print(parent.frame(2))
print(parent.frame(3))
}

现在看看当我们从 lapply 中调用它时会发生什么,无论是否传入 parent.frame() 作为参数:

lapply(1, function(y) fun())
<environment: 0x12c5bc1b0> # y argument
<environment: 0x12c5bc1b0> # parent.frame called inside
<environment: 0x12c5bc760> # 1 level up = lapply
<environment: R_GlobalEnv> # 2 levels up = globalEnv

lapply(1, function(y) fun(y = parent.frame()))
<environment: 0x104931358> # y argument
<environment: 0x104930da8> # parent.frame called inside
<environment: 0x104931358> # 1 level up = lapply
<environment: R_GlobalEnv> # 2 levels up = globalEnv

在第一个示例中,y 的值与您在函数内部调用 parent.frame() 时获得的值相同。在第二个示例中,y 的值与上一级环境(在 lapply 内)相同。因此,虽然它们看起来相同,但实际上它们在做不同的事情:在第一个示例中,parent.frame 在发现没有 y=< 时在函数内部进行计算 参数,在第二个参数中,parent.framelapply 匿名函数 first 中求值,然后调用 fun,然后传入其中。

关于r - 在 R 中使用 lapply 或 with 的用户定义函数中的非标准评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55813859/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com