【问题标题】:How to replace column with strings with look-up codes in R如何用R中的查找代码用字符串替换列
【发布时间】:2020-03-19 22:01:02
【问题描述】:

想象一下,我有一个带有字符串列的数据框或数据表,其中一行如下所示:

a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4

还有一个查找表,其中包含用于映射每个字符串的代码。例如:

string code
a1     10
b1     20
b2     30
b3     40
c1     50
c2     60
...

我想要一个将这个字符串映射到代码的映射函数:

10; b: 20, 30, 40; c: 50, 60, 70; d: 80, 90, 100

我在 data.table/data.frame 中有一列这些字符串(超过 100k),因此非常感谢任何快速的解决方案。 请注意,此字符串长度并不总是相同的...例如,在一行中我可以有字符串ad,在其他af

编辑

我们得到了上述情况的解决方案,但是想象一下我有一个这样的字符串:

a; b: peter, joe smith, john smith; c: luke, james, john smith

如何替换这些已知john smith 可以有两个不同的代码,具体取决于它属于b 还是c 类别? 此外,字符串可以包含单词,它们之间有空格。

编辑 2

   string     code
    a          10
    peter      20
    joe smith  30
    john smith 40
    luke       50
    james      60
    john smith 70
...

最终的解决方案是:

10; b: 20, 30, 40; c: 50, 60, 70

EDIT 3 正如建议的那样,我为下一期提出了一个新问题: How to replace repeated strings and space in-between with look-up codes in R

【问题讨论】:

  • lookup 函数来自 epicalc 包可能会满足您的需求
  • 新数据的预期输出是什么
  • @akrun 请参阅编辑 2。

标签: r dataframe data.table str-replace gsub


【解决方案1】:

我们可以使用gsubfn

library(gsubfn)
gsubfn("([a-z]\\d+)", setNames(as.list(df1$code), df1$string), str1)
#[1] "10; b: 20, 30, 40; c: 50, 60, 70; d: 80, 90, 100, 110"

对于修改后的版本

gsubfn("(\\w+ ?\\w+?)",  setNames(as.list(df2$code), df2$string), str2)
#[1] "a; b: 20, 30, 40; c: 50, 60, 40"

数据

str1 <- "a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4"
df1 <- structure(list(string = c("a1", "b1", "b2", "b3", "c1", "c2", 
 "c3", "d1", "d2", "d3", "d4"), code = c(10L, 20L, 30L, 40L, 50L, 
 60L, 70L, 80L, 90L, 100L, 110L)), class = "data.frame",
  row.names = c(NA, -11L))

str2 <- "a; b: peter, joe smith, john smith; c: luke, james, john smith"

df2 <- structure(list(string = c("a", "peter", "joe smith", "john smith", 
"luke", "james", "john smith"), code = c(10L, 20L, 30L, 40L, 
50L, 60L, 70L)), class = "data.frame", row.names = c(NA, -7L))

【讨论】:

  • 当我将[a-z]\\d+ 替换为\\w+ 时,它起作用了。谢谢!
  • @Makaroni 不是。可能有重复值的键
  • 正如@akrun 所指出的,您不能有重复的键,但如果适用于您的问题,您可以按组(即查找表中的三列,即组、字符串、代码)设置键。
  • @Makaroni - 编辑意味着问题已经超出了您原始问题的范围。我认为最好发布一个新问题。
  • @Makaroni 我认为最好将其作为新问题发布,因为根据您的原始问题有 3 个答案,如果您更改帖子,这也意味着解决方案毫无意义。跨度>
【解决方案2】:

一个更快的选择是使用stringr::str_replace_all():

library(stringr)
library(gsubfn)

mystring <- "a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4"
mystrings <- rep(mystring, 10000)

str_replace_all(mystrings, setNames(as.character(df$code), df$string))

microbenchmark::microbenchmark(gsubfn = gsubfn("([a-z]\\d+)", setNames(as.list(df$code), df$string), mystrings),
                               stringr = str_replace_all(mystrings, setNames(as.character(df$code), df$string)), check = "equal", times = 50)

Unit: milliseconds
    expr        min         lq      mean     median         uq        max neval cld
  gsubfn 4846.19633 5584.54845 5923.5042 5939.49794 6261.29821 7479.04022    50   b
 stringr   29.01798   29.94274   31.6118   30.80002   31.72871   50.57533    50  a 

【讨论】:

    【解决方案3】:

    这里有一些基本的 R 解决方案。

    • 方法一:使用Reduce
    res <- Reduce(function(x,k) gsub(df$string[k],df$code[k],x),
                  c(s,as.list(1:nrow(df))))
    

    这样

    > res
    [1] "10; b: 20, 30, 40; c: 50, 60, c3; d: d1, d2, d3, d4"
    
    • 方法二: 定义自定义递归函数f来实现
    f <- function(k) ifelse(k==0,s,gsub(df$string[k],df$code[k],f(k-1)))
    res <- f(nrow(df))
    

    这样

    > res
    [1] "10; b: 20, 30, 40; c: 50, 60, c3; d: d1, d2, d3, d4"
    

    数据

    s <- "a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4"
    df <-structure(list(string = c("a1", "b1", "b2", "b3", "c1", "c2"), 
        code = c(10L, 20L, 30L, 40L, 50L, 60L)), class = "data.frame", row.names = c(NA, 
    -6L))
    

    【讨论】:

      猜你喜欢
      • 2017-06-15
      • 2018-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-28
      • 1970-01-01
      • 2015-06-11
      • 2022-06-14
      相关资源
      最近更新 更多