【问题标题】:Efficiently joining more than 2 data.tables有效连接超过 2 个 data.tables
【发布时间】:2016-10-24 17:47:06
【问题描述】:

我想知道是否有一种内存有效的方式来连接 n 个 data.tables(或数据帧)。例如,如果我有以下 4 个 data.tables:

df1 = data.table(group = c(1L,2L,3L),value = rnorm(3),key = "group")
df2 = data.table(group = c(2L,1L,3L),value2 = rnorm(3),key = "group")
df3 = data.table(group = c(3L,2L,1L),value3 = rnorm(3),key = "group")
df4 = data.table(group = c(1L,3L,2L),value4 = rnorm(3),key = "group")

我可以像这样合并它们:

merge(df1,merge(df2,merge(df3,df4)))

但这似乎不是最佳解决方案。我可能有许多需要合并的 data.tables。有没有办法在不将每个连续合并复制到内存的情况下概括上述内容?在 data.table 之外是否有一种已经被接受的方式来执行此操作?

【问题讨论】:

  • 一种解决方案是将所有数据框放在一个列表中,然后使用Reduce() 合并它们。见stackoverflow.com/questions/8091303/…
  • Reduce(function(x, y) x[y], list(df1, df2, df3, df4)) 在您提供的数据上似乎要快一些,但您必须在更大的输入上对其进行测试才能看到它的扩展性。
  • @ulfelder 这只是语义糖。将进行相同的合并操作。
  • 这取决于你的实际应用。如果您的合并变量在所有表中完全匹配,就像这里一样,那么只需复制 cols
  • 我想您需要考虑很多问题。 Matthew 所做的重新安排本身并不是一个真正的问题——只是在复制之前setorder。如果您知道每个group 值最多出现在一行中,那么您可以制作最终表格(每组一行),然后执行一堆合并分配。如果您知道每个表只有一个额外的列(此处为value* 模式),那么您可以rbindlist 它们然后重塑......我想还有其他情况。

标签: r data.table data-manipulation


【解决方案1】:

以下是您可能拥有的其他一些选项,具体取决于您的数据。除了进行大量合并的明显路径之外的其他选项,我的意思是:在循环中,使用 Reduce 或使用 hadley 的 join_all/merge_all/wrap_em_all_up。

这些都是我在自己的工作中使用并发现速度更快的方法,但我不打算尝试一般的基准测试案例。首先,一些设置:

DFlist = list(df1,df2,df3,df4)
bycols = key(DFlist[[1]])

我假设这些表都由bycols 键入。

堆栈。如果每个表中的新列以某种方式相互关联并且出现在每个表中的相同位置,则考虑仅堆叠数据:

DFlong = rbindlist(DFlist, use.names = FALSE, idcol = TRUE)

如果出于某种原因你真的想要宽格式的数据,你可以dcast:

dcast(DFlong, 
  formula = sprintf("%s ~ .id", paste(bycols, collapse = "+")), 
  value.var = setdiff(names(DFlong), c(bycols, ".id"))
)

不过,Data.table 和 R 最适合长格式数据。

复制列。如果您知道bycols 在所有表中都采用相同的值,那么只需复制:

DF = DFlist[[1]][, bycols, with=FALSE]
for (k in seq_along(DFlist)){
  newcols = setdiff(names(DFlist[[k]]), bycols)
  DF[, (newcols) := DFlist[[k]][, newcols, with=FALSE]]
}

合并分配。如果某些表中可能缺少某些级别的bycols,则制作一个包含所有组合的主表并执行一系列合并分配:

DF = unique(rbindlist(lapply(DFlist, `[`, j = bycols, with = FALSE)))
for (k in seq_along(DFlist)){
  newcols = setdiff(names(DFlist[[k]]), bycols)
  DF[DFlist[[k]], (newcols) := mget(newcols)]
}

【讨论】:

    【解决方案2】:

    在 dplyr 中:

    由于您的试验都具有相同的名称(并且您已经清除了 NA),您可以只绑定行并进行汇总。

    library(dplyr)
    
    DF <- bind_rows(df1,df2,df3,df4) %>%
        group_by(group) %>%
        summarise_each(funs(na.omit))
    

    否则,有一个简单的局部最小值解决方案:尽管至少使用这种方言进行编码可以节省你自己的洋葱上的几层。

    DF <- 
        df1 %>% 
        full_join(df2) %>% 
        full_join(df3) %>% 
        full_join(df4) 
    

    由于 dplyr 在 C++ 而不是 S 中运行,它应该更快。很遗憾,我无法谈论内存使用的效率。

    (类似情况见:R: Updating a data frame with another data frame's dplyr sol'n)

    【讨论】:

    • 如果某些 cols 具有合法的 NA,第一种方法可能会变得很奇怪。
    • 我正在测试它对 NA 的响应方式,并且(不)幸运的是没有发现任何问题。 df3 = data.table(group = c(3L,2L),value3 = rnorm(2),key = "group")
    • 通过 NA,我的意思是 NA :) 试试df3 = data.table(group = c(NA,2L,1L),value3 = rnorm(3),key = "group") 或df3 = data.table(group = c(3L,2L,1L),value3 = rnorm(3),key = "group")[2, value3 := NA]。这两种方法都会导致您在我的计算机上使用的第一种方法出错。
    • 嗯。对:Error: expecting a single value 至少它不会返回不正确的值。将在上面添加警告。
    猜你喜欢
    • 1970-01-01
    • 2020-09-24
    • 1970-01-01
    • 2014-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多