【问题标题】:non-equi joins adding all columns of range table in data.table in one step非 equi 连接一步添加 data.table 中范围表的所有列
【发布时间】:2017-05-29 00:14:21
【问题描述】:

我确定我忽略了显而易见的问题,但是我找不到一种方法可以将“查找”表的所有列加入 data.table 非等值联接一步。

我查看了 Arun 的演示文稿 (https://github.com/Rdatatable/data.table/wiki/talks/ArunSrinivasanSatRdaysBudapest2016.pdf) 和多个 SO 问题,但几乎所有这些问题都只涉及更新单个列,而不是加入多个列。

假设我有 2 个 data.tables ab

library(data.table)
a <- data.table(Company_ID = c(1,1,1,1),
            salary = c(2000, 3000, 4000, 5000))

#   Company_ID salary
# 1:          1   2000
# 2:          1   3000
# 3:          1   4000
# 4:          1   5000

b <- data.table(cat = c(1,2),
            LB = c(0, 3000),
            UB = c(3000,5000),
            rep = c("Bob","Alice"))

#    cat   LB   UB   rep
# 1:   1    0 3000   Bob
# 2:   2 3000 5000 Alice

我最终想要的是将猫、LB、UB、rep(b 中的所有列)与表 a 匹配:

#    Company_ID salary cat   LB   UB   rep
# 1:          1   2000   1    0 3000   Bob
# 2:          1   3000   2 3000 5000 Alice
# 3:          1   4000   2 3000 5000 Alice

目前,我设法做到这一点的唯一方法是使用以下两行:

a <- a[b, on = .(salary >= LB, salary < UB), cat := cat]
a[b, on = .(cat == cat)]

它输出所需的表,但看起来很麻烦,一点也不像data.table 方法。任何帮助将不胜感激!

【问题讨论】:

  • 旁注:您应该/不需要执行a &lt;- a[..., ... := ...],因为:= 运算符通过引用修改了a
  • 这当然是正确的,谢谢 - 我猜是正常 join X[Y] 语法的习惯

标签: r join data.table


【解决方案1】:

由于您想要a 的每一行的结果,您应该像b[a, ...] 一样进行连接:

b[a, on=.(LB <= salary, UB > salary), nomatch=0, 
  .(Company_ID, salary, cat, LB = x.LB, UB = x.UB, rep)]

   Company_ID salary cat   LB   UB   rep
1:          1   2000   1    0 3000   Bob
2:          1   3000   2 3000 5000 Alice
3:          1   4000   2 3000 5000 Alice
  • nomatch=0 表示我们将删除在 b 中不匹配的 a 行。
  • 我们需要使用 x.* 前缀(来自 ?data.table 文档,其中参数命名为 x[i])显式引用 b 中的 UBLB 列。李>

关于奇怪的默认 cols,有一个未解决的问题可以改变这种行为:#1615


Issue #1989,如下所示,现已修复——请参阅 Uwe 的回答。

交替... 一种可行且避免显式列出所有列的方法:将a 的列添加到b,然后子集b

b[a, on=.(LB <= salary, UB > salary), names(a) := mget(paste0("i.", names(a)))] 
b[b[a, on=.(LB <= salary, UB > salary), which=TRUE, nomatch=0]]

这有两个问题。首先,当遇到mget (#1989) 时,有一个错误会导致非 equi 连接中断。临时解决方法是枚举a 的列:

b[a, on=.(LB <= salary, UB > salary), `:=`(Company_ID = i.Company_ID, salary = i.salary)] 
b[b[a, on=.(LB <= salary, UB > salary), which=TRUE, nomatch=0]]

其次,执行此连接两次效率低下(一次用于:=,第二次用于which),但我看不到任何解决方法......也许可以证明一个功能请求允许两者@ 987654344@和which?

【讨论】:

  • 谢谢,弗兰克,它就像你描述的那样工作。那么,我是否正确假设您必须在J 中使用.(...) 语法显式指定所有列?如果我阅读了 Arun 对您正确链接的问题的评论,那是设计使然,因为非等连接通常会返回更多行,因此默认情况下返回所有列不合适?
  • @bendae 我正在考虑是否有解决方法......如果我想到它,我会进行编辑。同时,请让您的问题保持开放/不要接受此答案,因为其他人可能知道方法。 Arun 的评论是针对之前的评论者(他有兴趣在 merge 而不是 [ 中非 equi 加入)。
  • 再次感谢您!如果接下来几天没有其他问题,我会接受你的回答。
【解决方案2】:

现在,#1989 已使用 data.table 版本 1.12.3(开发中)修复,可以从 ab 中选择所有列,而无需明确说明每个列名称:

a[b, on = .(salary >= LB, salary < UB), 
  mget(c(paste0("x.", names(a)), paste0("i.", names(b))))]
   x.Company_ID x.salary i.cat i.LB i.UB i.rep
1:            1     2000     1    0 3000   Bob
2:            1     3000     2 3000 5000 Alice
3:            1     4000     2 3000 5000 Alice

返回 OP 的预期结果,列标题除外。

要更改列标题,可以使用data.table 包中的setnames()

result <- a[b, on = .(salary >= LB, salary < UB), 
            mget(c(paste0("x.", names(a)), paste0("i.", names(b))))] 
setnames(result, c(names(a), names(b)))
result
   Company_ID salary cat   LB   UB   rep
1:          1   2000   1    0 3000   Bob
2:          1   3000   2 3000 5000 Alice
3:          1   4000   2 3000 5000 Alice

或者,使用管道并使用来自magrittr 包的set_names()

library(magrittr)
a[b, on = .(salary >= LB, salary < UB), 
  mget(c(paste0("x.", names(a)), paste0("i.", names(b)))) %>% 
    set_names(c(names(a), names(b)))]
   Company_ID salary cat   LB   UB   rep
1:          1   2000   1    0 3000   Bob
2:          1   3000   2 3000 5000 Alice
3:          1   4000   2 3000 5000 Alice

诚然,这仍然很麻烦。

【讨论】:

    猜你喜欢
    • 2017-11-01
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 1970-01-01
    相关资源
    最近更新 更多