R作为当今最为流行的统计分析软件之一,其处理数据一般都是默认单线程跑的。而现今各种云平台层出不穷,其主要目的就是加快分析速度从而节省分析成本,比如现在大型平台的全基因组分析的耗时都按分钟记了(当然是最快的那种),因此有时我们平时处理数据时,也可以优先考虑下将R并行化,在计算资源足够的情况下,肯定是越快越好 关于什么是并行计算以及R并行化可以看看这篇文章R 与并行计算
简单的说R的并行化计算其实没有改变其整个并行环境,而是先启动N个附属进程,然后将数据分割成N块在N个核上进行运行,等待附属进程结束返回结果
根据上述原理,在使用一些并行方法时有一点需要注意:
当你并行调用的自定义函数时,如果用到一些需要加载其他包才有的函数,则需要在自定义中先labrary下
下面以一个计算matrix
每行的标准差为例:
data <- matrix(runif(10^8), ncol = 10)
先看看for
循环和apply
等常规方法所用时间
system.time({
invisible(
for (i in 1:nrow(data)) {
sd(data[i,])
}
)
})
user system elapsed
256.451 0.082 256.628
system.time({
invisible(
apply(data, 1, sd)
)
})
user system elapsed
405.927 1.506 407.701
上面两者看起来有点差别(不知为啥。。),接着再试试向量化计算标准化
system.time({
invisible(
sqrt(rowSums((data - rowMeans(data))^2)/(dim(data)[2] - 1))
)
})
user system elapsed
1.479 0.408 1.888
从上看出这个例子如果将计算过程向量化后,提升的运算速度是非常明显的,所以有时向量化比apply等循环更加有效,比如参考文章提升R语言运算效率的11个实用方法
当必须用循环时,这时最好就尝试下用下面几种并行方法,如果计算资源充足的话
如果使用场景是apply
,lapply
或者sapply
,那么可以尝试下parallel包的parApply
,parLapply
和parSapply
,parallel包已经是R的默认安装包了;简单用法如下:detectCores
检查核数, makeCluster
设定核数,然后parApply
执行
library(parallel)
detectCores(logical = F)
cl <- makeCluster(getOption("cl.cores", 4))
system.time({
invisible(
parApply(cl, data, 1, sd)
)
})
stopCluster(cl)
user system elapsed
45.470 4.816 135.120
设定了4个核,parApply
并行速度比apply
提升了不少
接着再试试foreach包(个人觉得蛮好用的),其主要对for循环进行了并行,用法跟parallel包略有不同,如下:先加载下doParallel包,然后用registerDoParallel
登记下集群,然后用foreach
函数计算
注意:foreach
返回的是list,所用如果不用.combine
参数的话,需要用do.call
将所有结果整合
library(foreach)
library(doParallel)
cl <- makeCluster(4)
registerDoParallel(cl)
system.time({
invisible(
result_df <- foreach(i = 1:nrow(data), .combine = c) %dopar% sd(data[i,])
)
})
stopCluster(cl)
所用时间就不列了,反正是上万秒了,这里可以发现foreach并行比for循环还要慢,后来查了下,网上已经有人提出这个问题了,解释是:
foreach() is best when the number of jobs does not hugely exceed the number of processors you will be using. Or more generally, when each job takes a significant amount of time on its own (seconds or minutes, say). There is a lot of overhead in creating the threads, so you really don't want to use it for lots of small jobs.
https://stackoverflow.com/questions/5007458/problems-using-foreach-parallelization
所以我这个例子是循环1e6次,foreach就不太合适了;上述网页中有个回答给出的解决方案如下,先将循环变少,然后在用replicate()
在子循环中计算,但是这个只适合每次循环参数是一致的情况下,我这种情况似乎就不太适合了
但是呢,foreach包搭配doSNOW包可以在foreach
函数中用进度条,这点蛮好用的,参考https://stackoverflow.com/questions/5423760/how-do-you-create-a-progress-bar-when-using-the-foreach-function-in-r
library(doSNOW)
cl <- makeCluster(2)
registerDoSNOW(cl)
iterations <- 100
pb <- txtProgressBar(max = iterations, style = 3)
progress <- function(n) setTxtProgressBar(pb, n)
opts <- list(progress = progress)
result <- foreach(i = 1:iterations, .combine = rbind,
.options.snow = opts) %dopar%
{
s <- summary(rnorm(1e6))[3]
return(s)
}
close(pb)
stopCluster(cl)
iterators包作为一个迭代器可以跟foreach搭配使用,比如上述举例求data的每行标准差,用iter
函数:
foreach(d=iter(data, by = "row"), .combine = rbind) %dopar% sd(d)
从上可看出,有效的手段+加上并行计算可以明显的加快运算速度,或者再用Rcpp改写一些函数,那效果会更好,毕竟R属于高等语言,计算速度无法跟C这种比的
参考资料:
https://blog.csdn.net/sinat_26917383/article/details/52719232
https://www.cnblogs.com/litao1105/p/4605165.html
https://blog.csdn.net/sadfasdgaaaasdfa/article/details/45155103
https://blog.csdn.net/a358463121/article/details/51695054
本文出自于http://www.bioinfo-scrounger.com转载请注明出处