根据匹配的模式替换匹配项
Replace matches according to the pattern that was matched
给定一组正则表达式,是否有一种简单的方法来匹配多个模式,并根据匹配的模式替换匹配的文本?
例如,对于以下数据x
,每个元素以数字或字母开头,以数字或字母结尾。我们称这些模式为 num_num
(以数字开头,以数字结尾)、num_let
(以数字开头、以字母结尾)、let_num
和 let_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"
也许是其中之一。
# 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 方法大致相同。
stringr
我们可以使用 str_replace_all
如果我们改变替换使它们不再与任何正则表达式匹配,然后添加一个额外的替换 return 它们到它们的原始形式。例如
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
另一种方法是先匹配再替换,一种方法是使用grepl
和tidyr
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 包。
给定一组正则表达式,是否有一种简单的方法来匹配多个模式,并根据匹配的模式替换匹配的文本?
例如,对于以下数据x
,每个元素以数字或字母开头,以数字或字母结尾。我们称这些模式为 num_num
(以数字开头,以数字结尾)、num_let
(以数字开头、以字母结尾)、let_num
和 let_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"
也许是其中之一。
# 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 方法大致相同。
stringr
我们可以使用 str_replace_all
如果我们改变替换使它们不再与任何正则表达式匹配,然后添加一个额外的替换 return 它们到它们的原始形式。例如
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
另一种方法是先匹配再替换,一种方法是使用grepl
和tidyr
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 包。