为什么X [Y]连接data.tables不允许完全外连接或左连接?

时间:2012-10-07 23:46:04

标签: r join data.table

这是关于data.table连接语法的一个哲学问题。我发现data.tables的用途越来越多,但仍在学习......

data.tables的连接格式X[Y]非常简洁,方便和高效,但据我所知,它只支持内连接和右外连接。要获得左外连接或完全外连接,我需要使用merge

  • X[Y, nomatch = NA] - Y中的所有行 - 右外连接(默认)
  • X[Y, nomatch = 0] - 只有X和Y匹配的行 - 内连接
  • merge(X, Y, all = TRUE) - 来自X和Y的所有行 - 完全外连接
  • merge(X, Y, all.x = TRUE) - X中的所有行 - 左外连接

在我看来,如果X[Y]连接格式支持所有4种类型的连接,那将会很方便。有没有理由只支持两种类型的连接?

对我来说,nomatch = 0nomatch = NA参数值对于正在执行的操作不是非常直观。我更容易理解并记住merge语法:all = TRUEall.x = TRUEall.y = TRUE。由于X[Y]操作类似merge类似于match,因此为什么不使用merge语法进行连接而不是match函数的nomatch参数?

以下是4种连接类型的代码示例:

# sample X and Y data.tables
library(data.table)
X <- data.table(t = 1:4, a = (1:4)^2)
setkey(X, t)
X
#    t  a
# 1: 1  1
# 2: 2  4
# 3: 3  9
# 4: 4 16

Y <- data.table(t = 3:6, b = (3:6)^2)
setkey(Y, t)
Y
#    t  b
# 1: 3  9
# 2: 4 16
# 3: 5 25
# 4: 6 36

# all rows from Y - right outer join
X[Y]  # default
#  t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

X[Y, nomatch = NA]  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

merge(X, Y, by = "t", all.y = TRUE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

identical(X[Y], merge(X, Y, by = "t", all.y = TRUE))
# [1] TRUE

# only rows in both X and Y - inner join
X[Y, nomatch = 0]  
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t")  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t", all = FALSE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

identical( X[Y, nomatch = 0], merge(X, Y, by = "t", all = FALSE) )
# [1] TRUE

# all rows from X - left outer join
merge(X, Y, by = "t", all.x = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16

# all rows from both X and Y - full outer join
merge(X, Y, by = "t", all = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36

更新:data.table v1.9.6引入了on=语法,该语法允许在主键以外的字段上进行临时连接。问题jangorecki's answerHow to join (merge) data frames (inner, outer, left, right)?提供了data.table可以处理的其他连接类型的一些示例。

3 个答案:

答案 0 :(得分:64)

引用data.table FAQ 1.11 What is the difference between X[Y] and merge(X, Y)?

  

X[Y]是一个连接,使用Y(或Y的密钥,如果有的话)作为索引查找X的行。

     

Y[X]是一个连接,使用X(或X的密钥,如果有的话)查找Y的行

     

merge(X,Y)同时做两种方式。 X[Y]Y[X]的行数通常不同,而merge(X,Y)merge(Y,X)返回的行数相同。

     

但错过了重点。大多数任务都需要做一些事情   加入或合并后的数据。为什么要合并所有数据列,仅用于   之后使用它们中的一小部分?你可能会建议   merge(X[,ColsNeeded1],Y[,ColsNeeded2]),但这需要程序员确定需要哪些列。 data.table中的X[Y,j]只需一步即可完成所有操作   您。当您编写X[Y,sum(foo*bar)]时,data.table会自动检查j表达式以查看它使用的列。它只会仅对这些列进行子集化;其他人被忽略了。内存仅为j使用的列创建,Y列在每个组的上下文中享有标准R回收规则。假设foo位于X,栏位于Y(以及Y中的其他20列)。编程和运行速度是否X[Y,sum(foo*bar)]比合并浪费后跟子集的所有内容更快?


如果您想要X[Y]

的左外连接
le <- Y[X]
mallx <- merge(X, Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le, mallx)
# [1] TRUE

如果您想要完整的外部联接

# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
##   t  b  a
## 1: 1 NA  1
## 2: 2 NA  4
## 3: 3  9  9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA

# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]

答案 1 :(得分:21)

@ mnel的答案是现场,所以接受这个答案。这只是跟进,对于评论来说太长了。

正如mnel所说,左/右外连接是通过交换YXY[X] -vs- X[Y]获得的。因此,该语法支持4种连接类型中的3种,而不是2,iiuc。

添加第4个似乎是一个好主意。假设我们添加full=TRUEboth=TRUEmerge=TRUE(不确定最佳参数名称?)然后在X[Y,j,merge=TRUE]对{在FAQ 1.12中的BUT之后的原因。现在添加新功能请求并将其链接回来,谢谢:

FR#2301 : Add merge=TRUE argument for both X[Y] and Y[X] join like merge() does.

最新版本加快了merge.data.table(例如,在内部采用浅拷贝来更有效地设置密钥)。因此,我们正在努力让merge()X[Y]更加接近,并为用户提供所有选项以获得充分的灵活性。两者都有利弊。另一项出色的功能要求是:

FR#2033 : Add by.x and by.y to merge.data.table

如果还有其他人,请保持他们的到来。

问题的这一部分:

  

为什么不使用连接的合并语法而不是匹配函数的nomatch参数?

如果您更喜欢merge()语法及其3个参数allall.xall.y,那么只需使用X[Y]代替nomatch。认为它应涵盖所有情况。或者你的意思是为什么[.data.table中的论点是nomatch?如果是这样的话,这就是常见问题解答2.14:“你能进一步解释为什么data.table受到基础中A [B]语法的启发吗?”。但是,0目前只有两个值NAnomatch。这可以延长,以便负值意味着什么,或者12意味着使用第12行的值来填充NA,或者将来data.table可以是矢量,甚至可以是{{1}}

嗯。 by-without-by 如何与merge = TRUE交互?也许我们应该把它转到datatable-help

答案 2 :(得分:15)

此“回答”是供讨论的提案:如我的评论中所示,我建议在[.data.table()中添加join参数以启用其他类型的联接,即:{{1} }。除了4种类型的普通连接外,我还建议支持3种类型的独占连接,以及交叉连接。

建议各种连接类型的X[Y,j,join=string]字符串值(和别名):

  1. join"all.y" - 右连接,当前data.table默认值(nomatch = NA) -                           所有没有X匹配的具有NA的Y行;
  2. "right""both" - 内部联接(nomatch = 0) - 只有X和Y匹配的行;

  3. "inner""all.x" - 左连接 - X,NAs中没有Y匹配的所有行:

  4. "left""outer" - 完全外部联接 - 来自X和Y的所有行,不匹配的NAs

  5. "full""only.x" - 非连接或反连接返回没有Y匹配的X行

  6. "not.y""only.y" - 非联接或反联接返回没有X匹配的Y行
  7. "not.x" - 独占连接返回X和Y行,其中与其他表不匹配,即独占或(XOR)
  8. "not.both" - 交叉加入或笛卡儿积,每行X与Y的每一行匹配
  9. 默认值为"cross",对应于当前默认值。

    “all”,“all.x”和“all.y”字符串值对应join="all.y"个参数。 “右”,“左”,“内”和“外”字符串可能更适合SQL用户。

    “both”和“not.both”字符串是我目前最好的建议 - 但有人可能会为内部联接和独占联接提供更好的字符串建议。 (我不确定“独家”是否是正确的术语,如果有“XOR”加入的正确术语,请纠正我。)

    使用merge()join="not.y"X[-Y,j]非联接语法的替代方法,也许更清楚(对我而言),虽然我不确定它们是否相同( data.table版本1.8.3中的新功能。

    交叉连接有时候很方便,但它可能不适合data.table范例。