根据匹配的模式替换匹配项

时间:2016-01-09 01:49:03

标签: regex r string substitution

给定一组正则表达式,是否有一种简单的方法来匹配多个模式,并根据匹配的模式替换匹配的文本?

例如,对于以下数据x,每个元素都以数字或字母开头,并以数字或字母结尾。我们将这些模式称为num_num(以数字开头,以数字结尾),num_let(以数字开头,以字母结尾),let_numlet_let

x <- c('123abc', '78fdsaq', 'aq12111', '1p33', '123', 'pzv')
type <- list(
  num_let='^\\d.*[[:alpha:]]$',
  num_num='^\\d(.*\\d)?$',
  let_num='^[[:alpha:]].*\\d$',
  let_let='^[[:alpha:]](.*[[:alpha:]])$'
)

要用它所遵循的模式的名称替换每个字符串,我们可以这样做:

m <- lapply(type, grep, x)
rep(names(type), sapply(m, length))[order(unlist(m))]
## [1] "num_let" "num_let" "let_num" "num_num" "num_num" "let_let"

有更有效的方法吗?

gsubfn

我知道使用gsubfn我们可以同时替换不同的匹配,例如:

library(gsubfn)
gsubfn('.*', list('1p33'='foo', '123abc'='bar'), x)
## [1] "bar"     "78fdsaq" "aq12111" "foo"     "123"     "pzv"

但我不确定替换是否可以依赖于匹配的模式而不是匹配本身。

stringr

str_replace_all与这个例子不太匹配,因为迭代会替换模式匹配,最后我们会用let_let覆盖所有内容:

library(stringr)
str_replace_all(x, setNames(names(type), unlist(type)))
## [1] "let_let" "let_let" "let_let" "let_let" "let_let" "let_let"

重新排序type以便首先显示与let_let对应的模式解决问题,但需要这样做会让我感到紧张。

type2 <- rev(type)
str_replace_all(x, setNames(names(type2), unlist(type2)))
## [1] "num_let" "num_let" "let_num" "num_num" "num_num" "let_let"

2 个答案:

答案 0 :(得分:2)

也许就是其中之一。

# base R method
mm2 <- character(length(x))
for( n in 1:length(type))  mm2 <- replace(mm2, grep(type[n],x), names(type)[n]) 

# purrr 0.2.0 method
library(purrr)
mm3 <- map(grep, .x=type, x = x) %>% (function(z) replace(x, flatten_int(z), rep(names(type), lengths(z))))

基本R方法比小型和大型数据集的发布代码快一些。 purrr方法比小数据集的发布代码慢,但与大数据集的基本R方法大致相同。

答案 1 :(得分:1)

stringr

如果我们更改了替换项,我们就可以使用str_replace_all,因此它们不再与任何正则表达式匹配,然后添加一个额外的替换项以将它们恢复为原始形式。例如

library(stringr)
type2 <- setNames(c(str_replace(names(type), "(.*)", "__\\1__"), "\\1"), 
                  c(unlist(type), "^__(.*)__$"))
str_replace_all(x, type2)
## [1] "num_let" "num_let" "let_num" "num_num" "num_num" "let_let"

grepl和tidyr

另一种方法首先匹配然后替换,一种方法是使用grepltidyr

library(plyr)
library(dplyr)
library(tidyr)

out <- data.frame(t(1*aaply(type, 1, grepl, x)))

out[out == 0] <- NA
out <- out %>% 
  mutate(id = 1:nrow(.)) %>%
  gather(name,value, -id, na.rm = T) %>%
  select(name)
as.character(out[,1])
## [1] "num_let" "num_let" "num_num" "num_num" "let_num" "let_let"

虽然这种方法看起来效率不高,但却可以轻松找到多于或少于一个匹配的行。

据我所知,替换匹配是在pcre2中实现的,我相信这种类型的问题可以直接在正则表达式中解决。不幸的是,似乎还没有人为R建立一个pcre2包。