r - 如何比浅拷贝更快地通过引用进行更新?

标签 r data.table

我创建了一些代码,我需要在大约 100 万行的循环中进行大量连接。起初我使用左连接制作浅拷贝,但我更改了代码以进行更新连接,它变得很慢。
部分测试数据

a=data.table(id=1:10^6)
b=data.table(id=1:10^6, t='y')

cols=setdiff(names(b),names(a))
c=copy(b[a,on=.(id)])
a[b,(cols):=mget(cols),on=.(id)]
all.equal(a,c)
[1]TRUE
我不知道这是否是比较时间的正确方法,但它是
浅拷贝
microbenchmark(c=b[a,on=.(id)])
Unit: milliseconds
expr      min      lq     mean   median       uq      max neval
c 120.5911 122.734 128.1071 124.0922 126.1449 179.0127   100
更新数据表
microbenchmark(a[b,(cols):=mget(cols),on=.(id)])
Unit: milliseconds
expr      min       lq     mean   median       uq      max
a[b, `:=`((cols), mget(cols)), on = .(id)] 159.6128 162.9798 175.9855 168.5807 184.8756 308.5406
neval
100
虽然我需要一个解决方案来更新多个列,但不知道为什么,使用 mget比下面的方法慢
microbenchmark(a[b, t:=i.t, on=.(id)])
Unit: milliseconds
expr      min       lq     mean   median      uq      max neval
a[b, `:=`(t, i.t), on = .(id)] 138.0173 140.4671 146.3558 141.6204 143.281 185.1488   100
在这种情况下,我不关心内存使用情况,只关心速度。有没有比复制方法更快地更新没有副本的原始表的方法?

最佳答案

这是一种接近它的语言方式。基本上,很棒,并尽最大努力优化。但是,其他程序化欺骗,如 mget使 data.table效率较低,因为它必须假设 j表达式可以包含任何列,因此它需要分配内存来计算。
这翻译 mget使用语言了解实际发生的事情:a[b, (cols) = list(t)] .

nms = lapply(cols, as.name)
eval(substitute(a[b, (cols):= .x, on = .(id)], list(.x = as.call(c(quote(list), nms)))))

## performance against mget()

bench::mark(lang = {
  nms = lapply(cols, as.name)
  eval(substitute(a[b, (cols):= .x, on = .(id),], list(.x = as.call(c(quote(list), nms)))))
}
, mget_approach = a[b,(cols):=mget(cols),on=.(id)]
, normal =  b2[a2,on=.(id)]
  )

# A tibble: 3 x 13
  expression        min  median `itr/sec` mem_alloc `gc/sec` n_itr
  <bch:expr>    <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl> <int>
1 lang            157ms   160ms      5.97      23MB     1.99     3
2 mget_approach   244ms   255ms      3.92    57.3MB     5.87     2
3 normal          162ms   174ms      5.82    34.4MB     3.88     3

## additional data to make above work: a2 = copy(a); b2 = copy(b)
这是哪里mget的源代码检测到和 需要考虑到它。
https://github.com/Rdatatable/data.table/blob/94a12475f737892c542d3cb7daf42e534ea13a22/R/data.table.R#L1044-L1049
    # added 'mget' - fix for #994
    if (any(c("get", "mget") %chin% av)){
      if (verbose)
        catf("'(m)get' found in j. ansvars being set to all columns. Use .SDcols or a single j=eval(macro) instead. Both will detect the columns used which is important for efficiency.\nOld ansvars: %s \n", brackify(ansvars))
        # get('varname') is too difficult to detect which columns are used in general
        # eval(macro) column names are detected via the  if jsub[[1]]==eval switch earlier above.

关于r - 如何比浅拷贝更快地通过引用进行更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68750548/

相关文章:

r - 使用 stargazer 分析包含时间序列的数据帧

javascript - 将过滤器选择调整/更新为 Shiny 的 DT 数据表中已应用的过滤器

r - 使用 R 打开多个文件并分配给各个变量

R数据.表: In-memory left join multiple columns from left and right side

r - 如何根据第二个数据帧中的一系列可能值合并两个数据帧,但保留第一个数据帧的值?

R应用对数尺度时的选择性有限数误差

R-连分数

r - 通过查找按组快速 data.table 分配多列

r - 带有空格的列名进行的data.table操作失败

r - 使用fread读取带有双引号和不正确转义字符的数据