"应用"功能有哪些优点?它们什么时候比"for"循环更好用,什么时候不用?

Jos*_*osh 8 parsing for-loop r compilation lapply

可能重复:
R是否适用于家庭而不是语法糖

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

既然我想到了,如果必须解析"lapply"中的函数,为什么使用"lapply"而不是"for"会有任何性能上的好处,除非你正在做一些有编译函数的东西(如总结或乘法等)?

提前致谢!

玩笑

Rei*_*son 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
}
Run Code Online (Sandbox Code Playgroud)

这是一个愚蠢的例子,因为它>是一个矢量化运算符,但我想要点什么,即你必须管理输出.最重要的是,对于for()循环,您始终会在启动循环之前分配足够的存储空间来保存输出.如果您不知道需要多少存储空间,那么请分配一个合理的存储块,然后在循环中检查您是否已经耗尽了该存储空间,并锁定了另一大块存储空间.

在我看来,使用其中一个apply函数系列的主要原因是更优雅,可读的代码.我没有管理输出存储和设置循环(如上所示),我们可以让R处理它并简洁地要求R在我们数据的子集上运行一个函数.至少对我来说,速度通常不会做出决定.我使用最适合这种情况的功能,并且会产生简单易懂的代码,因为如果我不记得代码是什么,我总是选择最快的功能,因此我更有可能浪费更多的时间.做一天或一周或更长时间!

这个apply家族适合于标量或矢量运算.一for()环往往会借给自己使用相同的指标进行多次重复操作i.例如,我编写的代码使用for()循环对对象进行k -fold或bootstrap交叉验证.apply由于每个CV迭代需要多个操作,访问当前帧中的大量对象,并填充包含迭代输出的多个输出对象,因此我可能永远不会接受与其中一个系列相关的操作.

至于最后一点,关于为什么lapply()可能更快for()或者apply(),你需要意识到"循环"可以在解释的R代码或编译的代码中执行.是的,两者仍然会调用需要解释的R函数,但是如果你正在进行循环并直接从编译的C代码调用(例如lapply())那么性能增益可以来自于结果,apply()这可以归结为for()循环在实际的R代码中.查看源代码apply()以查看它是一个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>
Run Code Online (Sandbox Code Playgroud)

你应该明白为什么lapply()和/ for()和其他apply家庭功能之间的速度会有所不同.这.Internal()是R调用R本身使用的编译C代码的方法之一.除了操作和健全性检查之外FUN,整个计算在C中完成,调用R函数FUN.将其与源相比较apply().