如何进行data.table的合并操作

53

注意:本问题和以下答案是针对data.table版本<1.5.3的;v.1.5.3于2011年2月发布,解决了这个问题。请参阅更近期的处理(03-2012):将基于外键的SQL连接翻译为R data.table语法


我一直在查找data.table包的文档(它是data.frame的替代品,在某些操作上更加高效),包括Josh Reich在纽约R Meetup上关于SQL和data.table的演示(pdf),但是我无法弄清楚这个非常琐碎的操作。

> x <- DT(a=1:3, b=2:4, key='a')
> x
     a b
[1,] 1 2
[2,] 2 3
[3,] 3 4
> y <- DT(a=1:3, c=c('a','b','c'), key='a')
> y
     a c
[1,] 1 a
[2,] 2 b
[3,] 3 c
> x[y]
     a b
[1,] 1 2
[2,] 2 3
[3,] 3 4
> merge(x,y)
  a b c
1 1 2 a
2 2 3 b
3 3 4 c
文档中说:“当[第一个参数]本身是data.table时,会调用类似于base::merge的连接,但是使用已排序键的二分查找。”很明显这并不是这种情况。我能否使用data.table将y的其他列添加到x[y]结果中?它似乎只是取出x中与y键匹配的行,但完全忽略了y的其余部分...

3
这个问题已经在2011年2月发布到CRAN的v1.5.3版本中解决了。请查看该版本的NEWS、新的?data.table和修正后的FAQ。 - Matt Dowle
4个回答

29
您引用了文档中错误的部分。如果您查看[.data.table的文档,您会看到以下内容:

当 i 是一个 data.table 时,x 必须有一个键,这意味着加入 i 到 x 并返回与之匹配的行在 x 中。对于每个列在 i 中和 x 的关键字中的每个列之间执行 equi-join,以此方式进行操作类似于使用二维矩阵进行子集选择,对于高维度数组则通过 n 列矩阵进行操作。

我承认包的描述(您引用的部分)有点令人困惑,因为它似乎表明 "[" 操作可以代替 merge。但我认为它的意思是:如果 x 和 y 都是 data.table,则我们在索引上使用连接(类似于 merge)而不是二元搜索。


还有一件事:

我通过 install.packages 安装的 data.table 库缺少 merge.data.table 方法,因此使用 merge 会调用 merge.data.frame。在安装来自 R-Forge 的包后,R 使用更快的 merge.data.table 方法。

您可以通过检查以下输出来检查是否拥有 merge.data.table 方法:

methods(generic.function="merge")

编辑[回答不再适用]: 此回答是针对 data.table 版本 1.3 的。在版本1.5.3中,data.table 的行为发生了变化,x[y] 返回了预期的结果。感谢 data.table 的作者Matthew Dowle 在评论中指出这一点。


啊,看起来CRAN上的版本是1.2版,而R-Forge上的版本是1.3版。merge方法显然是在1.3版中添加的。从我在R-Forge周围发现的情况来看,该方法大约在8个月前添加,所以我不知道为什么它还没有出现在CRAN上! - Harlan
5
X[Y]语法在2011年2月发布到CRAN的v1.5.3版本中发生了变化。请查看它的NEWS、新的?data.table和已纠正的FAQ。 - Matt Dowle

15

感谢您的回答。当这个帖子最初发布时,我错过了它。自2月以来,data.table已经得到了更新。1.4.1版本已经发布到CRAN上一段时间了,而1.5版本即将推出。例如,DT()别名已被替换为list();作为基本类型,它更快,并且data.table现在继承自data.frame,因此它可以与仅接受data.frame的软件包(如ggplot和lattice)一起使用,无需进行任何转换(更快、更方便)。

是否有可能订阅data.table标签,以便在有人使用该标签发布问题时我能收到电子邮件?datatable-help列表已经增长到每月30-40封电子邮件,但如果我能得到某种通知,我也很乐意在这里回答。

Matthew


13

我认为不需要使用 base::merge 函数,因为使用 data.table 的连接可以更快。例如,看下面的代码。我创建了具有 3-3 列的 xy 数据表:

x <- data.table( foo = 1:5, a=20:24, zoo = 5:1 )
y <- data.table( foo = 1:5, b=30:34, boo = 10:14)
setkey(x, foo)
setkey(y, foo)

使用 base:mergedata.table 进行合并,以查看执行速度:

system.time(merge(x,y))
##    user  system elapsed 
##   0.027   0.000   0.023 

system.time(x[,list(y,x)])
##    user  system elapsed 
##   0.003   0.000   0.006 

结果并不完全相同,因为后者多了一列:

merge(x,y)
##      foo  a zoo  b boo
## [1,]   1 20   5 30  10
## [2,]   2 21   4 31  11
## [3,]   3 22   3 32  12
## [4,]   4 23   2 33  13
## [5,]   5 24   1 34  14

x[,list(x,y)]
##      foo  a zoo foo.1  b boo
## [1,]   1 20   5     1 30  10
## [2,]   2 21   4     2 31  11
## [3,]   3 22   3     3 32  12
## [4,]   4 23   2     4 33  13
## [5,]   5 24   1     5 34  14

这可能不会造成大麻烦 :)


3
谢谢,很有趣。然而语法完全不明显!如果我要使用它,我可能会重新定义一个函数,例如function(x,y) x[,list(x,y)] 或其他类似的东西,也许还会删除那个额外的键列... - Harlan
2
我认为在data.table中连接的正确语法实际上是x[y]或x[y,],即连接应该使用第一个索引。这与merge给出相同的结果。请注意,x[y]和y[x]不需要相同,即如果y包含不代表x的foo条目。也许我错过了什么,但请参阅data.table [vignette]中的Joins(http://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.pdf)。 - cboettig
1
上述的 x[,list(x,y)] 对我没用。我尝试了 x[,c(x,y)],它起作用了。但我不确定这是否有意义。 - bala
使用setkey后,只需执行x[y]即可。 - skan

3
我认为f3lix是正确的,文档有点误导。快速连接子集数据是其好处。最终仍需要像上面的例子一样使用merge函数。
你会在Josh关于使用data.table的演示中看到这是他的示例运行方式。他首先对其中一个数据表进行子集操作,然后进行合并:
library(data.table)
sdt <- DT(series, key='series_id')
ddt <- DT(data, key='series_id')
u <- sdt[ grepl('^[A-Z]{2}URN', fred_id) & !grepl('DSURN', fred_id) ]
d <- ddt[ u, DT(min=min(value)), by='series_id', mult='all']
data <- merge(d,series)[,c('title','min','mean','max')]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接