如何用 R 中的查找代码替换带有字符串的列
How to replace column with strings with look-up codes in R
想象一下,我有一个数据框或数据table,其中有一行如下所示的字符串列:
a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4
和查找 table,其中包含用于映射每个字符串的代码。例如:
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),因此非常感谢任何快速解决方案。
请注意,此字符串长度并不总是相同的......例如,在一行中我可以有字符串 a
到 d
,在其他 a
到 f
.
编辑:
我们得到了上述情况的解决方案,但是假设我有这样一个字符串:
a; b: peter, joe smith, john smith; c: luke, james, john smith
如何替换这些知道 john smith
可以有两个不同的代码,具体取决于它属于 b
还是 c
类别?
此外,字符串可以包含中间带有 space 的单词。
编辑 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
编辑 3 按照建议,我为下一期打开了一个新问题:
How to replace repeated strings and space in-between with look-up codes in R
我们可以使用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))
一个更快的替代方法是使用 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
这里有一些基本的 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))
想象一下,我有一个数据框或数据table,其中有一行如下所示的字符串列:
a1; b: b1, b2, b3; c: c1, c2, c3; d: d1, d2, d3, d4
和查找 table,其中包含用于映射每个字符串的代码。例如:
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),因此非常感谢任何快速解决方案。
请注意,此字符串长度并不总是相同的......例如,在一行中我可以有字符串 a
到 d
,在其他 a
到 f
.
编辑:
我们得到了上述情况的解决方案,但是假设我有这样一个字符串:
a; b: peter, joe smith, john smith; c: luke, james, john smith
如何替换这些知道 john smith
可以有两个不同的代码,具体取决于它属于 b
还是 c
类别?
此外,字符串可以包含中间带有 space 的单词。
编辑 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
编辑 3 按照建议,我为下一期打开了一个新问题: How to replace repeated strings and space in-between with look-up codes in R
我们可以使用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))
一个更快的替代方法是使用 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
这里有一些基本的 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))