首页 文章

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

提问于
浏览
109

这是关于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 = 0nomatch = NA 参数值对于正在执行的操作不是非常直观 . 我更容易理解并记住 merge 语法: all = TRUEall.x = TRUEall.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可以处理的其他连接类型的一些示例 .

3 回答

  • 15

    引用 data.table FAQ 1.11 What is the difference between X[Y] and merge(X, Y)?

    X [Y]是一个连接,使用Y(或Y的键,如果有的话)作为索引查找X的行 . Y [X]是一个连接,使用X查找Y的行(如果有一个X,则查找X的键)merge(X,Y)同时执行两种方式 . X [Y]和Y [X]的行数通常不同,而merge(X,Y)和merge(Y,X)返回的行数相同 . 但是错过了重点 . 大多数任务需要在连接或合并后对数据执行某些操作 . 为什么要合并所有数据列,之后只使用它们中的一小部分?您可以建议合并(X [,ColsNeeded1],Y [,ColsNeeded2]),但这需要程序员确定需要哪些列 . data.table中的X [Y,j]为您完成所有这一切 . 当您编写X [Y,sum(foo * bar)]时,data.table会自动检查j表达式以查看它使用的列 . 它只会仅对这些列进行子集化;其他人被忽略了 . 仅为j使用的列创建内存,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)]]
    ##   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)]]
    
  • 64

    @ mnel的答案是现场,所以接受这个答案 . 这只是跟进,对于评论来说太长了 .

    正如mnel所说,左/右外连接是通过交换 YXY[X] -vs- X[Y] 获得的 . 因此,在该语法中支持4种连接类型中的3种,而不是2,iiuc .

    添加第4个似乎是一个好主意 . 假设我们添加 full=TRUEboth=TRUEmerge=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() 和_2518361更接近,并为用户提供所有选项以获得充分的灵活性 . 两者都有利弊 . 另一项出色的功能要求是:

    FR#2033 : Add by.x and by.y to merge.data.table

    如果还有其他人,请保持他们的到来 .

    通过这部分问题:

    为什么不使用合并语法进行连接而不是匹配函数的nomatch参数?

    如果您更喜欢 merge() 语法及其3个参数 allall.xall.y ,那么只需使用它而不是 X[Y] . 认为它应涵盖所有情况 . 或者你的意思是为什么这个论点在 [.data.table 中是单一的 nomatch ?如果是这样的话,这就是常见问题解答2.14:"Can you explain further why data.table is inspired by A[B] syntax in base?" . 但是, nomatch 目前只有两个值 0NA . 可能被延长以便负值意味着什么,或者12意味着使用第12行的值来填充NA,例如,或者 nomatch 将来可能是一个向量甚至本身就是 data.table .

    嗯 . by-without-by如何与merge = TRUE交互?也许我们应该把这个拿到datatable-help .

  • 21

    这个"answer"是一个讨论提案:如我的评论所示,我建议在[.data.table()中添加 join 参数以启用其他类型的连接,即: X[Y,j,join=string] . 除了4种类型的普通连接外,我还建议支持3种类型的独占连接和交叉连接 .

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

    • "all.y""right" - 右连接,当前data.table默认值(nomatch = NA) - 所有Y行与NAs没有X匹配;

    • "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" - 非连接或反连接返回Y行,其中没有X匹配

    • "not.both" - 独占连接返回X和Y行,其中与另一个表不匹配,即异或(XOR)

    • "cross" - 交叉连接或笛卡尔积,每行X与Y的每一行匹配

    默认值为 join="all.y" ,对应于当前默认值 .

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

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

    使用 join="not.y"X[-Y,j]X[!Y,j] 非连接语法的替代方法,可能更清楚(对我而言),虽然我不确定它们是否相同(data.table版本1.8.3中的新功能) .

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

相关问题