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

How to replace strings with the matching string from a list?

假设我有一个列 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))

我们可以使用 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解决方案首先发布在这里


或使用 stringr

中的 str_replace
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

您可以使用以下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 组,而 替换模式将仅保留找到的关键字并丢弃所有它前后的文本。如果没有匹配,则不会有任何变化。

参见regex demo

这里有一个更长但更不透明的解决方案,使用 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