【问题标题】:Left join using data.table使用 data.table 左连接
【发布时间】:2016-04-08 11:28:37
【问题描述】:

假设我有两个 data.table

答:

  A  B
1: 1 12
2: 2 13
3: 3 14
4: 4 15

乙:

   A  B
1: 2 13
2: 3 14

我有以下代码:

merge_test = merge(dataA, dataB, by="A", all.data=TRUE)

我明白了:

   A B.x B.y
1: 2  13  13
2: 3  14  14

但是,我想要最终合并表中 dataA 中的所有行。有没有办法做到这一点?

【问题讨论】:

  • 搜索应该会产生一些涵盖此内容的问题。这是一个:stackoverflow.com/questions/12773822/…
  • 如果要进行左连接,可以使用all.x = TRUE。如果要进行全外连接,可以使用all = TRUE
  • 从投票来看,也许考虑改变接受的答案?

标签: r join merge data.table


【解决方案1】:

如果您想将Bb 值添加到A,那么最好将AB 连接起来,并通过引用更新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))

【讨论】:

  • 很好的答案。只是为了确认一下,我假设“A[B, bb:=ib, on='a']”中的“i”指的是一般数据表“DT[i,j,by]”中的“i”语法?
  • @cbailiss 是的,i.b 意味着在使用连接更新A 时,它应该查看bB 列。以类似的方式,使用x. 前缀,您可以引用A 的列。
  • @Jaap 当创建了多个新列时,您将如何通过引用管理联接?这里创建了新列bb := i.b,正如您所说,它在Bdata.table 对应的i 中查找相应的b 列值。但是,当您有许多可能通过合并(通过引用)更大的data.tables 创建的新列时会发生什么?
  • @Prevost see here for an example,希望能回答你的问题
  • @Prevost tric 正在使用mget,另请参阅我之前评论链接下的答案的最后部分
【解决方案2】:

你可以试试这个:

# used data
# set the key in 'B' to the column which you use to join
A <- data.table(a = 1:4, b = 12:15)
B <- data.table(a = 2:3, b = 13:14, key = 'a') 

B[A]

【讨论】:

  • 如果一个数据表键是另一个数据表键的子集,则此答案可以正常工作。如果它们部分相交,是否有可能加入?例如,如果A, B 类似于:A &lt;- data.table(a = 1:4, b = 12:15) B &lt;- data.table(a = 2:5, c = 13:16)
【解决方案3】:

为了完整起见,我添加了table.express 版本的答案来回答您的问题。 table.express 很好地将 tidyverse 语言扩展到 data.table,使其成为快速处理大型数据集的便捷工具。这是使用上述问题中的数据集的解决方案:

merge_test = dataA %&gt;% left_join(dataB, by="A")

A left_join 将 dataA 中的所有行保留在连接数据集中。

注意:您必须加载包data.table table.express

【讨论】:

    猜你喜欢
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 1970-01-01
    • 2013-09-28
    相关资源
    最近更新 更多