为什么 data.tables 的 X [ Y ]联接不允许完整的外部联接或左联接?

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

Table 的联接格式 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 = NA1参数进行连接呢?

下面是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 的回答向问题 如何连接(合并)数据帧(内部,外部,左,右) ?提供了一些 data.table 可以处理的其他联接类型的示例。

35528 次浏览

引用 data.table 常见问题1.11 ABC1和 merge(X, Y)有什么不同?

X[Y]是一个连接,它使用 Y (如果有 Y,则使用 Y 的键)作为索引查找 X 的行。

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

merge(X,Y)同时具有两种功能。X[Y]Y[X]的行数通常是不同的,而 merge(X,Y)merge(Y,X)返回的行数是相同的。

但是这没有抓住要点。大多数任务都需要在 为什么合并数据的所有列,只是为了 你可以建议 但这需要程序员计算出需要哪些列。[ X[Y,j]在 data.table 中一步完成所有这些操作 你。在编写 X[Y,sum(foo*bar)]时,data.table 会自动检查 j表达式,以查看它使用哪些列。它将只对这些列进行子集处理; 其他列将被忽略。只为 j使用的列创建内存,而 Y列在每个组的上下文中享受标准的 R 回收规则。假设 fooX中,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)]]
##   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)]]

@ mnel 的回答是正确的,所以请接受这个回答。

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

加上第四个似乎是个好主意。假设我们添加 full=TRUEboth=TRUEmerge=TRUE(不确定最佳参数名称?)然后它没有想到我之前的 X[Y,j,merge=TRUE]将是有用的原因之后,但在 FAQ 1.12。新功能请求现已添加并链接到这里,谢谢:

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

最近的版本加快了 merge.data.table的速度(例如,通过在内部获取一个浅表副本来更有效地设置键)。因此,我们正在努力使 merge()X[Y]更加接近,并为用户提供所有的选项,以实现充分的灵活性。两者都有利有弊。另一个尚未满足的功能要求是:

FR # 2033: 在 merge.data.table 中添加 by. x 和 by. y

如果还有其他人,请继续前进。

通过问题中的这一部分:

为什么不对连接使用 merge 语法,而不使用 match 函数的 nomatch 参数呢?

如果您更喜欢 merge()语法及其3个参数 allall.xall.y,那么只需使用它而不是 X[Y]。应该能涵盖所有案子。或者你的意思是为什么论点是一个单一的 nomatch[.data.table?如果是这样,这就是给定 FAQ 2.14的自然方式: “你能进一步解释为什么 data.table 受到基础语法 A [ B ]的启发吗?”.但是,nomatch目前只取两个值 0NA。这可以扩展到负值意味着什么,或者12意味着使用第12行的值来填充 NA,例如,或者将来的 nomatch可以是一个向量,甚至它本身是一个 all1。

嗯。 被,没有,被如何与 merge = TRUE 相互作用? 也许我们应该把这个交给 可数据化的帮助

这个“答案”是一个供讨论的建议: 正如我在评论中指出的,我建议在[中添加一个 join参数。Table ()启用其他类型的连接,即: X[Y,j,join=string]。除了4种类型的普通连接之外,我还建议支持3种类型的 独家新闻连接和 十字架连接。

各种联接类型的 join字符串值(和别名)建议如下:

  1. "all.y""right"——右连接,目前的 data.table 默认值(nomatch = NA)- 在没有 X 匹配的情况下,使用 NA 的所有 Y 行;
  2. "both""inner"——内部连接(nomatch = 0)——仅限于 X 和 Y 匹配的行;

  3. "all.x""left"——来自 X 和 NA 的所有左连接行,其中没有 Y 匹配:

  4. "outer""full"——完整的外部连接——来自 X 和 Y 的所有行,NAs 中没有匹配

  5. "only.x""not.y"——非连接或反连接返回 X 行,其中没有 Y 匹配

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

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

“ all”、“ all.x”和“ all.y”字符串值对应于 merge()参数。“ right”、“ left”、“ inner”和“ foreign”字符串可能更适合 SQL 用户。

目前,“ both”和“ not. both”字符串是我的最佳建议——但是对于内部联接和独占联接,有人可能有更好的字符串建议。(我不确定“独占”是否是正确的术语,如果有适当的术语表示“ XOR”连接,请纠正我。)

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

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