【问题标题】:How to use regular expressions to extract a string from a pattern in R如何使用正则表达式从 R 中的模式中提取字符串
【发布时间】:2018-10-17 19:56:16
【问题描述】:

在 R 中,我想从字符的模式中提取子字符串 向量。我的字符向量 x 的前几个条目(总共 400 个)如下所示:

x <- c(
  ">104K_THEPA | FPrate:0.000 | OMEGA:D-904",
  ">2MMP_ARATH | FPrate:0.006 | OMEGA:S-349",
  ">5MMP_ARATH | FPrate:0.018 | OMEGA:S-337",
  ">5NTD_DIPOM | FPrate:0.026 | OMEGA:S-552",
  ">5NTD_HUMAN | FPrate:0.154 | OMEGA:S-549",
  ">5NTD_MOUSE | FPrate:1.000 | OMEGA:S-551"
)

我想提取FPrate: 后面的 4 位数字,最后还要提取 OMEGA: 后面的字母和最后 3 位数字。 我是使用正则表达式的新手,我花了几个小时来解决这个问题并在网上搜索解决方案,但没有运气。

期望的输出是:

[1] "0.000"  
[2] "0.006"  
[3] "0.018"  
[4] "0.026"  
[5] "0.154"  
[6] "1.000"    

到目前为止,我已经想出了这行代码:

gsub("^[^(FPrate:)]*(FPrate:)|(\\s\\|\\sOMEGA:)[^(\\s\\|\\sOMEGA:)]*$", "", x)

这适用于我的一些条目,但不是全部。

实现这一目标的最佳方法是什么?

【问题讨论】:

  • 您能给我们举一个期望输出的例子吗?
  • 我已将所需的输出添加到帖子中。

标签: r regex


【解决方案1】:

使用stringr 中的str_match 函数仅提取匹配的特定部分(匹配组)将使您的问题变得更容易:

stringr::str_match(vec, 'FPrate:([^ ]*).*OMEGA:([^ ]*)')[,c(2,3)]
     [,1]    [,2]   
[1,] "0.000" "D-904"
[2,] "0.006" "S-349"
[3,] "0.018" "S-337"
[4,] "0.026" "S-552"
[5,] "0.154" "S-549"
[6,] "1.000" "S-551"

str_match 匹配正则表达式并返回一个数据框:第一列是整个匹配项,而接下来的每一列是正则表达式中括号的内容,按顺序排列。因此,通过获取第 2 列和第 3 列,我们只得到 'FPrate:''OMEGA:' 之后的非空白序列。

您可以根据需要添加任意数量的捕获组。例如,如果要将OMEGA 拆分为字母和数字,只需使用更多组:

stringr::str_match(vec, 'FPrate:([^ ]*).*OMEGA:([[:alnum:]])-(\\d*)')[,c(2:4)]
     [,1]    [,2] [,3] 
[1,] "0.000" "D"  "904"
[2,] "0.006" "S"  "349"
[3,] "0.018" "S"  "337"
[4,] "0.026" "S"  "552"
[5,] "0.154" "S"  "549"
[6,] "1.000" "S"  "551"

【讨论】:

  • 谢谢!效果很好。我使用str_match(x, 'FPrate:([^ ]*).*OMEGA:([^ ])') 只得到OMEGA:str_match(x, 'FPrate:([^ ]*).*OMEGA:.-([^ ]*)') 之后的字母,只得到最后3 位数字。有没有办法同时做到这一点(字母与最后 3 位数字分开)?
  • 好的,把它分成多个捕获组
【解决方案2】:

这使用了 stringr 对您进行掩码的无拐杖 stringi 操作以及可读/记录的正则表达式:

library(stringi)
library(tidyverse)

您的数据:

c(
  ">104K_THEPA | FPrate:0.000 | OMEGA:D-904",
  ">2MMP_ARATH | FPrate:0.006 | OMEGA:S-349",
  ">5MMP_ARATH | FPrate:0.018 | OMEGA:S-337",
  ">5NTD_DIPOM | FPrate:0.026 | OMEGA:S-552",
  ">5NTD_HUMAN | FPrate:0.154 | OMEGA:S-549",
  ">5NTD_MOUSE | FPrate:1.000 | OMEGA:S-551"
) -> xdat

提取:

stri_match_first_regex(
  xdat,
  "
  FPrate:([[:digit:]]\\.[[:digit:]]+) # this grabs the FPrate amount
  .*                                  # this skips a bit generically just in case it ever differs
  OMEGA:([[:alnum:]]-[[:digit:]]+)    # this grabs the OMEGA info
  ",
  opts_regex = stri_opts_regex(comments = TRUE)
)[,2:3] %>% 
  as_data_frame() %>% 
  mutate(V1 = as.numeric(V1), V2 = stri_replace_first_fixed(V2, "-", ""))
## # A tibble: 6 x 2
##      V1 V2   
##   <dbl> <chr>
## 1 0     D904 
## 2 0.006 S349 
## 3 0.018 S337 
## 4 0.026 S552 
## 5 0.154 S549 
## 6 1     S551 

另外:对问题中的正则表达式进行很好的尝试。正则表达式并不漂亮,而且在您使用一段时间之前通常没有多大意义。

【讨论】:

    【解决方案3】:

    基础 R

    以下是一些基本的 R 解决方案:

    1) 如果您只需要 FPrate 字段(这就是问题似乎要求的全部),那么这个 sub 就可以了。不需要任何软件包。

    as.numeric(sub(".*FPrate:(\\S+) .*", "\\1", x))
    ## [1] 0.000 0.006 0.018 0.026 0.154 1.000
    

    2) 如果你想解析出所有的 name:value 字段,那么,再一次,只使用 base R 用换行符替换前导非空格,然后替换每次出现的空格字符空格也有换行符。它现在是 dcf 格式,所以使用 read.dcf 读取它,给出字符矩阵m。这可能已经足够好了,但是如果您想要一个数据框,其中每一列都进行了适当的类型转换,那么将其转换为数据框d 并应用type.convert。此解决方案非常通用,因为它不会对 FPrate 和 OMEGA 进行硬编码。

    s <- gsub(" . ", "\n", sub("\\S+", "\n", x))
    m <- read.dcf(textConnection(s))
    d <- as.data.frame(m, stringsAsFactors = FALSE)
    d[] <- lapply(d, type.convert)
    

    给予:

    > m
         FPrate  OMEGA  
    [1,] "0.000" "D-904"
    [2,] "0.006" "S-349"
    [3,] "0.018" "S-337"
    [4,] "0.026" "S-552"
    [5,] "0.154" "S-549"
    [6,] "1.000" "S-551"
    
    > d
      FPrate OMEGA
    1  0.000 D-904
    2  0.006 S-349
    3  0.018 S-337
    4  0.026 S-552
    5  0.154 S-549
    6  1.000 S-551
    

    3) 这个使用strcapture,生成一个数据框,根据proto进行类型转换:

    proto <- data.frame(FPrate = numeric(0), OMEGA = character(0))
    strcapture(".*FPrate:(\\S+) . OMEGA:(\\S+)", x, proto)
    

    给予:

      FPrate OMEGA
    1  0.000 D-904
    2  0.006 S-349
    3  0.018 S-337
    4  0.026 S-552
    5  0.154 S-549
    6  1.000 S-551
    

    4) 在这一节中,我们用空格替换冒号,读入 read.table 剩下的内容,提取我们想要的列,然后设置列名。没有使用正则表达式。

    d <- read.table(text = chartr(":", " ", x), as.is = TRUE)[c(4, 7)]
    names(d) <- c("FPrate", "OMEGA")
    

    给出这个数据框:

      FPrate OMEGA
    1  0.000 D-904
    2  0.006 S-349
    3  0.018 S-337
    4  0.026 S-552
    5  0.154 S-549
    6  1.000 S-551
    

    gsubfn

    5) 此解决方案使用 gsubfn 包。

    library(gsubfn)
    
    pat <- ".*FPrate:(\\S+).*OMEGA:(\\S+)"
    nms <- c("FPrate", "OMEGA")
    read.pattern(text = x, pattern = pat, as.is = TRUE, col.names = nms)
    

    给予:

      FPrate OMEGA
    1  0.000 D-904
    2  0.006 S-349
    3  0.018 S-337
    4  0.026 S-552
    5  0.154 S-549
    6  1.000 S-551
    

    【讨论】:

      【解决方案4】:

      使用纯 r-base 的解决方案

      xx            <- strsplit(x, " \\| ")
      first.numbers <- sapply(xx, function(x) gsub("FPrate:", "", x[2]))
      letters       <- sapply(xx, function(x) gsub("OMEGA:(.?)-\\d+", "\\1", x[[3]]))
      last.digits   <- sapply(xx, function(x) gsub("OMEGA:.?-(\\d+)", "\\1", x[[3]]))
      

      说明

      如果你想坚持使用 r-base,我意识到 gsub 在 R 中非常通用。你甚至可以用它捕获组。

      在这个例子中,为了简单起见,我先用“|”strsplit事物:

      xx <- strsplit(x, " \\| ", perl=TRUE)
      

      如果你现在看xx

      > xx
      [[1]]
      [1] ">104K_THEPA"  "FPrate:0.000" "OMEGA:D-904" 
      
      [[2]]
      [1] ">2MMP_ARATH"  "FPrate:0.006" "OMEGA:S-349" 
      
      [[3]]
      [1] ">5MMP_ARATH"  "FPrate:0.018" "OMEGA:S-337" 
      
      [[4]]
      [1] ">5NTD_DIPOM"  "FPrate:0.026" "OMEGA:S-552" 
      
      [[5]]
      [1] ">5NTD_HUMAN"  "FPrate:0.154" "OMEGA:S-549" 
      
      [[6]]
      [1] ">5NTD_MOUSE"  "FPrate:1.000" "OMEGA:S-551" 
      

      因此您可以只选择第二个或第三个元素,并使用sapply 在列表中传播(在这种情况下相当于unlist(lapply(...)),并在最后返回一个向量。

      要捕获第一个数字,我会这样做:

      first.numbers <- sapply(xx, function(x) gsub("FPrate:", "", x[2]))
      first.numbers
      ## [1] "0.000" "0.006" "0.018" "0.026" "0.154" "1.000"
      

      在这里,我刚刚删除了“FPrate:”。我也可以通过分组来捕捉数字。我会在接下来的捕获中这样做:

      letters <- sapply(xx, function(x) gsub("OMEGA:(.?)-\\d+", "\\1", x[[3]]))
      letters
      ## [1] "D" "S" "S" "S" "S" "S"
      

      注意,这里我用"OMEGA:(.?)-\\d+" 匹配第三个元素的整个表达式,但是用分组() 只捕获一个位置(零或一个,但由于贪心,它会占用一个位置)。 有趣的是我给出的替换整个表达式的内容:"\\1" - 这是为第一组捕获的内容。因此在gsub 中,您可以使用对组\\1\\2 等的引用,具体取决于您添加的分组数量。

      所以我们可以捕捉到最后的数字:

      last.digits <- sapply(xx, function(x) gsub("OMEGA:.?-(\\d+)", "\\1", x[[3]]))
      last.digits
      ## [1] "904" "349" "337" "552" "549" "551"
      

      gsub()毕竟还不错,不是吗?

      【讨论】:

        猜你喜欢
        • 2021-10-19
        • 1970-01-01
        • 2021-11-28
        • 1970-01-01
        • 1970-01-01
        • 2015-03-18
        • 1970-01-01
        • 2014-10-17
        • 1970-01-01
        相关资源
        最近更新 更多