在R中通过最后两个字符拆分字符串?(/负字符串索引)

3

我的数据框如下:

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

我们可以看到姓名和年龄的值是相同的。因此我想通过字符串中的最后两个字符进行拆分:

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

我该怎么做呢?

1
我会在最后一个空格处分割,因为年龄有时可能有三个数字。请参阅https://dev59.com/PHnZa4cB1Zd3GeqPlxl7 - Rich Scriven
3
使用 library(tidyr) 库,对变量 b 进行操作。将 name 变量按照后三个字符为分隔符,拆分成新的两个变量 nameage,并将结果赋值给 b。其中,convert = TRUE 表示将 age 变量转换为数字类型。 - alistaire
但那需要更多的努力。你可以使用 b <- cbind(b[-2], setNames(as.data.frame(do.call(rbind, strsplit(as.character(b$name), '.(?=..$)', perl = T)), stringsAsFactors = FALSE), c('name', 'age'))) ; b$age <- type.convert(b$age) 来完成。 - alistaire
@alistaire,非常感谢你! - Denis
cbind(b[1], read.csv(text = gsub(' (..)$', ',\\1', b$name), header = FALSE)) - rawr
一般来说,R不支持负字符串索引,不像Python。虽然这确实让生活更轻松。 - smci
5个回答

7

tidyr::separate函数可以通过传递一个整数分割位置的索引来轻松地分离列,包括从字符串末尾进行负索引。当然,正则表达式也可以使用。

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
@classy_BLINK 你需要多么复杂取决于它所处理的数据中可能存在的情况。任何解决方案都会嵌入假设。处理大量数据的一种方法可能类似于 x <- c('$120', '£ 100', 'AFN1,000'); strsplit(x, split = '(?<=[^\\d,])\\s*(?=[\\d,]+)', perl = TRUE);一个更简单的方法是 cur <- gsub('\\d|,|\\s', '', x); amt <- as.numeric(gsub('\\D', '', x)) - alistaire

3

有很多选项可以使用正则表达式。我会使用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 应该是一个 字符
  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)

1

使用基础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)) 作为姓名。

1

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

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

这个正则表达式在字符串末尾寻找1-3个数字字符的序列。可能有一种方法将其纳入“separate”语法中,但我正在用手机写作,无法探索。
这个正则表达式的优点是它可以处理单个、双个和三个数字年龄,而不必依赖空格的存在或者从字符串末尾往前数。

1
我们可以使用sub创建一个分隔符(,),而不是在年龄前面使用空格,在read.table中读取它并用第一列使用base Rcbind连接。
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

或者使用 tidyr 中的 extract
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接