为什么data.tables的X 连接不允许完整的外连接或左连接?

这是关于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种types的连接,这将是方便的。 是否有一个原因只支持两种types的连接?

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

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

 # sample X and Y data.tables library(data.table) X <- data.table(t = 1:4, a = (1:4)^2) setkey(X, t) X # ta # 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 # tb # 1: 3 9 # 2: 4 16 # 3: 5 25 # 4: 6 36 # all rows from Y - right outer join X[Y] # default # tab # 1: 3 9 9 # 2: 4 16 16 # 3: 5 NA 25 # 4: 6 NA 36 X[Y, nomatch = NA] # same as above # tab # 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 # tab # 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] # tab # 1: 3 9 9 # 2: 4 16 16 merge(X, Y, by = "t") # same as above # tab # 1: 3 9 9 # 2: 4 16 16 merge(X, Y, by = "t", all = FALSE) # same as above # tab # 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) # tab # 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) # tab # 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的问题的答案 如何连接(合并)数据框架(内部,外部,左,右)? 提供了data.table可以处理的其他连接types的一些示例。

引用data.table常见问题解答1.12

1.12

X [Y]和merge(X,Y)有什么区别?

  • X[Y]是一个连接,使用Y(或Y的键,如果有的话)作为索引查找X的行。
  • Y[X]是一个连接,使用X查找Y的行(或X的键,如果它有一个)
  • merge(X,Y)执行两种方式。

X[Y]Y[X]的行数通常不同; 而merge(X,Y)merge(Y,X)返回的行数是相同的。 但是错过了主要观点。 join或合并后,大部分任务都需要对数据进行处理。 为什么要合并所有的数据列,只能使用其中的一小部分呢? 您可能会build议merge(X[,ColsNeeded1],Y[,ColsNeeded2]) ,但需要复制子集的数据,它需要程序员确定需要哪些列。 data.table中的X[Y,j ]为您完成所有步骤。 当你写X[Y,sum(foo*bar)] ,data.table将自动检查jexpression式来查看它使用的列。 它只会分列这些列。 其他人被忽略。 内存仅为j使用的列创build,Y列在每个组的上下文中享受标准R回收规则。 假设foo在X中,而bar在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)]] ## tba ## 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)]] 

@ mnel的答案是现货,所以请接受答案。 这只是跟进,太长的评论。

正如mnel所说,通过交换YX来获得左/右外连接: Y[X] -vs- X[Y] 。 所以在这种语法中支持4种连接types中的3种,而不是2,iiuc。

添加第四个似乎是一个好主意。 假设我们添加了full=TRUE或者both=TRUE或者merge=TRUE (不确定最好的参数名字吗?),那么在我之前没有发生过X[Y,j,merge=TRUE]在FAQ 1.12之后。 新function请求现在添加并链接回来,谢谢:

FR#2301:为X [Y]和Y [X]连接添加merge = TRUE参数,就像merge()一样。

最近的版本已经加快了merge.data.table (例如,通过内部浅拷贝来更有效地设置密钥)。 所以我们试图使merge()X[Y]更接近,并为用户提供所有的选项以获得充分的灵活性。 两者都有优点和缺点。 另一个突出的function要求是:

FR#2033:将by.x和by.y添加到merge.data.table

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

通过这部分的问题:

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

如果你喜欢merge()语法和它的3个参数allall.xall.y那么就用那个而不是X[Y] 。 认为它应该涵盖所有的情况。 还是你的意思是为什么在[.data.table ? 如果是这样的话,那么FAQ2.14就是这样一种自然的方式:“你能否进一步解释data.table为什么受到A [B]语法的启发?”。 而且,不nomatch只有0NA两个值。 这可以扩展,使负值意味着什么,或12将意味着使用第12行的值来填补nomatch ,例如,或将来的不nomatch可能是一个载体,甚至本身data.table

嗯。 by-without-by如何与merge = TRUE 进行交互? 也许我们应该把这个转到数据表的帮助 。

这个“答案”是一个讨论的build议:正如我的评论所指出的,我build议在[.data.table()中添加一个join参数来启用其他types的连接,例如: X[Y,j,join=string] 。 除了4种普通的连接外,我还build议支持3种types的独占连接和交叉连接。

build议各种连接types的joinstring值(和别名)为:

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

  3. "all.x""left" – 左连接 – 来自X的所有行,没有Y匹配的"all.x"

  4. "outer""full" – 完整的外部连接 – 来自X和Y的所有行,不匹配的NA

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

  6. "only.y""not.x" – 非连接或反连接返回没有X匹配的Y行
  7. "not.both" – 独占连接返回与其他表不匹配的X和Y行,即异或(XOR)
  8. "cross" – 与Y的每一行相匹配的每一行的交叉连接或笛卡尔乘积

默认值是join="all.y" ,它对应于当前的默认值。

“all”,“all.x”和“all.y”string值对应于merge()参数。 对于SQL用户来说,“右”,“左”,“内”和“外”string可能更适合。

“both”和“not.both”string是我现在最好的build议 – 但是对于内部联接和独占联接,有人可能会有更好的stringbuild议。 (我不确定“专属”是否是正确的术语,如果有“XOR”连接的适当术语,请纠正我)

使用join="not.y"X[-Y,j]X[!Y,j]非连接语法的替代方法,对于我来说也许更加清晰,尽pipe我不确定它们是否是相同(data.table版本1.8.3中的新function)。

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