如何用列表中的匹配字符串替换字符串?

时间:2018-12-12 17:01:05

标签: r regex string replace matching

说我有一列df1$z,其中包含一些“脏”字符串

> df1$z
 [1] alpha uybkh   kilo-mdjfyrs  lima qxaucnpe gamma-qpnej  
 [5] beta-okmwy    beta-uybkh    gamma mdjfyrs lima qxaucnpe
 [9] beta qpnej    kilo okmwy   
9 Levels: alpha uybkh beta-okmwy beta-uybkh ... lima qxaucnpe

某些字符串以包含在另一个向量a中的模式开头。

> a
[1] "alpha" "beta"  "gamma"

这些a中与z匹配的字符串要替换为向量a的相应模式,以便得到以下结果:

# [1] "alpha"         "kilo-mdjfyrs"  "lima qxaucnpe" "gamma"        
# [5] "beta"          "beta"          "gamma"         "lima qxaucnpe"
# [9] "beta"          "kilo okmwy" 

我写了一个函数使我接近,但是它不能一次替换字符串,而且我无法将所有东西放在一起:

> lapply(seq_along(a), function(x) {z[grep(paste0("^", a[x]), z)] <- a[x]; z})
[[1]]
 [1] "beta sfrmyijl" "lima-xudwfkm"  "lima-kirvpys"  "gamma wriygcb"
 [5] "alpha"         "alpha"         "kilo xudwfkm"  "alpha"        
 [9] "gamma wriygcb" "kilo-wvxgar"  

[[2]]
 [1] "beta"           "lima-xudwfkm"   "lima-kirvpys"   "gamma wriygcb" 
 [5] "alpha wvxgar"   "alpha-sfrmyijl" "kilo xudwfkm"   "alpha-kirvpys" 
 [9] "gamma wriygcb"  "kilo-wvxgar"   

[[3]]
 [1] "beta sfrmyijl"  "lima-xudwfkm"   "lima-kirvpys"   "gamma"         
 [5] "alpha wvxgar"   "alpha-sfrmyijl" "kilo xudwfkm"   "alpha-kirvpys" 
 [9] "gamma"          "kilo-wvxgar"   

我也尝试了一些我认为可能有用的mapply()方法,并调查了一些现有的答案,例如this one,但我无法适应我的特定问题。

那么我将如何以有效的 base R 方式进行此操作? 注意,应将替换项放回数据帧df1中,而不会干扰行的顺序。

数据

a <- c("alpha", "beta", "gamma")
set.seed(105056)
z <- paste0(sample(c(a, "kilo", "lima"), 10, replace=TRUE), 
            sample(c("-", " "), 10, replace=TRUE), 
            replicate(5, paste0(sample(letters, sample(5:9)), collapse="")))
df1 <- data.frame(z, x=rnorm(10))

3 个答案:

答案 0 :(得分:1)

您可以使用以下sub解决方案:

> sub(paste0(".*\\b(",paste(a, collapse="|"),")\\b.*"), "\\1", df1$z)
 [1] "alpha"         "kilo-mdjfyrs"  "lima qxaucnpe" "gamma"         "beta"         
 [6] "beta"          "gamma"         "lima qxaucnpe" "beta"          "kilo okmwy"

该模式将匹配a向量中关键字之前和之后的所有字符,并将关键字捕获到组1中,而\1替换模式将仅保留找到的关键字并丢弃之前的所有文本之后。如果没有匹配项,则不会有任何变化。

请参见regex demo

答案 1 :(得分:0)

我们可以使用sub。从'a'中创建单个字符串后,使用paste创建模式,然后使用它来捕获替换中带有后向引用(\\1)的模式

sub(paste0(".*\\b(", paste(a, collapse="|"), ")\\b.*"), "\\1", df1$z)
#[1] "alpha"         "kilo-mdjfyrs"  "lima qxaucnpe" "gamma"         "beta"          "beta"          "gamma"        
#[8] "lima qxaucnpe" "beta"          "kilo okmwy"   

注意:sub解决方案首先在此处发布


或使用str_replace中的stringr

library(tidyverse)
df1 %>% 
  mutate(z = str_replace(z, 
      paste0(".*\\b(", paste(a, collapse="|"), ")\\b.*"), "\\1"))
#           z           x
#1          alpha -0.18973111
#2   kilo-mdjfyrs -0.88150363
#3  lima qxaucnpe  0.01665189
#4          gamma  0.62647841
#5           beta -0.29526632
#6           beta  0.42480082
#7          gamma  1.03653486
#8  lima qxaucnpe -1.51910745
#9           beta  1.21504343
#10    kilo okmwy  1.25321421

答案 2 :(得分:0)

使用ifelsegrepl,这是一个更长一些但不透明的解决方案:

df1$z <- ifelse(grepl("alpha.*", df1$z), a[1],
            ifelse(grepl("beta.*", df1$z), a[2],
                   ifelse(grepl("gamma.*", df1$z), a[3], as.character(df1$z))))
df1
               z           x
1          alpha -0.18973111
2   kilo-mdjfyrs -0.88150363
3  lima qxaucnpe  0.01665189
4          gamma  0.62647841
5           beta -0.29526632
6           beta  0.42480082
7          gamma  1.03653486
8  lima qxaucnpe -1.51910745
9           beta  1.21504343
10    kilo okmwy  1.25321421