regex - R - 提取所有匹配模式的字符串并创建关系表

标签 regex r dplyr

我正在寻找以下问题的更短、更漂亮的解决方案(可能在 tidyverse 中)。我有一个数据框“数据”:

  id            string
1  A 1.001 xxx 123.123
2  B 23,45 lorem ipsum
3  C      donald trump
4  D    ssss 134, 1,45

我想做的是提取所有数字(无论分隔符是“.”还是“,” -> 在这种情况下,我假设字符串“134, 1,45”可以提取成两个数字: 134 和 1.45) 并创建一个类似于此的 data.frame“输出”:

  id  string
1  A   1.001
2  A 123.123
3  B   23.45
4  C    <NA>
5  D     134
6  D    1.45

我设法做到了这一点(下面的代码),但解决方案对我来说非常难看,而且效率不高(两个 for 循环)。有人可以建议一个更好的方法来做到这一点(最好使用 dplyr)

# data
data <- data.frame(id = c("A", "B", "C", "D"), 
                  string = c("1.001 xxx 123.123", 
                             "23,45 lorem ipsum", 
                             "donald trump", 
                             "ssss 134, 1,45"),
                  stringsAsFactors = FALSE)

# creating empty data.frame                     
len <- length(unlist(sapply(data$string, function(x) gregexpr("[0-9]+[,|.]?[0-9]*", x))))
output <- data.frame(id = rep(NA, len), string = rep(NA, len))

# main solution
start = 0

for(i in 1:dim(data)[1]){
  tmp_len <- length(unlist(gregexpr("[0-9]+[,|.]?[0-9]*", data$string[i])))
  for(j in (start+1):(start+tmp_len)){
    output[j,1] <- data$id[i]
    output[j,2] <- regmatches(data$string[i], gregexpr("[0-9]+[,|.]?[0-9]*", data$string[i]))[[1]][j-start]
  }
  start = start + tmp_len
}

# further modifications
output$string <- gsub(",", ".", output$string)
output$string <- as.numeric(ifelse(substring(output$string, nchar(output$string), nchar(output$string)) == ".",
                                   substring(output$string, 1, nchar(output$string) - 1),
                                   output$string))

output

最佳答案

1) Base R 这使用相对简单的正则表达式,没有包。

在前两行代码中,将任何逗号后跟一个空格替换为 空格,然后用点替换所有剩余的逗号。在这两行之后 s 将是:c("1.001 xxx 123.123", "23.45 lorem ipsum", "donald trump", "ssss 134 1.45")

在接下来的 4 行代码中,修剪每个字符串字段开头和结尾的空格,并在空格处拆分字符串字段,生成一个 列表。 grep 找出那些仅由数字和点组成的元素。 (正则表达式 ^[0-9.]*$ 匹配单词开头后跟零个或多个数字或点后跟单词结尾,因此只匹配仅包含这些字符的单词.) 用 NA 替换任何零长度组件。最后添加 data$id 作为名称。在这 4 行运行之后,列表 L 将是 list(A = c("1.001", "123.123"), B = "23.45", C = NA, D = c( "134", "1.45")) .

在最后一行代码中,将列表 L 转换为具有适当名称的数据框。

s <- gsub(", ", " ", data$string)
s <- gsub(",", ".", s)

L <- strsplit(trimws(s), "\\s+")
L <- lapply(L, grep, pattern = "^[0-9.]*$", value = TRUE)
L <- ifelse(lengths(L), L, NA)
names(L) <- data$id

with(stack(L), data.frame(id = ind, string = values))

给予:

  id  string
1  A   1.001
2  A 123.123
3  B   23.45
4  C    <NA>
5  D     134
6  D    1.45

2) magrittr (1) 的这种变体将其写为 magrittr 管道。

library(magrittr)

data %>%
     transform(string = gsub(", ", " ", string)) %>%
     transform(string = gsub(",", ".", string)) %>%
     transform(string = trimws(string)) %>%
     with(setNames(strsplit(string, "\\s+"), id)) %>%
     lapply(grep, pattern = "^[0-9.]*$", value = TRUE) %>%
     replace(lengths(.) == 0, NA) %>%
     stack() %>%
     with(data.frame(id = ind, string = values))

3) dplyr/tidyr 这是使用 dplyr 和 tidyr 的替代管道解决方案。 unnest 转换为长格式,id 成为因素,以便我们稍后可以使用 complete 恢复被后续过滤删除的 id,过滤器删除垃圾行,complete 为每个不会出现的 id 插入 NA 行。

library(dplyr)
library(tidyr)

data %>%
  mutate(string = gsub(", ", " ", string)) %>%
  mutate(string = gsub(",", ".", string)) %>%
  mutate(string = trimws(string)) %>%
  mutate(string = strsplit(string, "\\s+")) %>%
  unnest() %>%
  mutate(id = factor(id))
  filter(grepl("^[0-9.]*$", string)) %>%
  complete(id)

4) data.table

library(data.table)

DT <- as.data.table(data)
DT[, string := gsub(", ", " ", string)][, 
     string := gsub(",", ".", string)][,
     string := trimws(string)][,
     string := setNames(strsplit(string, "\\s+"), id)][,
     list(string = list(grep("^[0-9.]*$", unlist(string), value = TRUE))), by = id][,
     list(string = if (length(unlist(string))) unlist(string) else NA_character_), by = id]
DT

更新 删除了垃圾词没有数字或点的假设。还添加了 (2)、(3) 和 (4) 以及一些改进。

关于regex - R - 提取所有匹配模式的字符串并创建关系表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38631811/

相关文章:

r - "Error in colnames"合并xts集时

r - 使用 fill() 函数将名称填充到列中

r - 使用 "cases"在大括号中显示线性方程组(R-markdown)

r - 在R dplyr中,为什么扩展函数后面有一个单引号?

r - 查找多列具有相同值的行

java - 否定字符串替换的正则表达式

javascript - 使用正则表达式 match.map 将 React <img> 插入现有字符串

python - 无法匹配 url 正则表达式中电子邮件地址后的尾随斜杠

javascript - 如何从字符串中删除空格和一组字符

R doMC 库 detectorCores 误报核心数量