str_replace_all迭代替换命名向量元素,但不是一次替换所有元素

nue99wik  于 2023-01-15  发布在  其他
关注(0)|答案(4)|浏览(117)

假设我有一个长字符串:pneumonultramicroscopicsilicovolcanoosis。我想使用stringr::str_replace_all来替换某些字母。根据文档,str_replace_all可以接受一个命名向量,并将名称替换为值。这对于一次替换很有效,但对于多次替换,它似乎是迭代进行的,因此结果是上一次迭代的替换。我不确定这是预期的行为。

library(tidyverse)
text_string = "developer"
text_string %>% 
  str_replace_all(c(e ="X")) #this works fine
[1] "dXvXlopXr"
text_string %>% 
  str_replace_all(c(e ="p", p = "e")) #not intended behaviour
[1] "develoeer"

预期结果:

[1] "dpvploepr"

我引入了一个新角色:

text_string %>% 
  str_replace_all(c(e ="X", p = "e", X = "p"))

这是一个可用的变通方案,但很难推广。这是一个错误还是我的期望是错误的?
我还希望能够同时用n个其他字母替换n个字母,最好使用两个向量(如"old"和"new")或一个命名向量作为输入。

  • reprex经过编辑,便于人类阅读 *
kpbwa7wx

kpbwa7wx1#

    • 2023年更新**

当我第一次回答这个问题的时候,我有一个拼凑在一起的R包,它只是在我的github上,从那时起,我已经对它进行了实质性的改进,现在它在CRAN上,甚至在其他包中使用。
readme和CRAN的文档都清楚地说明了这一点,但是我理解这个页面上的代码是多么有帮助。更新的用法是基于模式和替换的向量传递的。有一个循环选项允许你提供一个比模式列表更短的替换列表,并且只是在它之间循环。你也可以在后端向regexpr传递参数(例如fixed=TRUE

install.packages('mgsub')
mgsub("developer", 
      pattern = c("e", "p"), 
      replacements = c("p", "e"))
#> [1] "dpvploepr"
    • 原始答案**

我正在开发一个包来处理这种类型的问题。这比qdap::mgsub函数更安全,因为它不依赖于占位符。它完全支持regex作为匹配和替换。您提供一个命名列表,其中的名称是要匹配的字符串,它们的值是替换。

devtools::install_github("bmewing/mgsub")
library(mgsub)
mgsub("developer",list("e" ="p", "p" = "e"))
#> [1] "dpvploepr"

qdap::mgsub(c("e","p"),c("p","e"),"developer")
#> [1] "dpvploppr"
pieyvz9o

pieyvz9o2#

我的解决方法是利用str_replace_all可以将函数作为替换的输入这一事实。

library(stringr)
text_string = "developer"
pattern <- "p|e"
fun <- function(query) {
    if(query == "e") y <- "p"
    if(query == "p") y <- "e"
    return(y)
}

str_replace_all(text_string, pattern, fun)

当然,如果你需要放大,我会建议使用更复杂的函数。

ppcbkaq5

ppcbkaq53#

迭代行为是有意的。也就是说,我们可以使用编写自己的工作区。我将使用字符子集来替换。
在一个命名向量中,我们可以通过名称查找事物,并为每个名称获得一个替换值,这就像同时进行所有的替换一样。

rules <- c(a = "X", b = "Y", X = "a")
chars <- c("a", "a", "b", "X", "X")
rules[chars]
#>   a   a   b   X   X 
#> "X" "X" "Y" "a" "a"

这里,在rules向量中查找"a"得到"X",有效地将"a"替换为"X",其他字符也是如此。
一个问题是没有匹配的名称会产生NA

rules <- c(a = "X", b = "Y", X = "a")
chars <- c("a", "Y", "Z")
rules[chars]
#>    a <NA> <NA> 
#>  "X"   NA   NA

为了防止出现NA,我们可以扩展规则以包括任何新字符,以便字符被其自身替换。

rules <- c(a = "X", b = "Y", X = "a")
chars <- c("a", "Y", "Z")
no_rule <- chars[! chars %in% names(rules)]
rules2 <- c(rules, setNames(no_rule, no_rule))
rules2[chars]
#>   a   Y   Z 
#> "X" "Y" "Z"

这就是下面这个函数背后的逻辑。

  • 将字符串拆分为字符
  • 创建替换规则的完整列表
  • 查找替换值
  • 把绳子粘在一起
library(stringr)

str_replace_chars <- function(string, rules) {
  # Expand rules to replace characters with themselves 
  # if those characters do not have a replacement rule
  chars <- unique(unlist(strsplit(string, "")))
  complete_rules <- setNames(chars, chars)
  complete_rules[names(rules)] <- rules

  # Split each string into characters, replace and unsplit
  for (string_i in seq_along(string)) {
    chars_i <- unlist(strsplit(string[string_i], ""))
    string[string_i] <- paste0(complete_rules[chars_i], collapse = "")
  }
  string
}

rules <- c(a = "X", p = "e", e = "p")
string <- c("application", "developer")
str_replace_chars(string, rules)
#> [1] "XeelicXtion" "dpvploepr"
jdzmm42g

jdzmm42g4#

这个函数的作用可能有一个顺序,所以在用s替换所有的c之后,你用c替换所有的s,只剩下c。

long_string %>% str_replace_all(c(c ="X", s = "U"))  %>% str_replace_all(c(X ="s", U = "c"))

相关问题