如何在连接中更新两个data.table

6

假设我想要追踪一个数据表中的哪些行已被合并到另一个数据表中。是否有一种同时/在合并过程中达成此目的的方法?请参见下面的示例和我通常所采用的方式。不过,这种方法似乎效率不高。

示例

library(data.table)

# initial data
DT = data.table(x = c(1,1,1,2,2,1,1,2,2), 
                y = c(1,3,6))

# data to merge
DTx <- data.table(x = 1:3,
                  y = 1,
                  k = "X")

# regular update join
copy(DT)[DTx,
         on = .(x, y),
         k := i.k][]
#>    x y    k
#> 1: 1 1    X
#> 2: 1 3 <NA>
#> 3: 1 6 <NA>
#> 4: 2 1    X
#> 5: 2 3 <NA>
#> 6: 1 6 <NA>
#> 7: 1 1    X
#> 8: 2 3 <NA>
#> 9: 2 6 <NA>

# DTx remains the same
DTx
#>    x y k
#> 1: 1 1 X
#> 2: 2 1 X
#> 3: 3 1 X

我通常做什么:

# set an Id variable
DTx[, Id := .I]

# assign the Id in merge
DT[DTx,
   on = .(x, y),
   `:=`(k = i.k,
        matched_id = i.Id)][]
#>    x y    k matched_id
#> 1: 1 1    X          1
#> 2: 1 3 <NA>         NA
#> 3: 1 6 <NA>         NA
#> 4: 2 1    X          2
#> 5: 2 3 <NA>         NA
#> 6: 1 6 <NA>         NA
#> 7: 1 1    X          1
#> 8: 2 3 <NA>         NA
#> 9: 2 6 <NA>         NA

# use matched_id to find merged rows
DTx[, matched := fifelse(Id %in% DT$matched_id, TRUE, FALSE)]
DTx
#>    x y k Id matched
#> 1: 1 1 X  1    TRUE
#> 2: 2 1 X  2    TRUE
#> 3: 3 1 X  3   FALSE

我还没有看到任何合并/连接机制支持在原地修改两个数据框,即使使用data.table的引用语义。我认为你目前使用的matched := ...是我能想到的最好的方法。 - r2evans
2
(虽然在这里实际上不需要使用 fifelse ... 只需使用 matched := Id %in% DT$matched_id 即可。) - r2evans
2
为了在两侧匹配,您需要访问内部函数。目前我不确定是否有一个可以轻松获取的函数。请查看merglist PR https://github.com/Rdatatable/data.table/pull/4370,查看dtmerge函数。 - jangorecki
谢谢,非常有趣的公关!你的解决方案(虚拟代码)会是什么样子?只是出于好奇:实现我想要的功能有多难?我猜在加入过程中这些信息应该是可以获得的,但只是没有被使用?或者,基于您的深厚知识,这将需要完全不同的连接逻辑。考虑自己发起一个公关... - mnist
@mnist 我投票支持保持这个问题开放:审核链接。还需要一个评审者的支持来保持它的开放状态,以使其从关闭投票审核队列中移除。 - karel
显示剩余5条评论
1个回答

1
根据Jan的评论:

这将为您提供匹配行的索引,但是您将不得不再次调用merge来执行实际合并,除非您手动使用提供的索引来匹配/更新那些表。

您可以提取这些索引:

merge_metaDT = DT[DTx, on=.(x, y), .(irow = .GRP, xrow = .I), by=.EACHI]

   x y irow xrow
1: 1 1    1    1
2: 1 1    1    7
3: 2 1    2    4
4: 3 1    3    0

然后,使用索引而不是再次合并或匹配第二次来编辑每个表:
rowDT = merge_metaDT[xrow != 0L]
DT[rowDT$xrow, k := DTx[rowDT$irow, k]]
DTx[, matched := FALSE][rowDT$irow, matched := TRUE]

它是如何工作的:
  • 加入时,x[i],符号.I索引x的行
  • 在使用by=.EACHI进行连接分组时,.GRP索引每个组,这意味着此处的每一行i
  • 我们删除编码为零的非匹配值.I
关于最后一点,我们可能期望返回NAs而不是零,就像DT[DTx,on=.(x,y),which=TRUE]返回的那样。我不确定为什么会有区别。
假设我想追踪一个数据表中的哪些行被合并到另一个数据表中。有没有一种同时/在合并时完成这个任务的方法?[...]看起来相当低效。
我认为这比多次合并或使用%in%更有效,尤其是在合并操作足够耗费时间的情况下。
它仍然需要多个步骤。我怀疑是否有任何绕过这个问题的方法,因为很难想出易于理解的更新逻辑和语法。
在基本的R中,更新逻辑已经很复杂了,允许对单个索引进行多次编辑:
> x = c(1, 2, 3)
> x[c(1, 1)] = c(4, 5)
> x
[1] 5 2 3

还有一个问题是如何同时匹配和编辑多个索引:
> x = c(1, 1, 3)
> x[match(c(1, 3), x)] = c(4, 5)
> x
[1] 4 1 5

在data.table更新中,后一个问题使用mult=来处理。在更新两个表的情况下,这些问题会变得更加复杂。

1
感谢您的回答,很抱歉只点了赞而没有留言。这种方法非常有帮助!但它并不是我正在寻找的解决方案,而是一种(更有效的)变通方法。因此,我不会接受它,因为未来可能会有“真正”的解决方案。 - mnist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接