使用 data.table 存储为列表元素的多个数据帧的完全外连接

Posted

技术标签:

【中文标题】使用 data.table 存储为列表元素的多个数据帧的完全外连接【英文标题】:Full outer join of multiple dataframes stored as elements of a list using data.table 【发布时间】:2014-12-28 02:56:06 【问题描述】:

我正在尝试使用data.table 对存储为列表元素的多个数据帧进行完全外部连接。我已经使用reshape 包的merge_recurse() 函数成功地做到了这一点,但是对于较大的数据集来说它非常慢,我想通过使用data.table 来加速合并。我不确定 data.table 处理具有多个数据框的列表结构的最佳方式。我也不确定我是否在唯一键上正确编写了 Reduce() 函数,以便对多个数据帧进行完全外部连接。

这是一个小例子:

#Libraries
library("reshape")
library("data.table")

#Specify list of multiple dataframes
filelist <- list(data.frame(x=c(1,1,1,2,2,2,3,3,3), y=c(1,2,3,1,2,3,1,2,3), a=1:9),
                 data.frame(x=c(1,1,1,2,2,2,3,3,4), y=c(1,2,3,1,2,3,1,2,1), b=seq(from=0, by=5, length.out=9)),
                 data.frame(x=c(1,1,1,2,2,2,3,3,4), y=c(1,2,3,1,2,3,1,2,2), c=seq(from=0, by=10, length.out=9)))

#Merge with merge_recurse()
listMerged <- merge_recurse(filelist, by=c("x","y"))

#Attempt with data.table
ids <- lapply(filelist, function(x) x[,c("x","y")])
unique_keys <- unique(do.call("rbind", ids))
dt <- data.table(filelist)
setkey(dt, c("x","y")) #error here

Reduce(function(x, y) x[y[J(unique_keys)]], filelist)

这是我的预期输出:

> listMerged
   x y  a  b  c
1  1 1  1  0  0
2  1 2  2  5 10
3  1 3  3 10 20
4  2 1  4 15 30
5  2 2  5 20 40
6  2 3  6 25 50
7  3 1  7 30 60
8  3 2  8 35 70
9  3 3  9 NA NA
10 4 1 NA 40 NA
11 4 2 NA NA 80

这是我的资源:

Suggestion to use Reduce() function on data.table(见答案的最后评论) Suggestion to use "unique keys" to do full outer join in data.table

【问题讨论】:

【参考方案1】:

这对我有用:

library("reshape")
library("data.table")
##
filelist <- list(
  data.frame(
    x=c(1,1,1,2,2,2,3,3,3), 
    y=c(1,2,3,1,2,3,1,2,3), 
    a=1:9),
  data.frame(
    x=c(1,1,1,2,2,2,3,3,4), 
    y=c(1,2,3,1,2,3,1,2,1), 
    b=seq(from=0, by=5, length.out=9)),
  data.frame(
    x=c(1,1,1,2,2,2,3,3,4), 
    y=c(1,2,3,1,2,3,1,2,2), 
    c=seq(from=0, by=10, length.out=9)))
##
## I used copy so that this would
## not modify 'filelist'
dtList <- copy(filelist)
lapply(dtList,setDT)
lapply(dtList,function(x)
  setkeyv(x,cols=c("x","y"))
)
##
> Reduce(function(x,y)
  merge(x,y,all=T,allow.cartesian=T)
,dtList)
    x y  a  b  c
 1: 1 1  1  0  0
 2: 1 2  2  5 10
 3: 1 3  3 10 20
 4: 2 1  4 15 30
 5: 2 2  5 20 40
 6: 2 3  6 25 50
 7: 3 1  7 30 60
 8: 3 2  8 35 70
 9: 3 3  9 NA NA
10: 4 1 NA 40 NA
11: 4 2 NA NA 80

我还注意到您的代码中存在一些问题。 dt &lt;- data.table(filelist) 导致

> dt
       filelist
1: <data.frame>
2: <data.frame>
3: <data.frame>

这很可能是您在上面指出的setkey(dt, c("x","y")) 错误的原因。另外,这对你有用吗?

Reduce(function(x, y) x[y[J(unique_keys)]], filelist)

我只是好奇,因为我在尝试运行它时遇到了错误(使用dtList 而不是filelist

Error in eval(expr, envir, enclos) : could not find function "J"

我认为这与自 data.table 的 1.8.8 版以来实施的更改有关,@Arun 在 this answer 中对此进行了解释。

【讨论】:

太好了,这完美地解决了我的问题。我对 Reduce() 函数的尝试也对我不起作用。我只需要根据我可以从不同来源拼凑的内容做出明智的猜测。您的代码工作得更好,谢谢! 不客气; FWIW 你的 Reduce 函数离你不远了,我只需要做一些小改动。

以上是关于使用 data.table 存储为列表元素的多个数据帧的完全外连接的主要内容,如果未能解决你的问题,请参考以下文章

在将列表存储为对象之前引用列表中的其他项目

在列表中有效地重复data.table,从循环中的另一个data.table顺序替换具有相同名称的列

已知线性表最多可能有20个元素,存储每个元素需要8字节,存储每个指针需要4字节。当元素个数为( )时使用单链表比使用数组存储此线性表更加节约空间。

lapply 在大型 data.table 上使用大型列表

将 data.table 列快速连接成一个字符串列

声明元素个数为 0 的数组仍然可以存储值