我正在寻找一种更快的方法来计算从FASTA文件读入的DNA字符串的GC内容。这归结为取一个字符串并计算字母'G'或'C'出现的次数。我还想指定要考虑的字符范围。
我有一个相当慢的工作函数,它导致我的代码瓶颈。它看起来像这样:
##
## count the number of GCs in the characters between start and stop
##
gcCount <- function(line, st, sp){
chars = strsplit(as.character(line),"")[[1]]
numGC = 0
for(j in st:sp){
##nested ifs faster than an OR (|) construction
if(chars[[j]] == "g"){
numGC <- numGC + 1
}else if(chars[[j]] == "G"){
numGC <- numGC + 1
}else if(chars[[j]] == "c"){
numGC <- numGC + 1
}else if(chars[[j]] == "C"){
numGC <- numGC + 1
}
}
return(numGC)
}
运行Rprof会给我以下输出:
> a = "GCCCAAAATTTTCCGGatttaagcagacataaattcgagg"
> Rprof(filename="Rprof.out")
> for(i in 1:500000){gcCount(a,1,40)};
> Rprof(NULL)
> summaryRprof(filename="Rprof.out")
self.time self.pct total.time total.pct
"gcCount" 77.36 76.8 100.74 100.0
"==" 18.30 18.2 18.30 18.2
"strsplit" 3.58 3.6 3.64 3.6
"+" 1.14 1.1 1.14 1.1
":" 0.30 0.3 0.30 0.3
"as.logical" 0.04 0.0 0.04 0.0
"as.character" 0.02 0.0 0.02 0.0
$by.total
total.time total.pct self.time self.pct
"gcCount" 100.74 100.0 77.36 76.8
"==" 18.30 18.2 18.30 18.2
"strsplit" 3.64 3.6 3.58 3.6
"+" 1.14 1.1 1.14 1.1
":" 0.30 0.3 0.30 0.3
"as.logical" 0.04 0.0 0.04 0.0
"as.character" 0.02 0.0 0.02 0.0
$sampling.time
[1] 100.74
有关使此代码更快的建议吗?
答案 0 :(得分:14)
最好不要分开,只计算比赛:
gcCount2 <- function(line, st, sp){
sum(gregexpr('[GCgc]', substr(line, st, sp))[[1]] > 0)
}
这快了一个数量级。
只是迭代字符的小C函数将会快一个数量级。
答案 1 :(得分:6)
一个班轮:
table(strsplit(toupper(a), '')[[1]])
答案 2 :(得分:4)
我不知道它更快,但你可能想看一下R包seqinR - http://pbil.univ-lyon1.fr/software/seqinr/home.php?lang=eng。它是一种优秀的通用生物信息学包,具有许多序列分析方法。它在CRAN中(在我写这篇文章的时候看起来很糟糕。)
GC内容将是:
mysequence <- s2c("agtctggggggccccttttaagtagatagatagctagtcgta")
GC(mysequence) # 0.4761905
这是一个字符串,你也可以使用“read.fasta()”读取一个fasta文件。
答案 3 :(得分:3)
这里不需要使用循环。
试试这个:
gcCount <- function(line, st, sp){
chars = strsplit(as.character(line),"")[[1]][st:sp]
length(which(tolower(chars) == "g" | tolower(chars) == "c"))
}
答案 4 :(得分:3)
从stringi
包
> stri_count_fixed("GCCCAAAATTTTCCGG",c("G","C"))
[1] 3 5
或者您可以使用正则表达式来计算g和G
> stri_count_regex("GCCCAAAATTTTCCGGggcc",c("G|g|C|c"))
[1] 12
或者您可以先使用tolower函数然后再使用stri_count
> stri_trans_tolower("GCCCAAAATTTTCCGGggcc")
[1] "gcccaaaattttccggggcc"
时间表现
> microbenchmark(gcCount(x,1,40),gcCount2(x,1,40), stri_count_regex(x,c("[GgCc]")))
Unit: microseconds
expr min lq median uq max neval
gcCount(x, 1, 40) 109.568 112.42 113.771 116.473 146.492 100
gcCount2(x, 1, 40) 15.010 16.51 18.312 19.213 40.826 100
stri_count_regex(x, c("[GgCc]")) 15.610 16.51 18.912 20.112 61.239 100
更长字符串的另一个例子。 stri_dup复制字符串n次
> stri_dup("abc",3)
[1] "abcabcabc"
如您所见,对于更长的序列,stri_count更快:)
> y <- stri_dup("GCCCAAAATTTTCCGGatttaagcagacataaattcgagg",100)
> microbenchmark(gcCount(y,1,40*100),gcCount2(y,1,40*100), stri_count_regex(y,c("[GgCc]")))
Unit: microseconds
expr min lq median uq max neval
gcCount(y, 1, 40 * 100) 10367.880 10597.5235 10744.4655 11655.685 12523.828 100
gcCount2(y, 1, 40 * 100) 360.225 369.5315 383.6400 399.100 438.274 100
stri_count_regex(y, c("[GgCc]")) 131.483 137.9370 151.8955 176.511 221.839 100
答案 5 :(得分:0)
感谢所有这篇文章,
为了优化我想要计算200bp的100M序列的GC内容的脚本,我最终测试了这里提出的不同方法。 Ken Williams的方法表现最佳(2.5小时),优于seqinr(3.6小时)。使用stringr str_count减少到1.5小时。
最后我用C ++编写它并使用Rcpp调用它,它将计算时间减少到10分钟!
这是C ++代码:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
float pGC_cpp(std::string s) {
int count = 0;
for (int i = 0; i < s.size(); i++)
if (s[i] == 'G') count++;
else if (s[i] == 'C') count++;
float pGC = (float)count / s.size();
pGC = pGC * 100;
return pGC;
}
我打电话给R打字:
sourceCpp("pGC_cpp.cpp")
pGC_cpp("ATGCCC")