【问题标题】:R - Replacing part of a string with a for loop and gsubR - 用 for 循环和 gsub 替换字符串的一部分
【发布时间】:2019-07-22 10:51:29
【问题描述】:

我有两个数据框(df1 和 df2),我想用 df2 中的相应字符串替换 df1 中的部分字符串。

例如:结果应该是df3

a <- c("extra text test-ID 1", "extra text test-ID 2", "extra text test-ID 3", "extra text test-ID 4")
b <- c("experiment 5","experiment 6","experiment 7","experiment 8") 
c <- c("exercise 9","exercise 10","exercise 11","exercise 12")

df1 <- data.frame(a,b,c)
names(df1) <- c('a','b','c')

d <- c("test-ID 1", "test-ID 2", "test-ID 4")
e <- c("test-ID 1098", "test-ID 245", "test-ID 77")

df2 <- data.frame(d,e)
names(df2) <- c('a','b')

df1
df2

f <- c("extra text test-ID 1098", "extra text test-ID 245", "extra text test-ID 3", "extra text test-ID 77")
g <- c("experiment 5","experiment 6","experiment 7","experiment 8") 
h <- c("exercise 9","exercise 10","exercise 11","exercise 12")

df3 <- data.frame(f,g,h)
names(df3) <- c('a','b','c')
df3

我想用一个函数来执行这个。

replacefunction <- function(x) {
  cat(paste("searching for ", x, "\n"))
  for (i in seq_along(df2$a)) {
    old <- df2$a[i]
    new <- df2$b[i]
    if (grepl(old, x)) { 
      cat(paste0('found ', '"', old, '"', "\n"))
      return(gsub(old, new, x))
    }
  }
}

但是,这会发出警告:

df4 <- replace_values(df1$a)

Warning message:
In if (grepl(old, x)) { :
  the condition has length > 1 and only the first element will be used

只有 df1$a 列中的第一个条目被更改,为什么会发生这种情况?

【问题讨论】:

  • 我并不完全遵循您希望如何从一个数据帧到下一个数据帧的逻辑。但是,当您在具有多个元素的向量上使用 if ... else 语句时,就会出现该警告。这错过了 R 的优点之一,即vectorized operations。像grepl 这样的函数将作用于整个向量——不需要循环一次遍历一个元素
  • @camille grepl 本身在这里并没有多大帮助,因为有多个替换以及多个要匹配的模式。
  • 确实如此。就像我说的,我没有得到从一个字符串到下一个字符串的所有逻辑。但是grepl 是引发此警告的原因,所以这似乎是使用if ... else 的一个实例,它不能代替像ifelse 这样的矢量化函数。除非目的是查找字符串是否在 any 元素中,在这种情况下 ifelse(any(grepl(...)) 可能是它?
  • 感谢您的快速回复。是的,目的是在任何元素中查找字符串,但是 ifelse(any(grepl(...)) 会产生相同的错误

标签: r string loops replace gsub


【解决方案1】:

这是一种基本的 R 方法,主要依赖于 apply 函数。我几乎喜欢我的回答,只是我不得不在对sapply 的最内层调用中使用显式的for 循环。此循环遍历 df2 模式/替换数据帧的所有行,并尝试对输入数据帧 df1 的每个元素进行替换。

d <- c("test-ID 1", "test-ID 2", "test-ID 4")
d <- paste0("\\b", d, "\\b")
e <- c("test-ID 1098", "test-ID 245", "test-ID 77")

df2 <- data.frame(d,e)
names(df2) <- c('a','b')

df1
df1[] <- lapply(df1, function(x) {    # apply function(x) to each element of df1
    sapply(x, function(y) {
        for (i in 1:nrow(df2)) {
            y <- gsub(df2[i, "a"], df2[i, "b"], y)
        }
        return(y)
    })
})

df1

                     a            b           c
1 extra text test-ID 1 experiment 5  exercise 9
2 extra text test-ID 2 experiment 6 exercise 10
3 extra text test-ID 3 experiment 7 exercise 11
4 extra text test-ID 4 experiment 8 exercise 12
                        a            b           c
1 extra text test-ID 1098 experiment 5  exercise 9
2  extra text test-ID 245 experiment 6 exercise 10
3    extra text test-ID 3 experiment 7 exercise 11
4   extra text test-ID 77 experiment 8 exercise 12

我使用sub 来处理替换逻辑,因为这适用于正则表达式模式,所以我用单词边界包围了你的目标模式。也就是说,我匹配\btest-ID 1\b 而不仅仅是test-ID 1,如果它作为子字符串出现在其他文本中,后者也将匹配该术语。

【讨论】:

  • 到目前为止,这似乎是解决方案。我正在对我的原始数据集进行采样以进行验证。谢谢。
  • H Rene,网站上有一个名为 @akrun 的 R 用户可能会给出更好的答案。这是我能做的最好的:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-18
  • 1970-01-01
  • 2020-01-22
  • 2021-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多