【问题标题】:Split string by last two characters in R? (/negative string indices)按R中的最后两个字符拆分字符串? (/负字符串索引)
【发布时间】:2016-10-01 21:09:14
【问题描述】:

我的数据框看起来像:

b <- data.frame(height = c(190,165,174,176), name = c('John Smith 34','Mr.Turner 54', 'Antonio P. 23', 'John Brown 31'))

#   height          name
# 1    190 John Smith 34
# 2    165  Mr.Turner 54
# 3    174 Antonio P. 23
# 4    176 John Brown 31

我们可以看到 name 和 age 是相同的值。所以我想用字符串中的最后两个字符来分割它:

  height       name age
1    190 John Smith  34
2    165  Mr.Turner  54
3    174 Antonio P.  23
4    176 John Brown  31

我该怎么做?

【问题讨论】:

  • 我会在最后一个空格上分开,因为年龄有时可以是三位数。见stackoverflow.com/questions/19959697/…
  • library(tidyr) ; b %&gt;% separate(name, into = c('name', 'age'), sep = -3, convert = TRUE)
  • b &lt;- cbind(b[-2], setNames(as.data.frame(do.call(rbind, strsplit(as.character(b$name), '.(?=..$)', perl = T)), stringsAsFactors = FALSE), c('name', 'age'))) ; b$age &lt;- type.convert(b$age),但这需要更多的努力。
  • @alistaire,非常感谢!
  • cbind(b[1], read.csv(text = gsub(' (..)$', ',\\1', b$name), header = FALSE))

标签: r dataframe split


【解决方案1】:

tidyr::separate 允许您传递拆分位置的整数索引,包括从字符串末尾开始的负索引,从而使分隔列变得简单。 (当然,Regex 也可以。)

library(tidyr)

b %>% separate(name, into = c('name', 'age'), sep = -4, convert = TRUE)
##   height        name age
## 1    190 John Smith   34
## 2    165  Mr.Turner   54
## 3    174 Antonio P.   23
## 4    176 John Brown   31

或用最后一个空格分隔:

b %>% separate(name, into = c('name', 'age'), sep = '\\s(?=\\S*?$)', convert = TRUE)

返回相同的东西。

在基础 R 中,这需要更多的工作:

b$name <- as.character(b$name)
split_name <- strsplit(b$name, '\\s(?=\\S*?$)', perl = TRUE)
split_name <- do.call(rbind, split_name)
colnames(split_name) <- c('name', 'age')
b <- data.frame(b[-2], split_name, stringsAsFactors = FALSE)
b$age <- type.convert(b$age)

b
##   height       name age
## 1    190 John Smith  34
## 2    165  Mr.Turner  54
## 3    174 Antonio P.  23
## 4    176 John Brown  31

【讨论】:

  • 有没有一种方法可以为单个观察做到这一点?实际上,我有一列包含不同货币的薪水,即 ₹、$、£ 和 AFN。我无法分隔具有 AFN 的行。在“A”分离后,它现在变成了“FN”。任何想法如何做到这一点。
  • @classy_BLINK 您需要获得的复杂程度取决于它需要处理的数据中可能存在的内容;任何解决方案都包含假设。处理很多事情的一种方法可能看起来像x &lt;- c('$120', '£ 100', 'AFN1,000'); strsplit(x, split = '(?&lt;=[^\\d,])\\s*(?=[\\d,]+)', perl = TRUE);更简单的方法是cur &lt;- gsub('\\d|,|\\s', '', x); amt &lt;- as.numeric(gsub('\\D', '', x))
【解决方案2】:

这里有许多选项使用正则表达式。我会使用substr,因为您想确切地知道要提取的字符数。

data.table 内(用于语法糖):

library(data.table)
setDT(b)[,c("name","age"):=list(
  substr(name,1,nchar(name)-3),
  substr(name,nchar(name)-2,nchar(name)))]

   height       name age
1:    190 John Smith  34
2:    165  Mr.Turner  54
3:    174 Antonio P.  23
4:    176 John Brown  31

注意 name 应该是 character

  b <- data.frame(
  height = c(190,165,174,176), 
  name = c('John Smith 34','Mr.Turner 54', 'Antonio P. 23', 'John Brown 31'),
  stringsAsFactors = FALSE)

【讨论】:

    【解决方案3】:

    就个人而言,我认为以下正则表达式最有用。

    library (stringr)
    b $age <- str_extract (b$name, "\\d{1,3}$")
    b $name <- str_replace (b $name,  "\\d{1,3}$", "")
    

    这会在字符串末尾查找长度为 1-3 个字符的数字序列。可能有一种方法可以将其合并到 separate 语法中,但我是用手机写的,无法探索。

    这个正则表达式的优点是它可以处理单数、双数和三位数的年龄,而不必依赖于存在的空格,也不必从字符串的末尾倒数。

    【讨论】:

      【解决方案4】:

      使用基础 R(与@agstudy 的答案中使用的数据相同):

      data.frame(t(apply(b,1,function(x) {s <- unlist(strsplit(trimws(x[2]), " "));
                 c(x[1],paste0(head(s,-1),collapse=" "),tail(s,1)) })))
      
         # X1         X2 X3
      # 1 190 John Smith 34
      # 2 165  Mr.Turner 54
      # 3 174 Antonio P. 23
      # 4 176 John Brown 31
      

      为了安全起见,我们将修剪 name 列按空格(即strsplit(trimws(x[2]), " "))并将最后一部分(即tail(s,1))作为age 和休息(即head(s,-1))作为名称。

      【讨论】:

        【解决方案5】:

        我们可以使用sub创建一个分隔符(,)而不是年龄之前的空格,使用read.tablecbind读取它,第一列使用base R

        cbind(b[1],read.table(text=sub("\\s+(\\d+)$", ", \\1", b$name), 
                         col.names = c("name", "age"), header=FALSE, sep=","))
        #  height       name age
        #1    190 John Smith  34
        #2    165  Mr.Turner  54
        #3    174 Antonio P.  23
        #4    176 John Brown  31
        

        或者使用来自tidyrextract

        library(tidyr)
        extract(b, name, into = c("name", "age"), "(.*)\\s+(\\S+)$")
        #  height       name age
        #1    190 John Smith  34
        #2    165  Mr.Turner  54
        #3    174 Antonio P.  23
        #4    176 John Brown  31
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-08-30
          • 2014-07-02
          • 2013-01-11
          • 1970-01-01
          • 2012-04-20
          • 1970-01-01
          相关资源
          最近更新 更多