【问题标题】:Extract numbers from strings including '|'从包含“|”的字符串中提取数字
【发布时间】:2014-06-17 00:55:13
【问题描述】:

我有一些数据是用“|”分隔的数字,例如:

head(mintimes)
[1] "3121|3151" "1171"      "1351|1381" "1050"      ""          "122" 
head(minvalues)
[1] 14    10    11    31 Inf    22

我想做的是提取所有时间并将它们与最小值匹配。最后得到类似的东西:

times    values
3121     14
3151     14
1171     10
1351     11
1381     11
1050     31
122      22

我试过strsplit(mintimes, "|")str_extract(mintimes, "[0-9]+"),但它们似乎不起作用。有什么想法吗?

【问题讨论】:

    标签: regex string r split


    【解决方案1】:

    | 是一个正则表达式元字符。在字面上使用时,这些特殊字符需要使用[]\\ 进行转义(或者您可以在某些函数中使用fixed = TRUE)。所以你打电话给strsplit() 应该是

    strsplit(mintimes, "[|]")
    

    strsplit(mintimes, "\\|")
    

    strsplit(mintimes, "|", fixed = TRUE)
    

    关于您使用 stringr 函数的其他尝试,str_extract_all() 似乎可以解决问题。

    library(stringr)
    str_extract_all(mintimes, "[0-9]+")
    

    为了得到你想要的结果

    > mintimes <- c("3121|3151", "1171", "1351|1381", "1050", "", "122") 
    > minvalues <- c(14, 10, 11, 31, Inf, 22)
    > s <- strsplit(mintimes, "[|]")
    > data.frame(times = as.numeric(unlist(s)), 
                 values = rep(minvalues, sapply(s, length)))
    #   times values
    # 1  3121     14
    # 2  3151     14
    # 3  1171     10
    # 4  1351     11
    # 5  1381     11
    # 6  1050     31
    # 7   122     22
    

    【讨论】:

    • 好的,先生,快画吧。 ;)
    • 我认为 OP 不仅要在垂直管道上拆分,还要将值与时间匹配。一种方法可能是在 times2 列中复制 times 变量,然后编写类似 ifelse(grep("\\|") 的内容,什么都不做(即,将 times1 和 times2 保留原样,否则 "" 删除不需要的 time2。然后,按照他的示例格式创建一个数据框。这样的魔法显然超出了这个新手。
    • @user2583119,我已经更新了答案以包含所需的结果。
    【解决方案2】:

    默认情况下strsplit 使用正则表达式和“|”进行拆分是正则表达式语法中的特殊字符。你可以逃避它

    strsplit(mintimes,"\\|")
    

    或者只是设置 fixed=T 不使用正则表达式

    strsplit(mintimes,"|", fixed=T)
    

    【讨论】:

      【解决方案3】:

      我编写了一个名为cSplit 的函数,它对这些类型的事情很有用。你可以从我的 Gist 获得它:https://gist.github.com/mrdwab/11380733

      用法是:

      cSplit(data.table(mintimes, minvalues), "mintimes", "|", "long")
      #    mintimes minvalues
      # 1:     3121        14
      # 2:     3151        14
      # 3:     1171        10
      # 4:     1351        11
      # 5:     1381        11
      # 6:     1050        31
      # 7:      122        22
      

      它还有一个“宽”设置,以防万一这对你有用:

      cSplit(data.table(mintimes, minvalues), "mintimes", "|", "wide")
      #    minvalues mintimes_1 mintimes_2
      # 1:        14       3121       3151
      # 2:        10       1171         NA
      # 3:        11       1351       1381
      # 4:        31       1050         NA
      # 5:       Inf         NA         NA
      # 6:        22        122         NA
      

      注意:输出是data.table

      【讨论】:

      • 现在应该可以更新为library(splitstackshape)了吧?
      • @RichardScriven,SO 上可能至少有几十个答案以cSplit 作为答案的一部分——不准备全部更新(也因为这将问题推到了前面的“活动”页面,我不希望这种情况发生)。
      • @RichardScriven,作为替代方案,我在cSplit Gist 中添加了一个message,提到它现在是“splitstackshape”的一部分。这应该会有所帮助:-)
      【解决方案4】:

      正如其他人所提到的,您需要转义 | 以将其包含在正则表达式中。与往常一样,我们可以通过多种方式给这只猫剥皮,这是使用stringr 的一种方法:

      x <- c("3121|3151", "1171", "1351|1381", "1050", "", "122")
      
      library(stringr)
      unlist(str_extract_all(x, "\\d+"))
      
      # [1] "3121" "3151" "1171" "1351" "1381" "1050" "122"
      

      如果数字字符串中有任何小数点,这将无法按预期工作,因此以下内容(表示匹配除| 之外的任何内容)可能更安全:

      unlist(str_extract_all(x, '[^|]+'))
      
      # [1] "3121" "3151" "1171" "1351" "1381" "1050" "122" 
      

      无论哪种方式,您都可能希望将结果包装在 as.numeric 中。

      【讨论】:

        【解决方案5】:

        这是另一个使用 stringi 包中的 stri_split_fixed 的解决方案。作为附加值,我们还使用mapplydo.call

        输入数据:

        mintimes <- c("3121|3151", "1171", "1351|1381", "1050", "", "122") 
        minvalues <- c(14, 10, 11, 31, Inf, 22)
        

        拆分mintimes w.r.t. | 并转换为数字:

        library("stringi")    
        mintimes <- lapply(stri_split_fixed(mintimes, "|"), as.numeric)
        ## [[1]]
        ## [1] 3121 3151
        ## 
        ## [[2]]
        ## [1] 1171
        ## 
        ## [[3]]
        ## [1] 1351 1381
        ## 
        ## [[4]]
        ## [1] 1050
        ## 
        ## [[5]]
        ## [1] NA
        ## 
        ## [[6]]
        ## [1] 122
        

        将每个minvalues与对应的mintimes进行列绑定:

        tmp <- mapply(cbind, mintimes, minvalues)
        ## [[1]]
        ##      [,1] [,2]
        ## [1,] 3121   14
        ## [2,] 3151   14
        ## 
        ## [[2]]
        ##      [,1] [,2]
        ## [1,] 1171   10
        ## 
        ## [[3]]
        ##      [,1] [,2]
        ## [1,] 1351   11
        ## [2,] 1381   11
        ## 
        ## [[4]]
        ##      [,1] [,2]
        ## [1,] 1050   31
        ## 
        ## [[5]]
        ##      [,1] [,2]
        ## [1,]   NA  Inf
        ## 
        ## [[6]]
        ##      [,1] [,2]
        ## [1,]  122   22
        

        行绑定所有 6 个矩阵并删除 NA-rows:

        res <- do.call(rbind, tmp)
        res[!is.na(res[,1]),]
        ##      [,1] [,2]
        ## [1,] 3121   14
        ## [2,] 3151   14
        ## [3,] 1171   10
        ## [4,] 1351   11
        ## [5,] 1381   11
        ## [6,] 1050   31
        ## [7,]  122   22
        

        【讨论】:

          【解决方案6】:

          要获得所需的输出,请尝试以下操作:

          library(dplyr)
          
          Split.Times <- function(x) {
            mintimes <- as.numeric(unlist(strsplit(as.character(x$mintimes), "\\|")))
            return(data.frame(mintimes = mintimes, minvalues = x$minvalues, stringsAsFactors=FALSE))
          }
          
          df <- data.frame(mintimes, minvalues, stringsAsFactors=FALSE)
          
          df %>%
            filter(mintimes != "") %>%
            group_by(mintimes) %>%
            do(Split.Times(.))
          

          这会产生:

            mintimes minvalues
          1     1050        31
          2     1171        10
          3      122        22
          4     1351        11
          5     1381        11
          6     3121        14
          7     3151        14
          

          (我从 my answer here 借来的——这几乎是同一个问题)

          【讨论】:

            【解决方案7】:

            这是一个qdap 封装方法:

            mintimes <- c("3121|3151", "1171", "1351|1381", "1050", "", "122") 
            minvalues <- c(14, 10, 11, 31, Inf, 22)
            
            library(qdap)
            list2df(setNames(strsplit(mintimes, "\\|"), minvalues), "times", "values")
            
            ##   times values
            ## 1  3121     14
            ## 2  3151     14
            ## 3  1171     10
            ## 4  1351     11
            ## 5  1381     11
            ## 6  1050     31
            ## 7   122     22
            

            【讨论】:

              【解决方案8】:

              你可以使用 [:punct:]

              strsplit(mintimes, "[[:punct:]]")
              

              【讨论】:

              • 我一般不会推荐这个。如果您在数字字符向量中有任何小数点,并使用该模式,您将破坏您的数据。
              猜你喜欢
              • 2020-01-11
              • 2021-03-19
              • 1970-01-01
              • 2018-05-13
              • 1970-01-01
              • 1970-01-01
              • 2023-01-10
              • 1970-01-01
              • 2023-03-24
              相关资源
              最近更新 更多