首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在两个字符串中查找公共元素的相交。

在两个字符串中查找公共元素的相交。
EN

Stack Overflow用户
提问于 2022-04-27 01:53:05
回答 2查看 147关注 0票数 1

我正在寻找最快的方法来计数两个字符串中的公共元素。

字符串中的元素由|分隔。

模拟数据:

代码语言:javascript
运行
复制
library(data.table)
dt <- data.table(input1 = c("A|B", "C|D|", "R|S|T", "A|B"),
                 input2 = c("A|B|C|D|E|F", "C|D|E|F|G", "R|S|T", "X|Y|Z"))

计算字符串中的公共元素并创建dt$outcome

代码语言:javascript
运行
复制
dt <- transform(dt, var1 = I(strsplit(as.character(input1), "\\|")))
dt <- transform(dt, var2 = I(strsplit(as.character(input2), "\\|")))
dt <- transform(dt, outcome = mapply(function(x, y) sum(x%in%y),
                                 var1, var2))

结果:

代码语言:javascript
运行
复制
> dt
   input1      input2  var1        var2 outcome
1:    A|B A|B|C|D|E|F   A,B A,B,C,D,E,F       2
2:   C|D|   C|D|E|F|G   C,D   C,D,E,F,G       2
3:  R|S|T       R|S|T R,S,T       R,S,T       3
4:    A|B       X|Y|Z   A,B       X,Y,Z       0

这个例子很好用,但是实际数据中有数千个用于input1input2的元素,并且有超过20万行。因此,当前代码运行数天,无法投入生产。

我们怎样才能加快速度?

dt$var1dt$var2不是必需的输出,可以省略。

EN

Stack Overflow用户

发布于 2022-04-27 02:42:03

有两件事应该有帮助:

  1. 使用data.table的引用语义,特别是为了提高效率/速度。您对transform的使用大大降低了您的速度:

工作台:mark( base ={ bigdt <- transform(bigdt,var1 = I(strsplit(as.character(input1),“\x”);},datatable ={ bigdt,var1 := str拆分(input1,“\\”);(})##A类:2x13#表达式最小中值itr/sec mem_alloc gc/sec n_itr n_gc total_time结果内存时间gc # #1碱基2.69ms 3.44ms 271。299 3> 0 136 0 501 3> <3>~ 2.27 1 441 3>

  1. strsplit(., "\\|")转移到strsplit(., "|", fixed = TRUE),以减少regex的开销。

工作台:mark( regex = strsplit(bigdt$input1 1,“\”),fixed = strsplit(bigdt$input1 1,“AC.26”),( min = TRUE) ##A tibble: 2x13#表达式最小中值itr/sec mem_alloc gc/sec n_itr n_gc total_time结果内存时间gc # #1 regex 1.94ms 2.12ms 419。31.3KB 0 210 0 501 3>

(由于许多列通常有不同的单元,所以我倾向于将`itr/sec`视为相对性能的合理度量。)

结合这两种技术(包括onyambu的优秀建议),我们看到了一个巨大的改进:

代码语言:javascript
运行
复制
inputs <- c("input1", "input2")
vars <- c("var1", "var2")
bench::mark(OP = {
  bigdt <- transform(bigdt, var1 = I(strsplit(as.character(input1), "\\|")))
  bigdt <- transform(bigdt, var2 = I(strsplit(as.character(input2), "\\|")))
  bigdt <- transform(bigdt, outcome = mapply(function(x, y) sum(x%in%y), var1, var2))
},
r2evans = {
  bigdt[, (vars) := lapply(.SD, strsplit, "|", fixed = TRUE), .SDcols = inputs
       ][, outcome := mapply(function(x, y) sum(x %in% y), var1, var2)]
},
onyambu = {
  bigdt[, outcome:= lengths(stringr::str_extract_all(input2, sub('[|]$', '',input1)))]
}
)
# # A tibble: 3 x 13
#   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                   memory   time    gc    
#   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                   <list>   <list>  <list>
# 1 OP           18.8ms  20.95ms      43.7    1.21MB     2.30    19     1      435ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~
# 2 r2evans       7.5ms   8.42ms     105.   238.19KB     2.28    46     1      439ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~
# 3 onyambu      10.9ms  11.87ms      80.8  130.36KB     0       41     0      508ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~

这个比例是一致的。如果我使用类似的更大的表,也许

代码语言:javascript
运行
复制
bench::mark(...)
# # A tibble: 3 x 13
#   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                     memory   time   gc   
#   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                     <list>   <list> <lis>
# 1 OP            2.71s    2.71s     0.369   96.56MB     2.21     1     6      2.71s <data.table [400,000 x 5]> <Rprofm~ <benc~ <tib~
# 2 r2evans       1.38s    1.38s     0.723    17.8MB     2.17     1     3      1.38s <data.table [400,000 x 5]> <Rprofm~ <benc~ <tib~
# 3 onyambu       1.53s    1.53s     0.652    7.66MB     0        1     0      1.53s <data.table [400,000 x 5]> <Rprofm~ <benc~ <tib~

虽然只有一次迭代,但这两个建议的答案在基本情况下都有显著的速度改进。

如果我们调整onyambu的选择而不保存中间的var1var2值,我们可以改进一些,具体如下:

代码语言:javascript
运行
复制
# r2evans_2
bigdt[, outcome := mapply(function(x, y) sum(x %in% y), 
                          strsplit(input1, "|", fixed = TRUE), 
                          strsplit(input2, "|", fixed = TRUE)) ]
bench::mark(...)
# # A tibble: 4 x 13
#   expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                   memory   time    gc    
#   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                   <list>   <list>  <list>
# 1 OP          18.27ms  18.85ms      52.7    1.21MB   190.       5    18     94.9ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~
# 2 r2evans      7.28ms   8.18ms     123.   241.09KB   133.      24    26    195.7ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~
# 3 r2evans_2    6.61ms   7.56ms     134.   205.57KB   105.      33    26      247ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~
# 4 onyambu      10.7ms  12.21ms      82.8  110.88KB     2.02    41     1    495.2ms <data.table [4,000 x 5]> <Rprofm~ <bench~ <tibb~

像这样的代码优化问题的诀窍是从大问题到小问题。我觉得这是个好的开始。如果您需要更快,您可能需要转向编译或另一种语言,我不知道(随手)如何改进这一点。

数据,大于您的4行:

代码语言:javascript
运行
复制
bigdt <- rbindlist(replicate(1000, dt, simplify=FALSE))
biggerdt <- rbindlist(replicate(100000, dt, simplify=FALSE))
票数 2
EN
查看全部 2 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72022417

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档