左边连接(一列)的首选data.table语法

sin*_*dur 7 r data.table

我该如何开始考虑我更喜欢哪种语法?

我的标准是效率(这是第一)以及可读性/可维护性.

这个

A <- B[A, on = .(id)] # wow such. concision
Run Code Online (Sandbox Code Playgroud)

或者那个

A[B, on = .(id), comment := i.comment]
Run Code Online (Sandbox Code Playgroud)

甚至(正如PoGibas所说):

A <- merge(A, B, all.x = TRUE)
Run Code Online (Sandbox Code Playgroud)

为了完整性,一个更基本的方法是使用match():

A[, comment := B[chmatch(A[["id"]], id), comment]]
Run Code Online (Sandbox Code Playgroud)

示例数据:

library(data.table)
A <- data.table(id = letters[1:10], amount = rnorm(10)^2)
B <- data.table(id = c("c", "d", "e"), comment = c("big", "slow", "nice"))
Run Code Online (Sandbox Code Playgroud)

Fra*_*ank 15

为了提高效率和可维护性,我更喜欢"更新连接"这个用语:**

DT[WHERE, v := FROM[.SD, on=, x.v]]
Run Code Online (Sandbox Code Playgroud)

它是vignette("datatable-reference-semantics")"通过引用更新一些列的行 - 按引用子分配 "中显示的内容的扩展.一旦联接上有一个小插图,这也应该是一个很好的参考.

这是有效的,因为它只使用选择的行WHERE并就地修改或添加列,而不是像新的简洁左连接那样创建新表FROM[DT, on=].

它使我的代码更具可读性,因为我可以很容易地看到连接点是添加列v; 而且我不需要考虑SQL中的"左"/"右"术语,或者在连接后是否保留行数.

它对于代码维护很有用,因为如果我后来想知道如何DT命名列v,我可以搜索我的代码v :=,同时FROM[DT, on=]模糊添加了哪些新列.此外,它允许WHERE条件,而左连接则不允许.例如,如果用于"填充"现有列中的NA,这可能很有FROMv.


与其他更新连接方法相比DT[FROM, on=, v := i.v],我可以想到两个优点.第一个是使用该WHERE子句的选项,第二个是当连接出现问题时通过警告透明,例如以FROM条件为条件的重复匹配on=.以下是扩展OP示例的说明:

library(data.table)
A <- data.table(id = letters[1:10], amount = rnorm(10)^2)
B2 <- data.table(
  id = c("c", "d", "e", "e"), 
  ord = 1:4, 
  comment = c("big", "slow", "nice", "nooice")
)

# left-joiny update
A[B2, on=.(id), comment := i.comment, verbose=TRUE]
# Calculated ad hoc index in 0.000s elapsed (0.000s cpu) 
# Starting bmerge ...done in 0.000s elapsed (0.000s cpu) 
# Detected that j uses these columns: comment,i.comment 
# Assigning to 4 row subset of 10 rows

# my preferred update
A[, comment2 := B2[A, on=.(id), x.comment]]
# Warning message:
# In `[.data.table`(A, , `:=`(comment2, B2[A, on = .(id), x.comment])) :
#   Supplied 11 items to be assigned to 10 items of column 'comment2' (1 unused)

    id     amount comment comment2
 1:  a 0.20000990    <NA>     <NA>
 2:  b 1.42146573    <NA>     <NA>
 3:  c 0.73047544     big      big
 4:  d 0.04128676    slow     slow
 5:  e 0.82195377  nooice     nice
 6:  f 0.39013550    <NA>   nooice
 7:  g 0.27019768    <NA>     <NA>
 8:  h 0.36017876    <NA>     <NA>
 9:  i 1.81865721    <NA>     <NA>
10:  j 4.86711754    <NA>     <NA>
Run Code Online (Sandbox Code Playgroud)

在左连接风格的更新中,comment即使有两个匹配项,您也会静默获取最终值id == "e".在另一个更新中,您会收到一条有用的警告消息(在将来的版本中升级为错误).即使verbose=TRUE使用左连接方法启用也没有提供信息 - 它说有四行正在更新,但并没有说一行正在更新两次.


我发现当我的数据被安排到一组整洁/关系表中时,这种方法效果最好.对此的一个很好的参考是Hadley Wickham的论文.

**在这个成语中,on=应该使用连接列名称和规则填充该部分,例如on=.(id)on=.(from_date >= dt_date).而且加入的规则可以被传递roll=,mult=nomatch=.详情?data.table请见.感谢@RYoda在评论中注意到这一点.

  • 只是一个附录:`on =`部分必须填充连接列名称(例如``on =.(id)`.PS:很好的答案! (2认同)
  • @Frank我认为你的答案是关于与data.table连接的非常好的资源。最近更新到版本 1.12.3,现在当我使用 `A[, comment2 := B2[A, on=.(id), x.comment]]` 时出现错误:_Supplied 11 items to be分配给 10 “comment2”列的项目。如果您希望“回收”RHS,请使用rep() 向代码读者明确此意图。_ (2认同)