作为最佳实践的问题,我试图确定apply()在矩阵中创建函数是否更好,或者如果通过函数简单地循环矩阵更好.我尝试了两种方式,并惊讶地发现apply()速度较慢.任务是取一个向量并将其评估为正数或负数,然后如果为正数则返回1,如果为负则返回-1.该mash()函数循环和squish()功能传递给apply()函数.
million <- as.matrix(rnorm(100000))
mash <- function(x){
for(i in 1:NROW(x))
if(x[i] > 0) {
x[i] <- 1
} else {
x[i] <- -1
}
return(x)
}
squish <- function(x){
if(x >0) {
return(1)
} else {
return(-1)
}
}
ptm <- proc.time()
loop_million <- mash(million)
proc.time() - ptm
ptm <- proc.time()
apply_million <- apply(million,1, squish)
proc.time() - ptm
Run Code Online (Sandbox Code Playgroud)
loop_million 结果:
user system elapsed
0.468 0.008 0.483
Run Code Online (Sandbox Code Playgroud)
apply_million 结果:
user system elapsed
1.401 0.021 1.423
Run Code Online (Sandbox Code Playgroud)
什么是优势,使用apply()过for,如果性能下降的循环?我的测试中有缺陷吗?我比较了两个结果对象的线索,发现:
> class(apply_million)
[1] "numeric"
> class(loop_million)
[1] "matrix"
Run Code Online (Sandbox Code Playgroud)
这只会加深神秘感.该apply()函数不能接受一个简单的数字向量,这就是我as.matrix()在开始时使用它的原因.但随后它返回一个数字.该for循环是精细用一个简单的数字矢量.它返回一个与传递给它的类相同的对象.
had*_*ley 39
apply(和plyr)函数族的要点不是速度,而是表达性.它们还倾向于防止错误,因为它们消除了循环所需的簿记代码.
最近,stackoverflow的答案过分强调了速度.随着计算机变得更快并且R-core优化R的内部,您的代码将变得更快.您的代码将永远不会变得更优雅或更容易理解.
在这种情况下,你可以充分利用两个世界:使用矢量化的优雅答案,也非常快(million > 0) * 2 - 1.
Jor*_*eys 12
正如Chase所说:利用矢量化的力量.你在这里比较两个不好的解决方案.
澄清为什么您的应用解决方案较慢:
在for循环中,实际上使用了矩阵的向量化索引,这意味着没有类型转换.我在这里稍微粗略一点,但基本上内部计算类型忽略了维度.它们只是作为属性保存,并返回表示矩阵的向量.为了显示 :
> x <- 1:10
> attr(x,"dim") <- c(5,2)
> y <- matrix(1:10,ncol=2)
> all.equal(x,y)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)
现在,当您使用apply时,矩阵在内部以100,000个行向量分割,每个行向量(即单个数字)都通过该函数,最后将结果合并为适当的形式.apply函数在这种情况下估计向量是最好的,因此必须连接所有行的结果.这需要时间.
另外,sapply函数首先用于as.vector(unlist(...))将任何东西转换为向量,并最终尝试将答案简化为合适的形式.这也需要时间,因此这里的速度也可能较慢.然而,它不在我的机器上.
如果申请在这里是一个解决方案(它不是),你可以比较:
> system.time(loop_million <- mash(million))
user system elapsed
0.75 0.00 0.75
> system.time(sapply_million <- matrix(unlist(sapply(million,squish,simplify=F))))
user system elapsed
0.25 0.00 0.25
> system.time(sapply2_million <- matrix(sapply(million,squish)))
user system elapsed
0.34 0.00 0.34
> all.equal(loop_million,sapply_million)
[1] TRUE
> all.equal(loop_million,sapply2_million)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)
如果需要,可以使用lapply或sapply在矢量上.但是,在这种情况下,为什么不使用适当的工具ifelse()呢?
> ptm <- proc.time()
> ifelse_million <- ifelse(million > 0,1,-1)
> proc.time() - ptm
user system elapsed
0.077 0.007 0.093
> all.equal(ifelse_million, loop_million)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)
为了比较,这里是使用for循环和sapply的两个可比较的运行:
> ptm <- proc.time()
> apply_million <- sapply(million, squish)
> proc.time() - ptm
user system elapsed
0.469 0.004 0.474
> ptm <- proc.time()
> loop_million <- mash(million)
> proc.time() - ptm
user system elapsed
0.408 0.001 0.417
Run Code Online (Sandbox Code Playgroud)
ifelse()在这种情况下,进行基于索引的替换比、*apply()族或循环要快得多:
> million <- million2 <- as.matrix(rnorm(100000))
> system.time(million3 <- ifelse(million > 0, 1, -1))
user system elapsed
0.046 0.000 0.044
> system.time({million2[(want <- million2 > 0)] <- 1; million2[!want] <- -1})
user system elapsed
0.006 0.000 0.007
> all.equal(million2, million3)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)
所有这些工具都触手可及,非常值得。您可以使用对您最有意义的解决方案(因为您需要在数月或数年后理解代码),然后在计算时间变得令人望而却步时开始转向更优化的解决方案。