【问题标题】:How to delete everything after nth delimiter in R?如何删除R中第n个分隔符之后的所有内容?
【发布时间】:2016-01-08 19:18:09
【问题描述】:

我有这个向量myvec。我想在第二个“:”之后删除所有内容并获得结果。如何删除第 n 个 ':' 之后的字符串?

myvec<- c("chr2:213403244:213403244:G:T:snp","chr7:55240586:55240586:T:G:snp" ,"chr7:55241607:55241607:C:G:snp")

result
chr2:213403244   
chr7:55240586
chr7:55241607

【问题讨论】:

    标签: r regex


    【解决方案1】:

    这里有一些选择。我们删除第 k 个冒号及其后的所有内容。问题中的示例对应于 k = 2。在下面的示例中,我们使用 k = 3。

    1) read.table 将数据读取到 data.frame 中,挑选出所需的列并将其重新粘贴在一起:

    k <- 3 # keep first 3 fields only
    do.call(paste, c(read.table(text = myvec, sep = ":")[1:k], sep = ":"))
    

    给予:

    [1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
    [3] "chr7:55241607:55241607"  
    

    2) sprintf/sub 构造适当的正则表达式(在下面的 k 等于 3 的情况下,它将是 ^((.*?:){2}.*?):.* )并将其与 sub 一起使用:

    k <- 3
    sub(sprintf("^((.*?:){%d}.*?):.*", k-1), "\\1", myvec)
    

    给予:

    [1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
    [3] "chr7:55241607:55241607"  
    

    注 1: 对于 k=1,可以进一步简化为 sub(":.*", "", myvec),对于 k=n-1,可以进一步简化为 sub(":[^:]*$", "", myvec)

    注2:这是k等于3的正则表达式的可视化:

    ^((.*?:){2}.*?):.*
    

    Debuggex Demo

    3) 迭代删除最后一个字段 我们可以使用上面注 1 中的最后一个正则表达式删除最后一个字段 n-k 次,如下所示:

    n <- 6 # number of fields
    k < - 3 # number of fields to retain
    out <- myvec
    for(i in seq_len(n-k)) out <- sub(":[^:]*$", "", out)
    

    如果我们想自动设置 n,我们可以选择将上面的硬编码行设置 n 替换为:

    n <- count.fields(textConnection(myvec[1]), sep = ":")
    

    4) 定位第 k 个冒号的位置 使用gregexpr 定位冒号的位置,然后提取第 k 个的位置减去一个,因为我们不想要尾随冒号。使用substr 从相应的字符串中提取那么多字符。

    k <- 3
    substr(myvec, 1, sapply(gregexpr(":", myvec), "[", k) - 1)
    

    给予:

    [1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
    [3] "chr7:55241607:55241607"  
    

    注3:假设有n个字段。该问题要求删除第 k 个分隔符之后的所有内容,因此该解决方案应该适用于 k = 1、2、...、n-1。它不需要为 k = n 工作,因为没有 n 分隔符;但是,如果我们将 k 定义为要返回的字段数,那么 k = n 是有意义的,事实上,(1) 和 (3) 在这种情况下也有效。 (2) 和 (4) 不适用于此扩展,但我们可以通过使用 paste0(myvec, ":") 而不是 myvec 作为输入轻松让它们工作。

    注意 4:我们比较性能:

    library(rbenchmark)
    benchmark(
     .read.table = do.call(paste, c(read.table(text = myvec, sep = ":")[1:k], sep = ":")),
     .sprintf.sub = sub(sprintf("^((.*?:){%d}.*?):.*", k-1), "\\1", myvec),
     .for = { out <- myvec; for(i in seq_len(n-k)) out <- sub(":[^:]*$", "", out)},
     .gregexpr = substr(myvec, 1, sapply(gregexpr(":", myvec), "[", k) - 1),
      order = "elapsed", replications = 1000)[1:4]
    

    给予:

              test replications elapsed relative
    2 .sprintf.sub         1000    0.11    1.000
    4    .gregexpr         1000    0.14    1.273
    3         .for         1000    0.15    1.364
    1  .read.table         1000    2.16   19.636
    

    使用 sprintf 和 sub 的解决方案是最快的,尽管它确实使用了复杂的正则表达式,而其他解决方案使用更简单的正则表达式或不使用正则表达式,并且出于简单的原因可能更受欢迎。

    添加添加了额外的解决方案和额外的注释。

    【讨论】:

      【解决方案2】:

      我们可以使用sub。我们从字符串的开头匹配一个或多个不是: 的字符(^([^:]+),然后是:,然后再匹配一个不是: 的字符([^:]+),将其放在捕获组,即在括号内。我们在替换中用捕获组(\\1)替换。

      sub('^([^:]+:[^:]+).*', '\\1', myvec)
      #[1] "chr2:213403244" "chr7:55240586"  "chr7:55241607" 
      

      以上适用于发布的示例。对于在第 n 个分隔符之后删除的一般情况,

      n <- 2
      pat <- paste0('^([^:]+(?::[^:]+){',n-1,'}).*')
      sub(pat, '\\1', myvec)
      #[1] "chr2:213403244" "chr7:55240586"  "chr7:55241607" 
      

      用不同的'n'检查

      n <- 3
      

      重复同样的步骤

      sub(pat, '\\1', myvec)
      #[1] "chr2:213403244:213403244" "chr7:55240586:55240586"  
      #[3] "chr7:55241607:55241607"  
      

      或者另一种选择是按: 拆分,然后将paste 拆分为n 个组件。

      n <- 2
      vapply(strsplit(myvec, ':'), function(x)
                  paste(x[seq.int(n)], collapse=':'), character(1L))
      #[1] "chr2:213403244" "chr7:55240586"  "chr7:55241607" 
      

      【讨论】:

        猜你喜欢
        • 2021-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-19
        • 2017-04-03
        • 1970-01-01
        • 2021-10-15
        相关资源
        最近更新 更多