我创建了一些代码,我需要在大约 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
在这种情况下,我不关心内存使用情况,只关心速度。有没有比复制方法更快地更新没有副本的原始表的方法?
最佳答案
这是一种接近它的语言方式。基本上,data.table很棒,并尽最大努力优化。但是,其他程序化欺骗,如 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
的源代码检测到和 data.table需要考虑到它。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/