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

Why does X[Y] join of data.tables not allow a full outer join, or a left join?

这是一个关于data.table连接语法的哲学问题。我正在寻找越来越多的数据表的用途,但仍在学习…

data.tables的join格式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种连接类型的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 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可以处理的其他联接类型的一些示例。


引用data.table常见问题1.11,X[Y]merge(X, Y)有什么区别?

X[Y] is a join, looking up X's rows using Y (or Y's key if it has one) as an index.

Y[X] is a join, looking up Y's rows using X (or X's key if it has one)

merge(X,Y) does both ways at the same time. The number of rows of X[Y] and Y[X] usually differ, whereas the number of rows returned by merge(X,Y) and merge(Y,X) is the same.

BUT that misses the main point. Most tasks require something to be done on the
data after a join or merge. Why merge all the columns of data, only to
use a small subset of them afterwards? You may suggest
merge(X[,ColsNeeded1],Y[,ColsNeeded2]), but that requires the programmer to work out which columns are needed. X[Y,j] in data.table does all that in one step for
you. When you write X[Y,sum(foo*bar)], data.table automatically inspects the j expression to see which columns it uses. It will only subset those columns only; the others are ignored. Memory is only created for the columns the j uses, and Y columns enjoy standard R recycling rules within the context of each group. Let's say foo is in X, and bar is in Y (along with 20 other columns in Y). Isn't X[Y,sum(foo*bar)] quicker to program and quicker to run than a merge of everything wastefully followed by a subset?

如果您想要X[Y]的左外联接

1
2
3
4
5
6
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

如果你想要一个完整的外部连接

1
2
3
4
5
6
7
8
9
10
11
12
13
# 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所说,通过交换YXY[X]X[Y],获得左/右外部联接。所以4个连接类型中有3个在该语法中受支持,而不是2,IIUC。

增加第四个似乎是个好主意。假设我们加上full=TRUEboth=TRUEmerge=TRUE(不确定最佳论证名称?)在我之前没有想到过,由于后面的原因,X[Y,j,merge=TRUE]会很有用,但是在常见问题1.12中。新功能请求现已添加并链接回此处,谢谢:

fr 2301:为x[y]和y[x]join添加merge=true参数,就像merge()一样。

最近的版本加速了merge.data.table(例如,通过在内部取一个较浅的副本来更有效地设置密钥)。因此,我们正在努力使merge()X[Y]更接近,并为用户提供所有选项,以实现完全的灵活性。两者都有利弊。另一个突出的功能请求是:

fr 2033:添加by.x和by.y到merge.data.table

如果还有其他人,请让他们来。

根据问题中的这一部分:

why not use the merge syntax for joins rather than the match function's nomatch parameter?

如果您喜欢merge()语法及其3个参数allall.xall.y,那么只需使用它而不是X[Y]。我想应该包括所有的情况。或者你的意思是说,为什么在[.data.table中只有一个nomatch?如果是这样的话,在常见问题2.14中,这看起来很自然:"您能进一步解释为什么data.table受到base中的[b]语法的启发吗?".但是,nomatch目前只取0NA两个值。这可以扩展到负值意味着什么,或者12表示使用第12行的值来填充NAS,例如,或者将来的nomatch可能是一个向量,甚至它本身也可能是data.table

hm.如果没有by-interaction-with-merge=true,该怎么办?也许我们应该把这个移交给数据表帮助。


这个"答案"是一个供讨论的建议:正如我在评论中所指出的,我建议在[.data.table()中添加一个join参数,以启用其他类型的连接,例如:X[Y,j,join=string]。除了4种类型的普通联接之外,我还建议支持3种类型的排他联接和交叉联接。

各种连接类型的join字符串值(和别名)建议为:

  • "all.y""right"右联接,当前data.table默认值(nomatch=na)-没有x匹配的所有带有nas的y行;
  • "both""inner"--内部连接(nomatch=0)——仅X和Y匹配的行;

  • "all.x""left"左联接—来自x、nas的所有行,其中y不匹配:

  • "outer""full"—完全外部联接—来自x和y、nas的所有行,不匹配

  • "only.x""not.y"—不存在y匹配的非联接或反联接返回x行

  • "only.y""not.x"——不存在x匹配的非连接或反连接返回y行
  • "not.both"—独占联接,返回与另一个表不匹配的x和y行,即独占或(x or)
  • "cross"——X每行与Y每行匹配的交叉联接或笛卡尔积
  • 默认值为与当前默认值相对应的join="all.y"

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

    "both"和"not.both"字符串是我目前最好的建议——但是对于内部联接和独占联接,可能有人有更好的字符串建议。(我不确定"exclusive"是否是正确的术语,如果"xor"连接有合适的术语,请更正我。)

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

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