R 通过多个变量将多个数据帧与data.table联接

R 通过多个变量将多个数据帧与data.table联接,r,data.table,dplyr,R,Data.table,Dplyr,所以我看到了一些关于加入不同包的答案。 我需要连接几个数据帧,这对于我的计算机来说是一个非常昂贵的操作,要用基本的“合并”算法来处理 我的数据: list.of.data.frames = list( data.table("P1" = c(1:3,1:3), "P2" = c(rep(2.5,3),rep(1.5,3)), "D1" = c(3.5,4.5,5.5,2.5,3.5,4.5)), data.table("P1" = c(1:3,1:

所以我看到了一些关于加入不同包的答案。 我需要连接几个数据帧,这对于我的计算机来说是一个非常昂贵的操作,要用基本的“合并”算法来处理

我的数据:

list.of.data.frames = list( data.table("P1" = c(1:3,1:3), "P2" = c(rep(2.5,3),rep(1.5,3)), "D1" = c(3.5,4.5,5.5,2.5,3.5,4.5)),
                        data.table("P1" = c(1:3,1:3), "P3" = c(rep(2,3),rep(3,3)), "D3" =c(3:5,4:6)),
                        data.table("P1" = c(2:4), "P4" = c(2:4))
                        )
我试过这两种代码:
使用整形

使用基数R

输出:

    P1  P2  D1 P3 D3 P4
 1:  1 2.5 3.5  2  3 NA
 2:  1 2.5 3.5  3  4 NA
 3:  2 2.5 4.5  2  4  2
 4:  2 2.5 4.5  3  5  2
 5:  3 2.5 5.5  2  5  3
 6:  3 2.5 5.5  3  6  3
 7:  1 1.5 2.5  2  3 NA
 8:  1 1.5 2.5  3  4 NA
 9:  2 1.5 3.5  2  4  2
 10: 2 1.5 3.5  3  5  2
 11: 3 1.5 4.5  2  5  3
 12: 3 1.5 4.5  3  6  3
 13: 4  NA  NA NA NA  4
我正在尝试使用最快的方法来执行此操作,它可以很容易地扩展到处理列表。对于
data.table
我被键卡住了,因为每个data.frame(或数据表)可以有不同的列,其中一些列可能与其他表相交或不相交

此外,我还看到了
data.table::merge.data.table()
函数,但我不知道这是否来自旧版本,因为我在控制台中找不到它

你知道怎么做吗


提前感谢您

注意:您包含的两种方法都不适合我,我无法操纵
重塑
方法使其运行

正如@David在一篇评论中提到的,您已经在
base
方法中使用了
merge.data.table
,因为
merge
是一种通用方法,可以“放手”使用更具体的方法(在本例中,对于
data.table

有一个版本可以使用
dplyr
left\u join
执行多重合并,可以在此处进行修改:

Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2), list.of.data.frames)
我们可以使用
microbenchmark
包显式地测试各种方法。我正在添加一个版本,在这个版本中,我告诉
left_join
要通过哪一列进行连接,而不是让它来计算(尽管如果每个连接都需要使用不同的列集进行匹配,那么这将不起作用)。我还包括@Axeman的建议,即使用
purr
中的
reduce
而不是
reduce

microbenchmark(
  base = Reduce(function(...) merge(..., all=T, by = "P1"), list.of.data.frames)
  , dplyr = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2), list.of.data.frames)
  , dplyrSet = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2, by = "P1"), list.of.data.frames)
  , dplyrPurrr = reduce(list.of.data.frames, full_join, by = "P1")
)
给出:

Unit: microseconds
       expr      min        lq      mean    median       uq      max neval cld
       base 2911.495 3025.2325 3227.3762 3077.8530 3211.995 5513.166   100   c
      dplyr  946.367 1022.0960 1087.8771 1066.3615 1131.675 1429.581   100  b 
   dplyrSet  443.828  485.3235  543.7130  511.1545  553.040 1918.009   100 a  
 dplyrPurrr  465.329  494.6615  548.7349  515.6695  551.943 1804.394   100 a  
因此,
left\u join
的速度大约是
merge
的3倍,将变量设置为join将进一步缩短大约一半的时间
reduce
并没有缩短时间,尽管它确实使代码更加简洁

我们可以(并且应该,正如@Frank指出的那样)确认返回的值是相同的。对于这种类型的结果,“相同”可能意味着什么存在一些争论,因此我使用
compare
包中的
compare
来检查差异(每个
full\u join
方法完全相同,所以我只展示了有趣的一种):

返回:

TRUE
  sorted
  renamed rows
  dropped row names
  dropped attributes
因此,这些值是相同的,但顺序不同(需要排序),行名称不同(需要重命名/删除),属性不同(需要删除)。如果其中任何一个与用例有关,那么用户需要确定哪种方法提供了他们想要的排序/行名/属性

正如@Davidernburg所指出的,不同的尺寸可能导致不同的结果。因此,这里有一些代码检查这些不同的大小

medianTimes_dataTable <- lapply(10^(1:5), function(n){
  list_of_longer_ones = list( data.table("P1" = c(1:n), "P2" = rnorm(n), "D1" = rnorm(n)),
                              data.table("P1" = sample(1:n), "P3" = rnorm(n), "D3" =rnorm(n)),
                              data.table("P1" = sample(1:n), "P4" = rnorm(n))
  )


  microbenchmark(
    base = Reduce(function(...) merge(..., all=T, by = "P1"), list_of_longer_ones)
    , dplyr = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2), list_of_longer_ones)
    , dplyrSet = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2, by = "P1"), list_of_longer_ones)
    , dplyrPurrr = reduce(list_of_longer_ones, full_join, by = "P1")
  ) %>%
    group_by(expr) %>%
    summarise(median = median(time)) %>%
    mutate(nRows = n)
}) %>%
  bind_rows

medianTimes_dataTable %>%
  mutate_at(c("median", "nRows"), format, big.mark = ",", scientific = FALSE) %>%
  spread(nRows, median)
给予

expr`10``100``1000``10000``100000`
*                                                  
1个底座806009.5 973636.0 2046009.5 19088482.5 519159607.0
2 dplyr 1092747.0 1242550.5 2010648.5 10618735.5 156958793.0
3 DPRhyrSet 526030.0 616996.0 1343766.5 9767689.5 147919013.5
4 dplyrPurrr 541182.0 624208.0 1351910.0 9711435.0 146379176.5

在这里,
full\u join
继续击败
merge
——这表明
merge.data.table
merge.data.frame
方法要好(而且很多)。

您在那里做的是
data.table
合并,而不是基本合并
merge
是通用的。请参见
methods(merge)
以及
data.table:::merge.data.table
。仅供参考,您可以通过使用长格式数据使其无限快:
L[[3][,“Dn”:=(NA)];rbindlist(L,use.names=FALSE,idcol=TRUE)
其中
L
是您的列表。也就是说,如果速度真的很重要,您应该重新考虑以这种(相当混乱的)方式管理数据。左连接与
merge
中的
all=TRUE
不同。还有,你把你的第一个版本叫做“base”,你是加入那里的data.frames还是data.tables?谢谢@DavidArenburg——这就是我在喝完咖啡之前回答任何问题的结果。我把它改为
full_join
,这应该可以解决问题。捕捉得很好。正在全速进行hadleyverse:
减少(list.of.data.frames,full_join,by=“P1”)
。(只是语法上的差异。)谢谢你提醒我关于
reduce
@Axeman的事。添加到microbenchmark.Good point@Frank——这是我第一次尝试回答一个明确的基准问题。从结果来看,区别似乎在于排序顺序(merge似乎将所有1放在一起,
full\u join
没有)。但是,返回的数据在其他方面似乎是相同的。我添加了一个比较的结果,使测试更加明确。
compare(
  Reduce(function(...) merge(..., all=T, by = "P1"), list.of.data.frames)
  , reduce(list.of.data.frames, full_join, by = "P1")
  , allowAll = TRUE
  )
TRUE
  sorted
  renamed rows
  dropped row names
  dropped attributes
medianTimes_dataTable <- lapply(10^(1:5), function(n){
  list_of_longer_ones = list( data.table("P1" = c(1:n), "P2" = rnorm(n), "D1" = rnorm(n)),
                              data.table("P1" = sample(1:n), "P3" = rnorm(n), "D3" =rnorm(n)),
                              data.table("P1" = sample(1:n), "P4" = rnorm(n))
  )


  microbenchmark(
    base = Reduce(function(...) merge(..., all=T, by = "P1"), list_of_longer_ones)
    , dplyr = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2), list_of_longer_ones)
    , dplyrSet = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2, by = "P1"), list_of_longer_ones)
    , dplyrPurrr = reduce(list_of_longer_ones, full_join, by = "P1")
  ) %>%
    group_by(expr) %>%
    summarise(median = median(time)) %>%
    mutate(nRows = n)
}) %>%
  bind_rows

medianTimes_dataTable %>%
  mutate_at(c("median", "nRows"), format, big.mark = ",", scientific = FALSE) %>%
  spread(nRows, median)
        expr     `     10`     `    100`     `  1,000`     ` 10,000`     `100,000`
*     <fctr>         <chr>         <chr>         <chr>         <chr>         <chr>
1       base   2,032,614.5   2,059,519.0   2,716,534.0   4,475,653.5  29,655,330.0
2      dplyr   1,147,676.5   1,205,818.0   2,369,464.5  11,170,513.5 154,767,265.5
3   dplyrSet     537,434.0     613,785.5   1,602,681.0  10,215,099.5 145,574,663.0
4 dplyrPurrr     540,455.5     626,076.5   1,549,114.0  10,040,808.5 145,086,376.0
medianTimes_dataFrame <- lapply(10^(1:5), function(n){
  list_of_longer_ones = list( data.frame("P1" = c(1:n), "P2" = rnorm(n), "D1" = rnorm(n)),
                              data.frame("P1" = sample(1:n), "P3" = rnorm(n), "D3" =rnorm(n)),
                              data.frame("P1" = sample(1:n), "P4" = rnorm(n))
  )


  microbenchmark(
    base = Reduce(function(...) merge(..., all=T, by = "P1"), list_of_longer_ones)
    , dplyr = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2), list_of_longer_ones)
    , dplyrSet = Reduce(function(dtf1,dtf2) full_join(dtf1,dtf2, by = "P1"), list_of_longer_ones)
    , dplyrPurrr = reduce(list_of_longer_ones, full_join, by = "P1")
  ) %>%
    group_by(expr) %>%
    summarise(median = median(time)) %>%
    mutate(nRows = n)
}) %>%
  bind_rows

medianTimes_dataFrame %>%
  mutate_at(c("median", "nRows"), format, big.mark = ",", scientific = FALSE) %>%
  spread(nRows, median)
        expr     `     10`     `    100`     `  1,000`     ` 10,000`     `100,000`
*     <fctr>         <chr>         <chr>         <chr>         <chr>         <chr>
1       base     806,009.5     973,636.0   2,046,009.5  19,088,482.5 519,159,607.0
2      dplyr   1,092,747.0   1,242,550.5   2,010,648.5  10,618,735.5 156,958,793.0
3   dplyrSet     526,030.0     616,996.0   1,343,766.5   9,767,689.5 147,919,013.5
4 dplyrPurrr     541,182.0     624,208.0   1,351,910.0   9,711,435.0 146,379,176.5