Tho*_*mas 9 evaluation scope r subset mapply
我不能使用subset的参数xtabs或aggregate(或I测试,包括任何功能ftable和lm)用mapply。以下调用因subset参数而失败,但它们可以在没有的情况下工作:
mapply(FUN = xtabs,
formula = list(~ wool,
~ wool + tension),
subset = list(breaks < 15,
breaks < 20),
MoreArgs = list(data = warpbreaks))
# Error in mapply(FUN = xtabs, formula = list(~wool, ~wool + tension), subset = list(breaks < :
# object 'breaks' not found
#
# expected result 1/2:
# wool
# A B
# 2 2
#
# expected result 2/2:
# tension
# wool L M H
# A 0 4 3
# B 2 2 5
mapply(FUN = aggregate,
formula = list(breaks ~ wool,
breaks ~ wool + tension),
subset = list(breaks < 15,
breaks < 20),
MoreArgs = list(data = warpbreaks,
FUN = length))
# Error in mapply(FUN = aggregate, formula = list(breaks ~ wool, breaks ~ :
# object 'breaks' not found
#
# expected result 1/2:
# wool breaks
# 1 A 2
# 2 B 2
#
# expected result 2/2:
# wool tension breaks
# 1 B L 2
# 2 A M 4
# 3 B M 2
# 4 A H 3
# 5 B H 5
Run Code Online (Sandbox Code Playgroud)
错误似乎是由于subset没有在正确的环境中评估参数。我知道我可以作为一种解决方法在data参数中进行子集化data = warpbreaks[warpbreaks$breaks < 20, ],但我希望提高我对 R 的了解。
我的问题是:
subset参数mapply?我尝试使用match.calland eval.parent,但到目前为止没有成功(更多细节在我之前的问题中)。formula参数在 中求值data = warpbreaks,但subset参数不是?简短的回答是,当您创建一个列表作为参数传递给函数时,它会在创建时进行评估。您收到的错误是因为 R 尝试创建您要在调用环境中传递的列表。
为了更清楚地看到这一点,假设您尝试创建要在调用之前传递的参数mapply:
f_list <- list(~ wool, ~ wool + tension)
d_list <- list(data = warpbreaks)
mapply(FUN = xtabs, formula = f_list, MoreArgs = d_list)
#> [[1]]
#> wool
#> A B
#> 27 27
#>
#> [[2]]
#> tension
#> wool L M H
#> A 9 9 9
#> B 9 9 9
Run Code Online (Sandbox Code Playgroud)
创建公式列表没有问题,因为在需要之前不会对它们进行评估,并且当然warpbreaks可以从全局环境访问,因此此调用mapply有效。
当然,如果您尝试在mapply通话前创建以下列表:
subset_list <- list(breaks < 15, breaks < 20)
Run Code Online (Sandbox Code Playgroud)
然后R会告诉你breaks没有找到。
但是,如果您warpbreaks在搜索路径中创建列表,则不会有问题:
subset_list <- list(breaks < 15, breaks < 20)
Run Code Online (Sandbox Code Playgroud)
所以你会认为我们可以把它传递给mapply一切都会好的,但现在我们得到一个新的错误:
subset_list <- with(warpbreaks, list(breaks < 15, breaks < 20))
subset_list
#> [[1]]
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [14] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
#> [27] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [40] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
#> [53] FALSE FALSE
#>
#> [[2]]
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE
#> [14] TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE TRUE
#> [27] FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
#> [40] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE
#> [53] TRUE FALSE
Run Code Online (Sandbox Code Playgroud)
那么为什么我们会得到这个?
问题在于传递给mapply该调用的任何函数eval,或者它们本身调用使用eval.
如果您查看源代码,mapply您会发现它接受您传递的额外参数并将它们放入名为 的列表中dots,然后它将传递给内部mapply调用:
mapply(FUN = xtabs, formula = f_list, subset = subset_list, MoreArgs = d_list)
#> Error in eval(substitute(subset), data, env) : object 'dots' not found
Run Code Online (Sandbox Code Playgroud)
如果您FUN自己调用另一个调用eval其任何参数的函数,则它将因此尝试eval使用 object dots,该对象在eval调用的环境中不存在。通过在包装器mapply上执行操作很容易看出这一点match.call:
mapply(function(x) match.call(), x = list(1))
[[1]]
(function(x) match.call())(x = dots[[1L]][[1L]])
Run Code Online (Sandbox Code Playgroud)
所以我们的错误的最小可重现示例是
mapply(function(x) eval(substitute(x)), x = list(1))
#> Error in eval(substitute(x)) : object 'dots' not found
Run Code Online (Sandbox Code Playgroud)
那么有什么解决办法呢?似乎您已经找到了一个非常好的方法,即手动设置您希望传递的数据框的子集。其他人可能会建议您探索purrr::map以获得更优雅的解决方案。
但是,它是可能得到mapply做你想要什么,这个秘密就是修改FUN把它变成一个匿名包装xtabs上的苍蝇子集:
mapply
#> function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
#> {
#> FUN <- match.fun(FUN)
#> dots <- list(...)
#> answer <- .Internal(mapply(FUN, dots, MoreArgs))
#> ...
Run Code Online (Sandbox Code Playgroud)