说到R,人们除了赞美它漂亮简洁的作图功能外,就是抱怨它的运行速度问题。作为常规使用,比如数据整理分析,统计计算或者作图等,或许没有感觉到它的运行速度有问题。但是如果数据量很大,像基因组数据,那么运行起来就尤其慢了。特别是对于循环语句,犹如老牛拉破车,所以在数据量很大的情况下,尽量不要在R中使用循环语句。
那么循环语句在R中会慢到什么地步?我们通过R语言和C语言的比较就能看出一些端倪。
我们假设有这么两个数据框,如下图。我们要判断每一个pos中的数据(4,15,19,27)是否落在第一个数据框的数据区间内(2-5,7-11,17-20,23-28,32-39)。这样我们就会用到两个嵌套for循环,对第二个数据框中的每一个数,依次比对第一个数据框中的每一个数据段。
首先我们使用随机数生成这么两个模拟数据框
set.seed(111)
creat_test <- function(len, seg){
x <- sample(1:len,2*seg, replace = F)
x <- x[order(x)]
start = numeric()
end = numeric()
for (i in seq(1,length(x),2)){
start <- c(start,x[i])
end <- c(end,x[i+1])
}
test_df <- data.frame("start" = start, "end" = end)
return(test_df)
}
# create first data frame, using length = 7,000,000, segments = 20,000
test_data <- creat_test(7000000, 200000) #object.size(test_data) -- 3.2Mb
# create second data frame, sequence data
sequence <- data.frame('x' = sample(1:7000000, 400, replace = F))
sequence$x <- sequence[order(sequence$x),]
自此,我们有了两个测试数据框,第一个test_data里面有20万条观测,第二个sequence里面有400个观测,那么我们首先通过R中的for循环来判断这400个观测时候落在来第一个数据框中数据段内。
# using R loop to see if sequence in test_data range
R_time <- function(sequence, test_data){
num_in <- 0
for (i in 1:nrow(sequence)){
for (j in 1:nrow(test_data)){
if (sequence[i,1] > test_data[j,1] & sequence[i,1] < test_data[j,2]){
num_in <- num_in + 1
break
}
}
}
print(num_in)
}
system.time(R_time(sequence,test_data))
这段代码中两个for循环,大概运行来:
下面我们看一看在C语言中完成这段代码需要多长时间。本人并不了解C语言,但是Rcpp包提供了在R中写C代码的可能,所以使用Rcpp完成上述功能如下:
# using Rcpp
library(Rcpp)
cppFunction('
int Rcpp_time(DataFrame sequence, DataFrame test_data){
IntegerVector start=test_data["start"];
IntegerVector end=test_data["end"];
IntegerVector pos=sequence["x"];
int seq_size=sequence.nrow();
int test_size=test_data.nrow();
int num_in=0;
for(int i=0; i<=seq_size; i++){
for(int j=0; j<=test_size; j++){
if(pos[i]>start[j] && pos[i]<end[j]){
num_in++;
break;
}
}
}
return num_in;
}
')
system.time(Rcpp_time(sequence,test_data))
而这段代码的运行时间仅仅为:
可以看出,同样的一段代码,得到相同的结果,在R中运行的时间是在C中运行时间的数万倍!什么概念:
在R中你要等1个多小时,在C中你在1秒内就完成!
当然这也很好理解,毕竟相对于C来讲,R属于高级编程语言,而且是解释型语言,没有C的编译过程,所以R会针对每一个for循环一一执行,这也降低了速度。
那么问题来了,应该怎样提高R的运行速度呢?
1、避免使用for循环,尤其是数据量特别大的时候;
2、避免数据的复制,使用%>%之类的管道操作;
3、使用一些更快的R包处理数据,比如data.table;
4、使用apply、lapply等之类的函数代替for;
5、使用Rcpp编写C语言函数;
6、使用多核并行计算(可以通过lapply来实现);
可以参考http://adv-r.had.co.nz/Performance.html