【问题标题】:split character data into numbers and letters将字符数据拆分为数字和字母
【发布时间】:2012-03-18 05:54:35
【问题描述】:

我有一个字符数据向量。向量中的大多数元素由一个或多个字母后跟一个或多个数字组成。我希望将向量中的每个元素拆分为字符部分和数字部分。我在 Stackoverflow.com 上发现了一个类似的问题:

split a character from a number with multiple digits

但是,上面给出的答案在我的情况下似乎并不完全有效,或者我做错了什么。下面是一个示例向量:

my.data <- c("aaa", "b11", "b21", "b101", "b111", "ccc1", "ddd1", "ccc20", "ddd13")

# I can obtain the number portion using:
gsub("[^[:digit:]]", "", my.data)

# However, I cannot obtaining the character portion using:
gsub("[:digit:]", "", my.data)

如何获取角色部分?我在 Windows 7 64 位机器上使用 R 版本 2.14.1。

【问题讨论】:

  • 或许你需要使用double-[:gsub("[[:digit:]]", "", my.data)

标签: r


【解决方案1】:

由于之前的答案都没有使用tidyr::separate,所以这里是:

library(tidyr)

df <- data.frame(mycol = c("APPLE348744", "BANANA77845", "OATS2647892", "EGG98586456"))

df %>%
  separate(mycol, 
           into = c("text", "num"), 
           sep = "(?<=[A-Za-z])(?=[0-9])"
           )

【讨论】:

  • 这太棒了,你能详细说明你正在使用的正则表达式吗?我试图使用“[0-9]”,但显然它会删除字母后的所有数字
  • ?&lt;= 是“向后看”:这里它基本上匹配“光标之前”的任何大写或小写字母 ([A-Za-z])。 ?= 是“向前看”:它匹配“光标后”的任何数字 ([0-9])。这两个都没有“移动光标”,因此将它们放在一起匹配字母和数字的“中间”,即我们要拆分的位置。有关 ICU 正则表达式的更多信息,请参阅 here
【解决方案2】:

对于您的正则表达式,您必须使用:

gsub("[[:digit:]]","",my.data)

[:digit:] 字符类仅在一组[] 中才有意义。

【讨论】:

    【解决方案3】:

    使用stringr,如果您愿意(与answer to the other question 略有不同):

    # load library
    library(stringr)
    #
    # load data
    my.data <- c("aaa", "b11", "b21", "b101", "b111", "ccc1", "ddd1", "ccc20", "ddd13")
    #
    # extract numbers only
    my.data.num <- as.numeric(str_extract(my.data, "[0-9]+"))
    #
    # check output
    my.data.num
    [1]  NA  11  21 101 111   1   1  20  13
    #
    # extract characters only
    my.data.cha <- (str_extract(my.data, "[aA-zZ]+"))
    # 
    # check output
    my.data.cha
    [1] "aaa" "b"   "b"   "b"   "b"   "ccc" "ddd" "ccc" "ddd"
    

    【讨论】:

      【解决方案4】:

      迟到的答案,但另一种选择是将strsplit 与正则表达式模式一起使用,该模式使用环视来查找数字和字母之间的边界:

      var <- "ABC123"
      strsplit(var, "(?=[A-Za-z])(?<=[0-9])|(?=[0-9])(?<=[A-Za-z])", perl=TRUE)
      [[1]]
      [1] "ABC" "123"
      

      当前一个字符是字母而后一个字符是数字时,上述模式将匹配(但不消耗),反之亦然。请注意,我们在 Perl 模式下使用 strsplit 来访问环视。

      Demo

      【讨论】:

        【解决方案5】:

        稍微优雅一点的方式(没有任何外部包):

        > x = c("aaa", "b11", "b21", "b101", "b111", "ccc1", "ddd1", "ccc20", "ddd13")
        > gsub('\\D','', x)       # replaces non-digits with blancs
        [1] ""    "11"  "21"  "101" "111" "1"   "1"   "20"  "13" 
        > gsub('\\d','', x)       # replaces digits with blanks
        [1] "aaa" "b"   "b"   "b"   "b"   "ccc" "ddd" "ccc" "ddd"
        

        【讨论】:

          【解决方案6】:

          您还可以使用reshape2 中的colsplit 一步将向量拆分为字符和数字列:

          library(reshape2)
          
          colsplit(my.data, "(?<=\\p{L})(?=[\\d+$])", c("char", "digit"))
          

          结果:

            char digit
          1  aaa    NA
          2    b    11
          3    b    21
          4    b   101
          5    b   111
          6  ccc     1
          7  ddd     1
          8  ccc    20
          9  ddd    13
          

          数据:

          my.data <- c("aaa", "b11", "b21", "b101", "b111", "ccc1", "ddd1", "ccc20", "ddd13")
          

          【讨论】:

            【解决方案7】:
            mydata.nub<-gsub("\ \ D","",my.data)
            
            mydata.text<-gsub("\ \ d","",my.data)
            

            这个就完美了,也把数字和文字分开了,即使文字之间有数字。

            【讨论】:

              【解决方案8】:

              如果结果应该被重新分配给一个单个分割字符串:

              var <- "foo123 bar1987"
              rpaste(strsplit(var, "(?=[A-Za-z])(?<=[0-9])|(?=[0-9])(?<=[A-Za-z])", perl=TRUE)[[1]], collapse = ' ')
              

              结果:

              "foo 123 bar 1987"
              

              或者对于要重新分配给数据框的矢量化版本

              df = data.frame(text=c("foo121", "131bar foo1516"))
              res = strsplit(df$text, "(?=[A-Za-z])(?<=[0-9])|(?=[0-9])(?<=[A-Za-z])", perl=TRUE)
              df$res = sapply(res, paste, collapse=" ")
              

              结果:

                          text              res
              1         foo121          foo 121
              2 131bar foo1516 131 bar foo 1516
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-09-30
                • 1970-01-01
                • 2019-05-26
                • 2021-06-15
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多