这是关于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 = 0
和nomatch = NA
参数值对于正在执行的操作不是非常直观。我更容易理解并记住merge
语法:all = TRUE
,all.x = TRUE
和all.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 answer的How to join (merge) data frames (inner, outer, left, right)?提供了data.table可以处理的其他连接类型的一些示例。
答案 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所说,左/右外连接是通过交换Y
和X
:Y[X]
-vs- X[Y]
获得的。因此,该语法支持4种连接类型中的3种,而不是2,iiuc。
添加第4个似乎是一个好主意。假设我们添加full=TRUE
或both=TRUE
或merge=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个参数all
,all.x
和all.y
,那么只需使用X[Y]
代替nomatch
。认为它应涵盖所有情况。或者你的意思是为什么[.data.table
中的论点是nomatch
?如果是这样的话,这就是常见问题解答2.14:“你能进一步解释为什么data.table受到基础中A [B]语法的启发吗?”。但是,0
目前只有两个值NA
和nomatch
。这可以延长,以便负值意味着什么,或者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]
字符串值(和别名):
join
和"all.y"
- 右连接,当前data.table默认值(nomatch = NA) -
所有没有X匹配的具有NA的Y行; "right"
和"both"
- 内部联接(nomatch = 0) - 只有X和Y匹配的行;
"inner"
和"all.x"
- 左连接 - X,NAs中没有Y匹配的所有行:
"left"
和"outer"
- 完全外部联接 - 来自X和Y的所有行,不匹配的NAs
"full"
和"only.x"
- 非连接或反连接返回没有Y匹配的X行
"not.y"
和"only.y"
- 非联接或反联接返回没有X匹配的Y行"not.x"
- 独占连接返回X和Y行,其中与其他表不匹配,即独占或(XOR)"not.both"
- 交叉加入或笛卡儿积,每行X与Y的每一行匹配默认值为"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范例。