如果您想将B 的b 值添加到A,那么最好将A 与B 连接起来,并通过引用更新A,如下所示:
A[B, on = 'a', bb := i.b]
给出:
> A
a b bb
1: 1 12 NA
2: 2 13 13
3: 3 14 14
4: 4 15 NA
这是比使用B[A, on='a'] 更好的方法,因为后者只是将结果打印到控制台。当您想将结果返回到A 时,您需要使用A <- B[A, on='a'],它会得到相同的结果。
A[B, on = 'a', bb := i.b] 优于A <- B[A, on = 'a'] 的原因在于内存效率。对于A[B, on = 'a', bb := i.b],A 在内存中的位置保持不变:
> address(A)
[1] "0x102afa5d0"
> A[B, on = 'a', bb := i.b]
> address(A)
[1] "0x102afa5d0"
而另一方面,使用A <- B[A, on = 'a'],会创建一个新对象并将其保存在内存中作为A,因此在内存中有另一个位置:
> address(A)
[1] "0x102abae50"
> A <- B[A, on = 'a']
> address(A)
[1] "0x102aa7e30"
使用merge (merge.data.table) 会导致内存位置发生类似的变化:
> address(A)
[1] "0x111897e00"
> A <- merge(A, B, by = 'a', all.x = TRUE)
> address(A)
[1] "0x1118ab000"
为了提高内存效率,最好使用 'update-by-reference-join' 语法:
A[B, on = 'a', bb := i.b]
虽然这对像这样的小型数据集没有明显的区别,但它确实对设计data.table 的大型数据集产生了影响。
可能还值得一提的是A 的顺序保持不变。
要查看对速度和内存使用的影响,让我们用一些更大的数据集进行基准测试(有关数据,请参阅下面使用的数据部分的第二部分):
library(bench)
bm <- mark(AA <- BB[AA, on = .(aa)],
AA[BB, on = .(aa), cc := cc],
iterations = 1)
给出(仅显示相关的测量值):
> bm[,c(1,3,5)]
# A tibble: 2 x 3
expression median mem_alloc
<bch:expr> <bch:tm> <bch:byt>
1 AA <- BB[AA, on = .(aa)] 4.98s 4.1GB
2 AA[BB, on = .(aa), `:=`(cc, cc)] 560.88ms 384.6MB
因此,在此设置中,'update-by-reference-join' 的速度提高了大约 9 倍,内存消耗减少了 11 倍。
注意:速度和内存使用的增益可能在不同的设置中有所不同。
使用的数据:
# initial datasets
A <- data.table(a = 1:4, b = 12:15)
B <- data.table(a = 2:3, b = 13:14)
# large datasets for the benchmark
set.seed(2019)
AA <- data.table(aa = 1:1e8, bb = sample(12:19, 1e7, TRUE))
BB <- data.table(aa = sample(AA$a, 2e5), cc = sample(2:8, 2e5, TRUE))