为什么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 = 0
和nomatch = NA
参数值对于正在执行的操作并不是很直观。 对我来说,理解和记住merge
语法更为容易: all = TRUE
, all.x = TRUE
, all.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所说,通过交换Y
和X
来获得左/右外连接: 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个参数all
, all.x
和all.y
那么就用那个而不是X[Y]
。 认为它应该涵盖所有的情况。 还是你的意思是为什么在[.data.table
? 如果是这样的话,那么FAQ2.14就是这样一种自然的方式:“你能否进一步解释data.table为什么受到A [B]语法的启发?”。 而且,不nomatch
只有0
和NA
两个值。 这可以扩展,使负值意味着什么,或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的join
string值(和别名)为:
-
"all.y"
和"right"
– 右连接,当前的data.table默认值(nomatch = NA) – 所有的Y行与NAs没有X匹配; -
"both"
和"inner"
– 内连接(nomatch = 0) – 只有X和Y匹配的行; -
"all.x"
和"left"
– 左连接 – 来自X的所有行,没有Y匹配的"all.x"
: -
"outer"
和"full"
– 完整的外部连接 – 来自X和Y的所有行,不匹配的NA -
"only.x"
和"not.y"
– 非连接或反连接返回没有Y匹配的X行 -
"only.y"
和"not.x"
– 非连接或反连接返回没有X匹配的Y行 -
"not.both"
– 独占连接返回与其他表不匹配的X和Y行,即异或(XOR) -
"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范例。