8

可能的重复:
R 的应用系列比语法糖更多吗

就像标题所说的那样。也许是愚蠢的问题,但我的理解是,当使用“应用”函数时,迭代是在编译代码中执行的,而不是在 R 解析器中。例如,这似乎意味着 lapply 仅比“for”循环快,如果有大量迭代并且每个操作都相对简单。例如,如果对包含在 lapply 中的函数的一次调用需要 10 秒,并且只有 12 次迭代,我想使用“for”和“lapply”之间几乎没有区别。

现在我想到了,如果无论如何都必须解析“lapply”中的函数,为什么使用“lapply”而不是“for”会有任何性能优势,除非你正在做一些有编译函数的事情(比如求和或乘法等)?

提前致谢!

乔什

4

2 回答 2

14

有几个原因可能会导致人们更喜欢apply家庭函数而不是for循环,反之亦然。

首先 ,for()apply(),sapply()如果正确执行,通常会彼此一样快。lapply()它在 R 内部的编译代码中比其他函数更多地运行,因此可以比那些函数更快。当“循环”数据的行为是计算时间的重要部分时,速度优势似乎最大;在许多一般的日常使用中,您不太可能从固有更快的lapply(). 最后,这些都将调用 R 函数,因此它们需要被解释然后运行。

for()循环通常更容易实现,特别是如果您来自循环普遍存在的编程背景。在循环中工作可能比将迭代计算强制到一个apply族函数中更自然。但是,要for()正确使用循环,您需要做一些额外的工作来设置存储并管理将循环的输出重新组合在一起。这些apply功能会自动为您执行此操作。例如:

IN <- runif(10)
OUT <- logical(length = length(IN))
for(i in IN) {
    OUT[i] <- IN > 0.5
}

这是一个愚蠢的例子,>矢量化运算符也是如此,但我想说明一点,即你必须管理输出。最主要的是,对于for()循环,您总是在开始循环之前分配足够的存储空间来保存输出。如果您不知道需要多少存储空间,则分配一个合理的存储空间,然后在循环中检查您是否已用完该存储空间,然后再使用另一大存储空间。

apply在我看来,使用其中一个函数家族的主要原因是为了更优雅、更易读的代码。与其管理输出存储和设置循环(如上所示),我们可以让 R 处理它并简洁地要求 R 对我们的数据子集运行一个函数。速度通常不会进入决定,至少对我来说是这样。我使用最适合情况的函数,并且会生成简单、易于理解的代码,因为如果我不记得代码是什么,我总是选择最快的函数比我节省的时间要多得多一天或一周或更长时间后做!

apply族适合于标量或矢量运算。for()循环通常适合使用相同的索引进行多次迭代操作i。例如,我编写了使用for()循环对对象进行k折叠或引导交叉验证的代码。我可能永远不会对apply家族中的一个人这样做,因为每次 CV 迭代都需要多个操作,访问当前帧中的许多对象,并填充几个保存迭代输出的输出对象。

至于最后一点,关于为什么lapply()可能比for()or更快apply(),您需要意识到“循环”可以在解释的 R 代码或编译代码中执行。是的,两者仍将调用需要解释的 R 函数,但如果您正在执行循环并直接从编译的 C 代码(例如lapply())调用,那么这就是性能提升的来源,apply()而归结为for()循环在实际的 R 代码中。查看源代码apply()以查看它是for()循环的包装器,然后查看代码 for lapply(),即:

> lapply
function (X, FUN, ...) 
{
    FUN <- match.fun(FUN)
    if (!is.vector(X) || is.object(X)) 
        X <- as.list(X)
    .Internal(lapply(X, FUN))
}
<environment: namespace:base>

lapply()你应该明白为什么和for()其他apply系列函数之间的速度会有所不同。这.Internal()是 R 调用 R 本身使用的已编译 C 代码的方法之一。除了对 的操作和完整性检查之外FUN,整个计算都是在 C 中完成的,调用 R 函数FUN。将其与apply().

于 2011-06-23T22:35:50.927 回答
3

来自 Burns 的R Inferno (pdf),第 25 页:

for当每次迭代都是不平凡的任务时,使用显式循环。但是使用函数可以更清晰、更紧凑地表达一个简单的循环apply 。这条规则至少有一个例外......如果结果将是一个列表并且某些组件可以是NULL,那么 for 循环就很麻烦(大麻烦)并lapply给出了预期的答案。

于 2011-06-24T10:16:58.223 回答