【问题标题】:String split with conditions in R在 R 中使用条件拆分字符串
【发布时间】:2015-11-03 02:22:53
【问题描述】:

我有这个mystring 和分隔符_。这里的条件是如果有两个或多个分隔符,我想在第二个分隔符处拆分,如果只有一个分隔符,我想在".Recal"处拆分并得到result,如下所示。

mystring<-c("MODY_60.2.ReCal.sort.bam","MODY_116.21_C4U.ReCal.sort.bam","MODY_116.3_C2RX-1-10.ReCal.sort.bam","MODY_116.4.ReCal.sort.bam")

结果

"MODY_60.2"  "MODY_116.21" "MODY_116.3"  "MODY_116.4"

【问题讨论】:

  • 有趣的问题。不知道有没有办法一次性搞定。我可以像 sub("(.+_.+)_.+$", "\\1", sub("\\.ReCal.+$", "", mystring)) 一样破解它,但我相信有人可以改进它。
  • 模仿正则表达式的逻辑,可能是这样的:sub("(?(?=.+_.+_)(.+_.+)_.*|(.+)\\.ReCal.*)", "\\1\\2", mystring, perl = TRUE).
  • 如果您确实设法在一个正则表达式中实现了这一点,或者甚至采用了@nongkrong 的解决方案,在代码上方添加评论,以便您未来的自己(或成为看守人)知道你想做什么。我用我年轻时自己制作的“非常聪明的解决方案”咬了自己几次,花了很长时间才记住背后的细节/推理。

标签: regex r string split


【解决方案1】:

您可以使用gsubfn 来做到这一点

library(gsubfn)
f <- function(x,y,z) if (z=="_") y else strsplit(x, ".ReCal", fixed=T)[[1]][[1]]
gsubfn("([^_]+_[^_]+)(.).*", f, mystring, backref=2)
# [1] "MODY_60.2"   "MODY_116.21" "MODY_116.3"  "MODY_116.4" 

这允许您有两个以上的“_”,并且您希望在第二个上拆分,例如,

mystring<-c("MODY_60.2.ReCal.sort.bam",
            "MODY_116.21_C4U.ReCal.sort.bam",
            "MODY_116.3_C2RX-1-10.ReCal.sort.bam",
            "MODY_116.4.ReCal.sort.bam",
            "MODY_116.4_asdfsadf_1212_asfsdf",
            "MODY_116.5.ReCal_asdfsadf_1212_asfsdf",  # split by second "_", leaving ".ReCal"
            "MODY")

gsubfn("([^_]+_[^_]+)(.).*", f, mystring, backref=2)
# [1] "MODY_60.2"        "MODY_116.21"      "MODY_116.3"       "MODY_116.4"      
# [5] "MODY_116.4"       "MODY_116.5.ReCal" "MODY"            

在函数中,fx是原始字符串,yz是接下来的匹配项。因此,如果z 不是“_”,则继续按替代字符串进行拆分。

【讨论】:

  • 你能解释一下这些数字是什么吗:[[1]][[1]];这个结构:("([^_]+_[^_]+)(.).*", f, mystring, backref=2)
  • @MAPK [[1]][[1]] 正在索引strsplit 返回的结果,以获取返回拆分的第一部分。 "[^_]+" 正则表达式的意思是'匹配任何不等于 "_" 的东西'。因此,正则表达式的第一部分(包含在第一组“( )”中)匹配到第二个“_”,如果有的话。 “(.)”匹配下一个字符,检查是否有第二个“_”(这个匹配对应于函数f中的z)。
【解决方案2】:

使用stringr 包:

str_extract(mystring, '.*?_.*?(?=_)|^.*?_.*(?=\\.ReCal)')
[1] "MODY_60.2" "MODY_116.21" "MODY_116.3" "MODY_116.4"

它也适用于两个以上的分隔符。

【讨论】:

    【解决方案3】:

    Perl/PCRE 具有 branch reset 功能,当您在不同的替代方案中捕获组时,可以重复使用组号,并被视为一个捕获组。

    IMO,当您想提供不同的替代方案时,此功能非常优雅。

    x <- c('MODY_60.2.ReCal.sort.bam', 'MODY_116.21_C4U.ReCal.sort.bam', 
           'MODY_116.3_C2RX-1-10.ReCal.sort.bam', 'MODY_116.4.ReCal.sort.bam',
           'MODY_116.4_asdfsadf_1212_asfsdf', 'MODY_116.5.ReCal_asdfsadf_1212_asfsdf', 'MODY')
    
    sub('^(?|([^_]*_[^_]*)_.*|(.*)\\.ReCal.*)$', '\\1', x, perl=T)
    # [1] "MODY_60.2"        "MODY_116.21"      "MODY_116.3"       "MODY_116.4"      
    # [5] "MODY_116.4"       "MODY_116.5.ReCal" "MODY"  
    

    【讨论】:

      【解决方案4】:
      gsub('^(.*\\.\\d+).*','\\1',mystring)
      [1] "MODY_60.2"   "MODY_116.21" "MODY_116.3"  "MODY_116.4"
      

      【讨论】:

      • 这可行,但没有采用所需的逻辑。您正在查看示例字符串并重新解释要识别的模式。您至少应该就其工作原理提供更多解释。
      【解决方案5】:
      ^([^_\\n]*_[^_\\n]*)(?:_.*|\\.ReCal[^_]*)$
      

      您可以简单地使用gsub,而无需使用任何复杂的正则表达式。只需替换为\\1。参见演示。

      https://regex101.com/r/wL4aB6/1

      【讨论】:

        【解决方案6】:

        有点长,但需要的正则表达式知识较少:

        library(stringr)
        indx <- str_locate_all(mystring, "_")
        
        for (i in seq_along(indx)) {
          if (nrow(indx[[i]]) == 1) {
            mystring[i] <- strsplit(mystring[i], ".ReCal")[[1]][1]
          } else {
            mystring[i] <- substr(mystring[i], start = 1, stop = indx[[i]][2] - 1)
          }
        }
        

        【讨论】:

          【解决方案7】:

          gregexpr 可以在字符串中搜索模式并给出位置。

          首先,我们使用gregexpr来查找mystring的每个元素中所有_的位置。然后,我们遍历该输出并在mystring 的每个元素中提取第二个_ 的索引。如果没有第二个_,它将返回一个NA(在下面的示例中检查inds)。

          之后,我们可以根据提取的索引使用substr提取相关部分,或者,如果有NA,我们可以在.ReCal处拆分字符串并只保留第一部分。

          inds = sapply(gregexpr("_", mystring, fixed = TRUE), function(x) x[2])
          ifelse(!is.na(inds),
                 substr(mystring, 1, inds - 1), 
                 sapply(strsplit(mystring, ".ReCal"), '[', 1))
          #[1] "MODY_60.2"   "MODY_116.21" "MODY_116.3"  "MODY_116.4" 
          

          【讨论】:

          • 如果您解释了您提供的代码如何回答问题,这将是一个更好的答案。
          猜你喜欢
          • 2014-09-16
          • 2017-10-01
          • 1970-01-01
          • 2021-04-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-30
          • 1970-01-01
          相关资源
          最近更新 更多