使用dplyr和data.table进行左连接得到不同结果

5

我在想为什么使用data.table进行左连接时未能获取多个匹配项,似乎有一些奇怪的内置“无重复项”功能,这实际上并不是一个左连接,对吗?

数据:

test=data.table(mtcars[1,])
test2=data.table(mtcars[c(1,1),])

data.table:

test[test2, on = c(carb = "carb"), wt2 := i.wt]

dplyr:

test %>% left_join(test2 %>% select(carb, wt) %>% rename(wt2 = wt), 
                       by = "carb")

dplyr给出了正确的两行结果,但是data.table只给出了一行。这是怎么回事?


不应该是 test2[test, on = c(carb = "carb"), wt2 := i.wt] 吗? - AnilGoyal
@AnilGoyal:不,这个例子中没有。dplyr的join等同于left_join(test, test2),data.table也是一样。 - Helen
请参阅R4DS的第13.4.4节(由tidyverse的作者编写),它解释了tidyr如何使用笛卡尔积来评估多对多关系,即所有可能的组合。我对data.table不是很了解,但似乎在这种情况下它可能会有所不同。 - AnilGoyal
@AnilGoyal 嗯,使用tidyr/tidyverse/dplyr的left_join效果很好。这确实是一个左连接,所以我认为问题不在于此。dplyr按预期工作。问题在于data.table不能像左连接一样工作,但声称这是data.table的左连接,这是错误的。 - Helen
请参考用户@Jaap在此处的答案:https://dev59.com/tFsW5IYBdhLWcg3wwZnG#34600831 - Helen
2个回答

6
参考 @Frank 的回答: Which data.table syntax for left join (one column) to prefer
以及 @Jaap 的回答: left join using data.table
我认为这对于真正寻找左连接并来到社区寻求答案的人来说是危险的领域。链接中提供的按引用更新示例(使用 := 解决方案)实际上不是左连接。左连接返回左表中的所有记录和右表中匹配的记录,但我们正在讨论的这个 data.table 语法实际上并没有返回右表中所有匹配的记录,因此不是左连接。
data.table 文档清楚地说明了左连接是:
X[DT, on="x"] # left join

将会产生正确的data.table,满足 OP 的需求:

test[test2, on="carb"] # or,
test[test2[, wt2 := wt][,c("carb", "wt2")], on="carb"] # left join

@Jaap在他的回答中解释了使用:=符号引用连接和常规左连接之间的区别:

虽然对于像这样的小数据集,这并没有什么明显的区别,但是对于data.table专门设计的大型数据集来说确实会有所不同。

我的看法是,这里有两个令人担忧的陈述。一个陈述认为确实有明显的差异-在结果上-因为使用这两种方法得到的结果是不同的(尽管我理解他谈论的是速度,编辑:正如@jangorecki所指出的,这与速度无关,而与内存使用有关。通过引用更新不会在内存中创建第二个对象)。第二件事是假设(在我的眼中是尊重的),如果一个人在处理大型数据集,并且打算进行左连接,那么我从来没有见过任何不想要右表中所有匹配项的人。
我不知道如何在"data.table"中使用更新引用进行左连接。我的推理是,:=仅在j中定义(data.table语法)。它通过引用添加、更新或删除列。它根本不会复制任何内存的一部分。但由于我们可能需要向左侧表添加额外的行,如果我们在右侧表中获得多个匹配项(如果我们打算进行实际的左连接),所以我认为不能使用:=运算符,因为它仅作用于列。

1
@jangorecki,这就是你给负评的原因吗?在试图揭示一个重要问题时,这是实际的尝试。我更愿意礼貌地指出这一点,以便我可以将其删除。 - Helen
这不是downvoting的原因。好的答案应该从逻辑角度解释为什么update-on-join(使用:=)不能有多个匹配项(与DT无关,sqlite也是如此)。否则,用户将不理解为什么会这样,并且他们只会尝试记住何时使用哪个接口(与DT有关)。我期待着upvote,但对于这样一个好问题,我期望得到好的答案。 - jangorecki
1
@jangorecki 好的,我会更好地解释一下,谢谢你指出来。 - Helen
1
https://github.com/Rdatatable/data.table/issues/3747 这可能会有所帮助,而https://rdatatable.gitlab.io/data.table/library/data.table/html/assign.html 也解释了这种行为。 - jangorecki
@jangorecki:我已经尽力解释了(据我所知)为什么通过引用进行实际的左连接会很困难。不确定是否完全正确,但我已经尝试了。如果您和其他人认为我的问题确实很好,那么为什么社区没有给我原始问题点赞呢?我并不是说我有答案,我只是好奇,即使我可能会冒犯我链接到的某些答案,我的道歉,但我更希望有一个良好的讨论,人们可以向我解释我的推理何处有缺陷。谢谢提供的链接,我会看一下! - Helen
显示剩余4条评论

2
< p > 等效于 dplyr 连接操作

test %>% left_join(test2 %>% select(carb, wt) %>% rename(wt2 = wt), 
                       by = "carb")

data.table 中,它是
test[test2[, .(carb, wt2 = wt)], on = 'carb']

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