给定参考字符串中的坐标,从长参考字符串中拼接字符串
Splicing strings out of a long reference string given their coordinates in that reference string
虽然这是一个 genomics
问题,但由于它处理的是字符串的拼接(获取子集),我认为它与此听众相关,而不是仅与 Bioconductor
相关。
很简单,我有一个长字符串列表(基因组的染色体)。例如,我使用 Bioconductor
Biostrings
包创建并存储 10 条染色体:
set.seed(1)
set <- NULL
for (i in 1:10) set <- c(set,paste(sample(Biostrings::DNA_ALPHABET[1:4],10000,replace=T),collapse=""))
genome.set <- Biostrings::DNAStringSet(set)
names(genome.set) <- paste0("chr",1:10)
然后我有一个 data.frame
的抄本坐标(来自 GTF
文件),其中每个抄本可以有多行:
library(dplyr)
gtf.df <- data.frame(seqnames = sample(names(genome.set),100,replace=T),
strand = sample(c("+","-"),100,replace=T),
start = sample(1:9000,100,replace=F)) %>%
dplyr::mutate(end = start+sample(1:1000,100,replace = F))
gtf.df <- gtf.df %>% dplyr::group_by(seqnames) %>%
dplyr::arrange(start,end) %>%
dplyr::mutate(transcript_id = paste0(seqnames,"_",sample(1:8,length(seqnames),replace=T))) %>%
dplyr::ungroup()
我想要做的是通过从 genome.set
.
中拼接出每个转录本来加入其序列
再次使用 Biostrings
我是这样实现的:
transcript_ids <- unique(gtf.df$transcript_id)
transcript.seqs <- sapply(1:length(transcript_ids),function(t){
transcript.gtf.df <- gtf.df %>% dplyr::filter(gtf.df$transcript_id == transcript_ids[t])
transcript.seq <- paste(sapply(1:nrow(transcript.gtf.df),function(e)
unname(as.character(Biostrings::subseq(genome.set[which(names(genome.set) == transcript.gtf.df$seqnames[1])],start=transcript.gtf.df$start[e],end=transcript.gtf.df$end[e])))
),collapse="")
if(transcript.gtf.df$strand[1] == "-") transcript.seq <- unname(as.character(Biostrings::reverseComplement(Biostrings::DNAString(transcript.seq))))
return(transcript.seq)
})
我的问题是我的真实数据中有 4520919
个成绩单,最后一部分需要很长时间。所以我的问题是是否以及如何更快地完成此操作,使用 Biostrings
或任何其他方式。
我重写了你的 sapply
方法,有两个主要变化:
- 首先,我使用了
vapply
,一般来说速度更快
- 其次,我使用了很多
.subset2
来对数据帧进行子集化
编辑
- 我已经成功摆脱了内循环(
vapply
)
- 替换函数
Biostrings::reverseComplement
这是代码
names_genome.set <- names(genome.set)
transcript_ids <- unique(gtf.df$transcript_id)
transcript_seqs <- vapply(seq_along(transcript_ids), function (t) {
ind_id <- which(.subset2(gtf.df, 5L) == transcript_ids[t])
x <- unname(as.character(genome.set[names_genome.set == .subset2(gtf.df, 1L)[ind_id[1L]]]))
out <- paste0(substring(text = x, first = .subset2(gtf.df, 3L)[ind_id], last = .subset2(gtf.df, 4L)[ind_id]), collapse = '')
if (.subset2(gtf.df, 2L)[ind_id[1L]] == '-') {
out <- unlist(strsplit(out, ''))
ind_A <- out == 'A'
ind_T <- out == 'T'
ind_C <- out == 'C'
ind_G <- out == 'G'
out[ind_A] <- 'C'
out[ind_T] <- 'G'
out[ind_G] <- 'T'
out[ind_C] <- 'A'
out <- paste(out, collapse = '')
}
out
}, character(1))
这里有一些基准数据和提供的样本数据
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# sapply 160.94296 169.97698 180.13836 175.20474 182.58224 400.3273 100 c
# vapply_old 66.20113 69.59185 72.96804 71.45861 74.56051 120.0023 100 b
# vapply_new 47.45224 49.51573 52.87001 50.97023 54.52104 109.3320 100 a
microbenchmark::microbenchmark(
'sapply' = {
transcript.seqs <- sapply(1:length(transcript_ids),function(t){
transcript.gtf.df <- gtf.df %>% dplyr::filter(gtf.df$transcript_id == transcript_ids[t])
transcript.seq <- paste(sapply(1:nrow(transcript.gtf.df),function(e)
unname(as.character(Biostrings::subseq(genome.set[which(names(genome.set) == transcript.gtf.df$seqnames[1])],start=transcript.gtf.df$start[e],end=transcript.gtf.df$end[e])))
),collapse="")
if(transcript.gtf.df$strand[1] == "-") transcript.seq <- unname(as.character(Biostrings::reverseComplement(Biostrings::DNAString(transcript.seq))))
return(transcript.seq)
})
},
'vapply_old' = {
transcript_seqs <- vapply(seq_along(transcript_ids), function (t) {
ind_id <- which(.subset2(gtf.df, 5L) == transcript_ids[t])
x <- unname(as.character(genome.set[names_genome.set == .subset2(gtf.df, 1L)[ind_id[1L]]]))
out <- vapply(ind_id,
function (e) substr(x = x, start = .subset2(gtf.df, 3L)[e], stop = .subset2(gtf.df, 4L)[e]),
character(1))
out <- paste(out, collapse = '')
if (.subset2(gtf.df, 2L)[ind_id[1L]] == '-') {
out <- unname(as.character(Biostrings::reverseComplement(Biostrings::DNAString(out))))
}
out
}, character(1))
},
'vapply_new' = {
transcript_seqs <- vapply(seq_along(transcript_ids), function (t) {
ind_id <- which(.subset2(gtf.df, 5L) == transcript_ids[t])
x <- unname(as.character(genome.set[names_genome.set == .subset2(gtf.df, 1L)[ind_id[1L]]]))
out <- paste0(substring(text = x, first = .subset2(gtf.df, 3L)[ind_id], last = .subset2(gtf.df, 4L)[ind_id]), collapse = '')
if (.subset2(gtf.df, 2L)[ind_id[1L]] == '-') {
out <- unlist(strsplit(out, ''))
ind_A <- out == 'A'
ind_T <- out == 'T'
ind_C <- out == 'C'
ind_G <- out == 'G'
out[ind_A] <- 'C'
out[ind_T] <- 'G'
out[ind_G] <- 'T'
out[ind_C] <- 'A'
out <- paste(out, collapse = '')
}
out
}, character(1))
}
)
我将尝试找到增强它的方法(可能有矢量化)。 例如,我还没有掌握函数 reverseComplement
的作用 - 也许可以更有效地执行它。
您可以尝试使用更大的数据集,看看是否有改进。此外,如果效率真的受到威胁,Rcpp
可能是一个想法。
虽然这是一个 genomics
问题,但由于它处理的是字符串的拼接(获取子集),我认为它与此听众相关,而不是仅与 Bioconductor
相关。
很简单,我有一个长字符串列表(基因组的染色体)。例如,我使用 Bioconductor
Biostrings
包创建并存储 10 条染色体:
set.seed(1)
set <- NULL
for (i in 1:10) set <- c(set,paste(sample(Biostrings::DNA_ALPHABET[1:4],10000,replace=T),collapse=""))
genome.set <- Biostrings::DNAStringSet(set)
names(genome.set) <- paste0("chr",1:10)
然后我有一个 data.frame
的抄本坐标(来自 GTF
文件),其中每个抄本可以有多行:
library(dplyr)
gtf.df <- data.frame(seqnames = sample(names(genome.set),100,replace=T),
strand = sample(c("+","-"),100,replace=T),
start = sample(1:9000,100,replace=F)) %>%
dplyr::mutate(end = start+sample(1:1000,100,replace = F))
gtf.df <- gtf.df %>% dplyr::group_by(seqnames) %>%
dplyr::arrange(start,end) %>%
dplyr::mutate(transcript_id = paste0(seqnames,"_",sample(1:8,length(seqnames),replace=T))) %>%
dplyr::ungroup()
我想要做的是通过从 genome.set
.
再次使用 Biostrings
我是这样实现的:
transcript_ids <- unique(gtf.df$transcript_id)
transcript.seqs <- sapply(1:length(transcript_ids),function(t){
transcript.gtf.df <- gtf.df %>% dplyr::filter(gtf.df$transcript_id == transcript_ids[t])
transcript.seq <- paste(sapply(1:nrow(transcript.gtf.df),function(e)
unname(as.character(Biostrings::subseq(genome.set[which(names(genome.set) == transcript.gtf.df$seqnames[1])],start=transcript.gtf.df$start[e],end=transcript.gtf.df$end[e])))
),collapse="")
if(transcript.gtf.df$strand[1] == "-") transcript.seq <- unname(as.character(Biostrings::reverseComplement(Biostrings::DNAString(transcript.seq))))
return(transcript.seq)
})
我的问题是我的真实数据中有 4520919
个成绩单,最后一部分需要很长时间。所以我的问题是是否以及如何更快地完成此操作,使用 Biostrings
或任何其他方式。
我重写了你的 sapply
方法,有两个主要变化:
- 首先,我使用了
vapply
,一般来说速度更快 - 其次,我使用了很多
.subset2
来对数据帧进行子集化
编辑
- 我已经成功摆脱了内循环(
vapply
) - 替换函数
Biostrings::reverseComplement
这是代码
names_genome.set <- names(genome.set)
transcript_ids <- unique(gtf.df$transcript_id)
transcript_seqs <- vapply(seq_along(transcript_ids), function (t) {
ind_id <- which(.subset2(gtf.df, 5L) == transcript_ids[t])
x <- unname(as.character(genome.set[names_genome.set == .subset2(gtf.df, 1L)[ind_id[1L]]]))
out <- paste0(substring(text = x, first = .subset2(gtf.df, 3L)[ind_id], last = .subset2(gtf.df, 4L)[ind_id]), collapse = '')
if (.subset2(gtf.df, 2L)[ind_id[1L]] == '-') {
out <- unlist(strsplit(out, ''))
ind_A <- out == 'A'
ind_T <- out == 'T'
ind_C <- out == 'C'
ind_G <- out == 'G'
out[ind_A] <- 'C'
out[ind_T] <- 'G'
out[ind_G] <- 'T'
out[ind_C] <- 'A'
out <- paste(out, collapse = '')
}
out
}, character(1))
这里有一些基准数据和提供的样本数据
# Unit: milliseconds
# expr min lq mean median uq max neval cld
# sapply 160.94296 169.97698 180.13836 175.20474 182.58224 400.3273 100 c
# vapply_old 66.20113 69.59185 72.96804 71.45861 74.56051 120.0023 100 b
# vapply_new 47.45224 49.51573 52.87001 50.97023 54.52104 109.3320 100 a
microbenchmark::microbenchmark(
'sapply' = {
transcript.seqs <- sapply(1:length(transcript_ids),function(t){
transcript.gtf.df <- gtf.df %>% dplyr::filter(gtf.df$transcript_id == transcript_ids[t])
transcript.seq <- paste(sapply(1:nrow(transcript.gtf.df),function(e)
unname(as.character(Biostrings::subseq(genome.set[which(names(genome.set) == transcript.gtf.df$seqnames[1])],start=transcript.gtf.df$start[e],end=transcript.gtf.df$end[e])))
),collapse="")
if(transcript.gtf.df$strand[1] == "-") transcript.seq <- unname(as.character(Biostrings::reverseComplement(Biostrings::DNAString(transcript.seq))))
return(transcript.seq)
})
},
'vapply_old' = {
transcript_seqs <- vapply(seq_along(transcript_ids), function (t) {
ind_id <- which(.subset2(gtf.df, 5L) == transcript_ids[t])
x <- unname(as.character(genome.set[names_genome.set == .subset2(gtf.df, 1L)[ind_id[1L]]]))
out <- vapply(ind_id,
function (e) substr(x = x, start = .subset2(gtf.df, 3L)[e], stop = .subset2(gtf.df, 4L)[e]),
character(1))
out <- paste(out, collapse = '')
if (.subset2(gtf.df, 2L)[ind_id[1L]] == '-') {
out <- unname(as.character(Biostrings::reverseComplement(Biostrings::DNAString(out))))
}
out
}, character(1))
},
'vapply_new' = {
transcript_seqs <- vapply(seq_along(transcript_ids), function (t) {
ind_id <- which(.subset2(gtf.df, 5L) == transcript_ids[t])
x <- unname(as.character(genome.set[names_genome.set == .subset2(gtf.df, 1L)[ind_id[1L]]]))
out <- paste0(substring(text = x, first = .subset2(gtf.df, 3L)[ind_id], last = .subset2(gtf.df, 4L)[ind_id]), collapse = '')
if (.subset2(gtf.df, 2L)[ind_id[1L]] == '-') {
out <- unlist(strsplit(out, ''))
ind_A <- out == 'A'
ind_T <- out == 'T'
ind_C <- out == 'C'
ind_G <- out == 'G'
out[ind_A] <- 'C'
out[ind_T] <- 'G'
out[ind_G] <- 'T'
out[ind_C] <- 'A'
out <- paste(out, collapse = '')
}
out
}, character(1))
}
)
我将尝试找到增强它的方法(可能有矢量化)。 例如,我还没有掌握函数 reverseComplement
的作用 - 也许可以更有效地执行它。
您可以尝试使用更大的数据集,看看是否有改进。此外,如果效率真的受到威胁,Rcpp
可能是一个想法。