mapply 的 MoreArgs 参数什么时候可以不被 R 的向量回收规则替换?

When can mapply's MoreArgs argument not be replaced by R's vector recycling rules?

我想举一个例子来说明 mapplyMoreArgs 论点何时有用。我被彻底打败了。令人发指的是,甚至 mapply 文档中给出的示例也不充分。文档给出 mapply(rep, times = 1:4, MoreArgs = list(x = 42)) 作为使用 MoreArgs 的唯一示例,但我发现 R 的向量回收规则意味着 mapply(rep, times = 1:4, 42) 给出完全相同的输出。

那么什么时候MoreArgs不可以只删除相应的MoreArgs = list(...)包装器来代替呢?我试着自己想出一些例子,但每次都失败了。例如,mapply(rnorm,1:10,11:20,MoreArgs = list(5),SIMPLIFY = FALSE) 等同于 mapply(rnorm,1:10,11:20,5,SIMPLIFY = FALSE)

根据评论的引导, 示例我们需要为每次调用 mapply 正在调用的函数回收一个向量。但是,似乎 mapply 在使用包含所述向量的列表之间在功能上没有区别 - 例如list(c(0.6,0.3,0.1)) - 作为 ... 参数或 MoreArgs 参数。在 MoreArgs 的情况下,列表的内容按预期回收,而在 ... 的情况下,向量回收规则意味着列表的内容将在每种情况下回收,从而提供相同的功能MoreArgs。这让我回到了最初的问题:什么时候可以使用 mapplyMoreArgs 参数来提供无法从 ... 参数和向量回收规则中获得的功能?我还没有看到通过简单地删除 MoreArgs= 参数并将相关部分传递给 ....

无法获得相同功能的情况

如果我们传递命名参数,MoreArgs 可以在 list 中传递。有时,参数取决于函数中参数的顺序,并按该顺序计算。例如如果我们想要指定参数 n

mapply(rnorm, 1:10, 11:20, MoreArgs = list(n = 5),SIMPLIFY = FALSE)

它甚至在没有 MoreArgs 的情况下也能正常工作,因为它对传递的每个元素进行回收。但是,在 MoreArgs 中传递参数有助于区分函数参数与 mapply/Map 的输入。这些主要在我们调用没有 lambda/anonymous 函数的函数时使用,因为它也可以写成

mapply(function(x, y) rnorm(n = 5, mean = x, sd = y), 1:10, 11:20, SIMPLIFY = FALSE)

请注意,下面的失败是因为 mapply 没有输入,只有函数参数输入

mapply(rnorm, MoreArgs = list(n = 5, mean = 1:10, sd = 11:20))

当我们按照 OP post 中指定的顺序传递没有名称的参数时,假设 n 是 5,它不会被视为 5,而是现在的 sd 因为 rnorm 的顺序是 n, meansd?rnorm[=33= 的 Usage ]

rnorm(n, mean = 0, sd = 1)

mapply(rnorm, 1:10, 11:20, 5, SIMPLIFY = FALSE)

比较
mapply(rnorm, 5, 1:10, 11:20, SIMPLIFY = FALSE)

mapply(rnorm, 1:10, 11:20, n = 5, SIMPLIFY = FALSE)

可以用其他方式写成 MoreArgs 并显示在第一个语法中

更新:我开始相信这个答案是不正确的。我下面的大胆担忧似乎是正确的,使这个答案的关键部分无效。正确答案见我的另一个答案。

根据Henrik的评论,我们可以给出答案。 MoreArgs 最适合用于您想要回收的参数,但不是 Mapply 想要的方式。当所述参数是向量或列表时,通常就是这种情况。以下是一个扩展示例。

考虑 rmultinom 函数。它的行为非常恶劣。它需要 3 个参数:n 控制要采样的数量,size 控制每个样本的大小,以及概率向量 prob,其中向量中的每个概率都可以看作length(prob) 面的偏向骰子掷出对应于 prob 条目的面的几率。它的输出是一个向量,显示偏置骰子落在每一侧的次数。例如,这是一种可能的输入和输出(从上到下阅读,而不是从左到右阅读):

 rmultinom(3,10,c(0.6,0.3,0.1))
     [,1] [,2] [,3]
[1,]    6    6    6
[2,]    2    2    4
[3,]    2    2    0

为什么我说rmultinom行为不端?因为这也是有效的:

rmultinom(3,10,c(0.6))
     [,1] [,2] [,3]
[1,]   10   10   10

您可能认为给 rmultinom 一个概率向量,但总和不为 1 会引发错误,但由于我不声称理解的技术原因,它不会。

让我们回到mapply。假设我想用 n 作为 3、4 和 5 进行测试,第一次测试的样本为 size 10,第二次测试为 11,最后一次测试为 12。我希望 prob 固定在 c(0.6,0.3,0.1)。如果我弄错了最后一点,那么我们就会得到我们之前的不良行为。

让我们开始尝试 mapply(rmultinom,3:5,10:12,c(0.6,0.3,0.1)):

mapply(rmultinom,3:5,10:12,c(0.6,0.3,0.1))
[[1]]
     [,1] [,2] [,3]
[1,]   10   10   10

[[2]]
     [,1] [,2] [,3] [,4]
[1,]   11   11   11   11

[[3]]
     [,1] [,2] [,3] [,4] [,5]
[1,]   12   12   12   12   12

那没用。我可以告诉您,使用 list(0.6,0.3,0.1) 作为我们的最后一个参数也不起作用。它给出了完全相同的输出,所以我什至不会展示它。忘记 clist 并直接使用 0.6,0.3,0.1 怎么样?

mapply(rmultinom,3:5,10:12,0.6,0.3,0.1)
Error in (function (n, size, prob)  : 
  unused arguments (dots[[4]][[1]], dots[[5]][[1]])

运气不好。由于 rmultinom 的工作方式,我无法判断向量回收规则或 mapply 是否背叛了我们,但我想不出任何明智的方法来使用我们还没有的回收规则尝试过。那么将 MoreArgs 与我们的原始向量一起使用怎么样?

Error in mapply(rmultinom, 3:5, 10:12, MoreArgs = c(0.6, 0.3, 0.1)) : 
  argument 'MoreArgs' of 'mapply' is not a list

那好吧,我们必须使用列表...

mapply(rmultinom,3:5,10:12,MoreArgs=list(0.6,0.3,0.1))
Error in (function (n, size, prob)  : unused arguments (0.3, 0.1)

我们的向量列表呢!

> mapply(rmultinom,3:5,10:12,MoreArgs=list(c(0.6,0.3,0.1)))
[[1]]
     [,1] [,2] [,3]
[1,]    4    8    9
[2,]    3    1    0
[3,]    3    1    1

[[2]]
     [,1] [,2] [,3] [,4]
[1,]    6    8    4    7
[2,]    4    3    7    3
[3,]    1    0    0    1

[[3]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    7    6    9    8    7
[2,]    2    2    3    3    4
[3,]    3    4    0    1    1

现在我们终于有了一些有用的东西。顺便说一下,mapply(rmultinom,3:5,10:12,list(c(0.6,0.3,0.1))) 也可以,但是 我认为 那是因为 mapply 知道将 list(c(0.6,0.3,0.1)) 作为 MoreArgs=list(c(0.6,0.3,0.1)) 传递。我很想知道是否有人可以证实这一点。可能 mapply 正在回收 list(c(0.6,0.3,0.1)) 的第一个条目,这将是向量 c(0.6,0.3,0.1) 呈现整个答案 false

假设以上不是回收的情况,那么我们可以自信地说我们找不到向量回收的方式来做我们想做的事情,因此这回答了问题并表明我们需要MoreArgs。但作为奖励,值得展示如何使用匿名函数执行此操作:

mapply(function(x,y,z) rmultinom(x,y,c(0.6,0.3,0.1)),3:5,10:12)
[[1]]
     [,1] [,2] [,3]
[1,]    5    7    4
[2,]    4    2    5
[3,]    1    1    1

[[2]]
     [,1] [,2] [,3] [,4]
[1,]    5    7    5    5
[2,]    5    1    5    4
[3,]    1    3    1    2

[[3]]
     [,1] [,2] [,3] [,4] [,5]
[1,]   10    8    8    8    8
[2,]    2    2    3    3    3
[3,]    0    2    1    1    1

就个人而言,匿名函数的方式似乎更自然,但我想如果你有太多的参数,它可能会变得有点难看。 mapply(function(...,z) rmultinom(...,c(0.6,0.3,0.1)),3:5,10:12)mapply(function(...) rmultinom(...,c(0.6,0.3,0.1)),3:5,10:12) 也可以,但我不知道它们是安全的还是惯用的。

I've yet to see a case where identical functionality cannot be gained by simply deleting the MoreArgs= argument and letting the relevant parts pass to ....

这是错误的,但只是轻微的错误。比较:

options(max.print = 50)#Before running this, make sure that you know how to undo it.

> mapply(sum,1:5,MoreArgs=list(runif(10),runif(10000)))
[1] 5019.831 5020.831 5021.831 5022.831 5023.831

> mapply(sum,1:5,list(runif(10)),list(runif(10000)))
[1] 5069.321 5070.321 5071.321 5072.321 5073.321

> mapply(sum,1:5,list(runif(10),runif(10000)))
[1]    6.658275 4984.177882    8.658275 4986.177882   10.658275

> mapply(sum,1:5,runif(10),runif(10000))
 [1] 1.750417 3.286090 3.186474 5.310268 5.962829 1.343564 2.325567 3.928796 4.955376
[10] 5.507385 1.992290 3.454536 3.399763 5.242883 5.589296 1.637056 2.964259 3.839006
[19] 5.647123 5.883139 1.863512 2.827110 3.633137 5.174900 5.365155 2.022725 3.139846
[28] 3.830624 5.064546 5.697612 1.242803 3.456888 3.726114 5.271773 5.881724 1.533730
[37] 2.489976 3.509690 5.657166 5.400823 1.972689 2.858276 3.571505 5.582752 5.482381
[46] 1.956237 2.497409 3.864434 5.389969 5.965341
 [ reached getOption("max.print") -- omitted 9950 entries ]
Warning message:
In mapply(sum, 1:5, list(runif(10), runif(10000))) :
  longer argument not a multiple of length of shorter

在第一种情况下,MoreArgs 参数中列表的每个元素都会在每次调用时被回收。同样,第二种情况是为每个调用回收 runif(10)runif(10000),给出我有信心称之为相同的行为。第四种情况的存在只是为了展示如果我们愚蠢到根本不使用任何列表我们会得到什么。

我上面引用的声明是第一个和第三个案例应该是相同的。显然不是这样。如果我们尝试在没有 MoreArgs 的情况下使用一个列表(而不是像我们的第二种情况那样使用两个),R 的法线向量回收规则将让我们为第一个、第三个和第五个重用 runif(10) 的值调用,并为第二个和第四个使用 runif(10000),并由于这种奇怪的行为给我们一个警告。

总而言之,MoreArgs 参数似乎总是可以被 R 的向量回收规则替换(尽管我的 ),但不是我在问题中所说的确切方式.事实似乎是 MoreArgs=list(foo,bar,etc) 等同于使用 list(foo)list(bar)list(etc) 作为 mapply... 个参数。请注意,这与使用 list(foo,bar,etc) 作为 ... 参数不同。因此,最终:您不会总是通过省略 MoreArgs 参数获得相同的功能。

作为最后一个小细节:省略 MoreArgs 参数是无害的,但省略 ... 参数并使用 MoreArgs 代替 ,通常是一个空列表。