假设我有一个这样的字符串:
> x <- c("16^TG40")
我正在尝试将结果c(16 2 40)
设为2
length(^TG)-1
。我能够找到这种模式,例如:
> gsub("(\\^[ACGT]+)", " \\1 ", x)
[1] "16 ^TG 40"
但是,我无法直接用length-1
替换此字符串。是否有更简单的方法用长度替换匹配的模式?
经过相当多的搜索(这里是SO和谷歌搜索),我最终得到了stringr
包,我觉得这很棒。但是,这一切都归结为找到这个模式的位置(使用str_locate_all
),然后用你想要的任何值替换子字符串(使用str_sub
)。我有超过100,000个字符串,这非常耗时(因为模式也可能在字符串中多次出现)。
我现在并行运行以弥补缓慢,但我很高兴知道这是否可能直接(或快速)。
有什么想法吗?
答案 0 :(得分:8)
(1)gsubfn gsubfn
语句将^ ...部分替换为空格包围的长度,strapply
从该字符串中抽出数字并转换他们是数字的。如果字符输出足够,则省略strapply
。
> library(gsubfn)
> xx <- gsubfn("\\^[ACGT]*", ~ sprintf(" %s ", nchar(x) - 1), x)
> strapply(xx, "\\d+", as.numeric)
[[1]]
[1] 16 2 40
(2)循环通过一组长度
这假设每个ACGT序列中的字符数在mn和mx之间,它只是用循环中的gsub继续替换i长度的ACGT序列。如果只有几个可能的长度,那么只会有几次迭代,所以它会很快但是如果字符串可能有很多不同的长度,那么它将会很慢,因为需要更多的迭代迭代。下面我们假设ACGT序列长2,4或6,但这些可能需要调整。该解决方案的可能缺点是需要假设一组可能的序列长度。
x <- "4^CG5^CAGT656"
mn <- 2
mx <- 6
y <- x
for(i in seq(mn, mx, 2)) {
pat <- sprintf("\\^[ACGT]{%d}(\\d)", i)
replacement <- sprintf(" %d \\1", i)
y <- gsub(pat, replacement, y)
}
(3)循环ACGT序列
这个循环通过ACGT序列替换其长度直到没有留下。如果存在少量的ACGT序列,则它可以是快速的,因为将发生很少的迭代但是如果可能存在许多ACGT序列,则由于较大的迭代次数将会很慢。
x <- "4^CG5^CAGT656"
y <- x
while(regexpr("^", y, fixed = TRUE) > 0) {
y <- sprintf("%s %d %s", sub("\\^.*", "", y),
nchar(sub("^[0-9 ]+\\^([ACGT]+).*", "\\1", y)),
sub("^[0-9 ]+\\^[ACGT]+", "", y))
}
<强>基准强>
这是一个基准。请注意,在某些解决方案中,我将字符串转换为数字(当然需要额外的时间),但为了使基准测试可比,我比较了创建字符串的速度而没有任何数字转换。
x <- "4^CGT5^CCA656"
library(rbenchmark)
benchmark(order = "relative", replications = 10000,
columns = c("test", "replications", "relative", "elapsed"),
regmatch = {
pat <- "(\\^[ACGT]+)"
x2 <- x
m <- gregexpr(pat, x2)
regmatches(x2, m) <- sapply(regmatches(x2, m), modFun)
x2
},
gsubfn = gsubfn("\\^[ACGT]*", ~ sprintf(" %s ", length(x) - 1), x),
loop.on.len = {
mn <- 2
mx <- 6
y <- x
for(i in seq(mn, mx, 2)) {
pat <- sprintf("\\^[ACGT]{%d}(\\d)", i)
replacement <- sprintf(" %d \\1", i)
y <- gsub(pat, replacement, y)
}
},
loop.on.seq = {
y <- x
while(regexpr("^", y, fixed = TRUE) > 0) {
y <- sprintf("%s %d %s", sub("\\^.*", "", y),
nchar(sub("^[0-9 ]+\\^([ACGT]+).*", "\\1", y)),
sub("^[0-9 ]+\\^[ACGT]+", "", y))
}
}
)
结果如下所示。两个循环解决方案在所示输入上是最快的,但它们的性能将根据需要多少次迭代而变化,因此实际数据可能会有所不同。 loop.on.len解决方案的缺点是ACGT长度必须在假定集合中。来自Josh的regmatch解决方案不涉及循环而且速度很快。 gsubfn解决方案的优势在于它只有一行代码并且特别直接。
test replications relative elapsed
4 loop.on.seq 10000 1.000 1.93
3 loop.on.len 10000 1.140 2.20
1 regmatch 10000 1.803 3.48
2 gsubfn 10000 7.145 13.79
更新已添加两个循环解决方案,并删除了之前部分帖子中不处理多个ACGT序列的解决方案(基于澄清问题的评论)。还重新做了基准测试,包括只处理多个ACGT序列的解决方案。
更新删除了一个无法使用多个^ ...序列的解决方案。它先前已从基准测试中删除,但代码尚未删除。改进了(1)中的解释。
答案 1 :(得分:8)
这是一个基础R方法。
语法远非直观,但通过密切关注此模板,您可以执行各种操作和替换匹配的子串。 (有关更复杂的示例,请参阅?gregexpr
。)
x2 <- x <- c("16^TG40", "16^TGCT40", "16^TG40^GATTACA40")
pat <- "(\\^[ACGT]+)" ## A pattern matching substrings of interest
modFun <- function(ss) { ## A function to modify them
paste0(" ", nchar(ss) - 1, " ")
}
## Use regmatches() <- regmatches(gregexpr()) to search, modify, and replace.
m <- gregexpr(pat, x2)
regmatches(x2, m) <- sapply(regmatches(x2, m), modFun)
x2
## [1] "16 2 40" "16 4 40" "16 2 40 7 40"
答案 2 :(得分:2)
我正在投票支持令人难以置信的gsubfn
答案,但因为我已经有了这个笨重的代码:
mod <- gsub("(\\^[ACGT]+)", " \\1 ", x)
locs <- gregexpr(" ", mod , fixed=TRUE)[[1]]
paste( substr( x, 1, locs[1]-1),
diff(locs)-2,
substr(mod, locs[2]+1, nchar(mod) ) , sep=" ")
#[1] "16 2 40"