如何从字符中高效地提取特定的模式?

9

我有这样的大数据:

> Data[1:7,1]
[1] mature=hsa-miR-5087|mir_Family=-|Gene=OR4F5        
[2] mature=hsa-miR-26a-1-3p|mir_Family=mir-26|Gene=OR4F9
[3] mature=hsa-miR-448|mir_Family=mir-448|Gene=OR4F5   
[4] mature=hsa-miR-659-3p|mir_Family=-|Gene=OR4F5      
[5] mature=hsa-miR-5197-3p|mir_Family=-|Gene=OR4F5     
[6] mature=hsa-miR-5093|mir_Family=-|Gene=OR4F5        
[7] mature=hsa-miR-650|mir_Family=mir-650|Gene=OR4F5

我想做的是,对于每一行,我想选择单词mature=后面的名称以及Gene=后面的单词,并将它们连在一起使用

分隔。
paste(a,b, sep="-")

例如,前两行的预期输出应该如下所示:
hsa-miR-5087-OR4F5
hsa-miR-26a-1-3p-OR4F9

那么,最终的实现方法如下:
for(i in 1:nrow(Data)){
    Data[i,3] <- sub("mature=([^|]*).*Gene=(.*)", "\\1-\\2", Data[i,1])
    Name <- strsplit(as.vector(Data[i,2]),"\\|")[[1]][2]
    Data[i,4] <- as.numeric(sub("pvalue=","",Name))
    print(i)
}

这个实现方式虽然能够正常工作,但是速度很慢。由于Data的大小非常大,有2亿行数据,因此这个实现方式非常慢。那么如何提高它的速度呢?


2
我们没有Data,因此在像这样制定问题时最好像这样显示数据:x <- Data[1:7, 1]; dput(x) - G. Grothendieck
2
你的编辑使得这个问题变得有点模糊了——最初并不清楚你需要一个计算效率高的解决方案。我鼓励你发表自己的答案,并在一个相当大的数据集上(例如,尝试在你的数据集的前100,000行上运行)对下面提供的所有答案进行基准测试,遵循这个问题中给出的格式。 - Ben Bolker
2
你也可以查看stringi包,它旨在进行快速字符串处理,以及data.table和/或dplyr包。 - Ben Bolker
5个回答

11

如果您能保证格式与您指定的完全一致,那么正则表达式可以捕获(在下面的括号中表示)等号到管道符之间以及从Gene = 到末尾的所有内容,并用连字符将它们粘合在一起:

sub("mature=([^|]*).*Gene=(.*)", "\\1-\\2", Data[,1])

5

另一种选择是使用read.table函数,并将=作为分隔符,然后将两列粘贴在一起:

res = read.table(text=txt,sep='=')
paste(sub('[|].*','',res$V2),            ## get rid from last part here
      sub('^ +| +$','',res$V4),sep='-')  ## remove extra spaces 

[1] "hsa-miR-5087-OR4F5"     "hsa-miR-26a-1-3p-OR4F9" "hsa-miR-448-OR4F5"      "hsa-miR-659-3p-OR4F5"  
[5] "hsa-miR-5197-3p-OR4F5"  "hsa-miR-5093-OR4F5"     "hsa-miR-650-OR4F5"   

5

已经给出的简单sub解决方案看起来非常好,但为了保险起见,这里提供一些其他方法:

1) read.pattern 使用gsubfn包中的read.pattern将数据解析为一个数据框。这个中间形式DF可以用多种方式进行操作。在这种情况下,我们使用paste与问题中基本相同的方式:

library(gsubfn)
DF <- read.pattern(text = Data[, 1], pattern = "(\\w+)=([^|]*)")
paste(DF$V2, DF$V6, sep = "-")

提供:

[1] "hsa-miR-5087-OR4F5"     "hsa-miR-26a-1-3p-OR4F9" "hsa-miR-448-OR4F5"     
[4] "hsa-miR-659-3p-OR4F5"   "hsa-miR-5197-3p-OR4F5"  "hsa-miR-5093-OR4F5"    
[7] "hsa-miR-650-OR4F5"   

生成的中间数据框 DF 如下所示:

> DF
      V1               V2         V3      V4   V5    V6
1 mature     hsa-miR-5087 mir_Family       - Gene OR4F5
2 mature hsa-miR-26a-1-3p mir_Family  mir-26 Gene OR4F9
3 mature      hsa-miR-448 mir_Family mir-448 Gene OR4F5
4 mature   hsa-miR-659-3p mir_Family       - Gene OR4F5
5 mature  hsa-miR-5197-3p mir_Family       - Gene OR4F5
6 mature     hsa-miR-5093 mir_Family       - Gene OR4F5
7 mature      hsa-miR-650 mir_Family mir-650 Gene OR4F5

这是我们使用的正则表达式的可视化效果:

(\w+)=([^|]*)

正则表达式可视化

Debuggex演示

1a) 名称 通过分别读取三列数据和三个名称,我们可以使DF看起来更好。这也改善了paste语句:

DF <- read.pattern(text = Data[, 1], pattern = "=([^|]*)")
names(DF) <- unlist(read.pattern(text = Data[1,1], pattern = "(\\w+)=", as.is = TRUE))

paste(DF$mature, DF$Gene, sep = "-") # same answer as above

这一部分产生的 DF 看起来像这样。它只有 3 列而不是 6 列,剩余的列被用来确定适当的列名:
> DF
            mature mir_Family  Gene
1     hsa-miR-5087          - OR4F5
2 hsa-miR-26a-1-3p     mir-26 OR4F9
3      hsa-miR-448    mir-448 OR4F5
4   hsa-miR-659-3p          - OR4F5
5  hsa-miR-5197-3p          - OR4F5
6     hsa-miR-5093          - OR4F5
7      hsa-miR-650    mir-650 OR4F5

2) strapplyc

另一种使用相同软件包的方法。它提取等号后面且不含有|的字段,产生一个列表。然后我们对该列表使用sapply函数将第一个和第三个字段粘贴在一起:

sapply(strapplyc(Data[, 1], "=([^|]*)"), function(x) paste(x[1], x[3], sep = "-"))

给出相同的结果。

这是使用的正则表达式的可视化:

=([^|]*)

正则表达式可视化

Debuggex演示


4
这里有一种方法:

以下是一个示例:

Data <- readLines(n = 7)
mature=hsa-miR-5087|mir_Family=-|Gene=OR4F5        
mature=hsa-miR-26a-1-3p|mir_Family=mir-26|Gene=OR4F9
mature=hsa-miR-448|mir_Family=mir-448|Gene=OR4F5   
mature=hsa-miR-659-3p|mir_Family=-|Gene=OR4F5      
mature=hsa-miR-5197-3p|mir_Family=-|Gene=OR4F5     
mature=hsa-miR-5093|mir_Family=-|Gene=OR4F5        
mature=hsa-miR-650|mir_Family=mir-650|Gene=OR4F5
df <- read.table(sep = "|", text = Data, stringsAsFactors = FALSE)
l <- lapply(df, strsplit, "=")
trim <- function(x) gsub("^\\s*|\\s*$", "", x)
paste(trim(sapply(l[[1]], "[", 2)), trim(sapply(l[[3]], "[", 2)), sep = "-")
# [1] "hsa-miR-5087-OR4F5"     "hsa-miR-26a-1-3p-OR4F9" "hsa-miR-448-OR4F5"      "hsa-miR-659-3p-OR4F5"   "hsa-miR-5197-3p-OR4F5"  "hsa-miR-5093-OR4F5"    
# [7] "hsa-miR-650-OR4F5"

4
也许不是最优雅的方法,但您可以尝试:
sapply(Data[,1],function(x){
                   parts<-strsplit(x,"\\|")[[1]]
                   y<-paste(gsub("(mature=)|(Gene=)","",parts[grepl("mature|Gene",parts)]),collapse="-")
                   return(y)
                })

例子

 Data<-data.frame(col1=c("mature=hsa-miR-5087|mir_Family=-|Gene=OR4F5","mature=hsa-miR-26a-1-3p|mir_Family=mir-26|Gene=OR4F9"),col2=1:2,stringsAsFactors=F)

> Data[,1]
[1] "mature=hsa-miR-5087|mir_Family=-|Gene=OR4F5"          "mature=hsa-miR-26a-1-3p|mir_Family=mir-26|Gene=OR4F9"

> sapply(Data[,1],function(x){
+                        parts<-strsplit(x,"\\|")[[1]]
+                        y<-paste(gsub("(mature=)|(Gene=)","",parts[grepl("mature|Gene",parts)]),collapse="-")
+                        return(y)
+                     })
         mature=hsa-miR-5087|mir_Family=-|Gene=OR4F5 mature=hsa-miR-26a-1-3p|mir_Family=mir-26|Gene=OR4F9 
                                "hsa-miR-5087-OR4F5"                             "hsa-miR-26a-1-3p-OR4F9"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接