避免在strsplit列表上的循环

时间:2013-05-07 19:47:29

标签: r

我有一个向量v,其中每个条目是由分号分隔的一个或多个字符串(或可能是字符(0)):

ABC

DEF;ABC;QWE

TRF

character(0)

ABC;GFD

我需要在“;”之后找到包含“ABC”(1,2,5或逻辑向量T,T,F,F,T)的向量索引

我目前正在使用循环如下:

toSelect=integer(0)
for(i in c(1:length(v))){
if(length(v[i])==0) next
words=strsplit(v[i],";")[[1]] 
if(!is.na(match("ABC",words))) toSelect=c(toSelect,i)
}

不幸的是,我的矢量有450k条目,所以这需要太长时间。我更喜欢通过执行类似

的操作来创建逻辑矢量
toSelect=(!is.na(match("ABC",strsplit(v,";")))

但是由于strsplit返回一个列表,我找不到一种方法来正确格式化strsplit(v,“;”)作为向量(unlist不会这样做,因为它会破坏索引)。有没有人对如何加快这段代码有任何想法?

谢谢!

3 个答案:

答案 0 :(得分:4)

使用正则表达式:

v = list("ABC", "DEF;ABC;QWE", "TRF", character(0), "ABC;GFD")
grep("(^|;)ABC($|;)", v)
#[1] 1 2 5

答案 1 :(得分:3)

棘手的部分是处理character(0),@ BlueMagister通过用character(1)替换它来捏造(这允许使用向量,但不允许表示原始问题)。也许

v <- list("ABC", "DEF;ABC;QWE", "TRF", character(0), "ABC;GFD")
v[sapply(v, length) != 0] <- strsplit(unlist(v), ";", fixed=TRUE)

进行字符串拆分。可以在基础R中继续,但我建议使用IRanges

source("http://bioconductor.org/biocLite.R")
biocLite("IRanges")

安装,然后

library(IRanges)
w = CharacterList(v)

它给出了一个类似于列表的结构,其中所有元素都必须是字符向量。

> w
CharacterList of length 5
[[1]] ABC
[[2]] DEF ABC QWE
[[3]] TRF
[[4]] character(0)
[[5]] ABC GFD

然后可以做一些有趣的事情,比如问“元素成员是否等于ABC”

> w == "ABC"
LogicalList of length 5
[[1]] TRUE
[[2]] FALSE TRUE FALSE
[[3]] FALSE
[[4]] logical(0)
[[5]] TRUE FALSE

或“任何元素成员等于ABC”

> any(w == "ABC")
[1]  TRUE  TRUE FALSE FALSE  TRUE

这将很好地扩展。对于“开箱即用”不支持的操作,策略(计算上便宜)是unlist然后使用原始CharacterList作为骨架转换为等长矢量然后relist,例如使用每个成员reverse

> relist(reverse(unlist(w)), w)
CharacterList of length 5
[[1]] CBA
[[2]] FED CBA EWQ
[[3]] FRT
[[4]] character(0)
[[5]] CBA DFG

正如@eddi指出的那样,这比grep慢。动机是(a)避免需要制定复杂的正则表达式,同时(b)获得人们可能喜欢对其结构化的其他操作的灵活性。

答案 2 :(得分:2)

strsplitsapply%in%一起使用:

v <- c("ABC","DEF;ABC;QWE","TRF",character(1),"ABC;GFD")
sapply(strsplit(v,";"),function(x) "ABC" %in% x)
#[1]  TRUE  TRUE FALSE FALSE  TRUE