【问题标题】:Combine a list of data frames into one data frame by row将一组数据帧逐行组合成一个数据帧
【发布时间】:2023-03-20 01:23:01
【问题描述】:

我的代码在某个地方以数据框列表结尾,我真的想将其转换为单个大数据框。

我从 earlier question 那里得到了一些指示,它试图做一些类似但更复杂的事情。

这是我开始的一个例子(为了说明,这被大大简化了):

listOfDataFrames <- vector(mode = "list", length = 100)

for (i in 1:100) {
    listOfDataFrames[[i]] <- data.frame(a=sample(letters, 500, rep=T),
                             b=rnorm(500), c=rnorm(500))
}

我目前正在使用这个:

  df <- do.call("rbind", listOfDataFrames)

【问题讨论】:

标签: r list dataframe r-faq


【解决方案1】:

使用 dplyr 包中的bind_rows()

bind_rows(list_of_dataframes, .id = "column_label")

【讨论】:

  • 不错的解决方案。 .id = "column_label" 根据列表元素名称添加唯一的行名称。
  • 自 2018 年以来,dplyr 既快速又可靠,我已将其更改为已接受的答案。岁月飞逝!
  • 这正是我所需要的!!
  • 奇怪,但它不能与小标题列表一起正常工作
【解决方案2】:

另一种选择是使用 plyr 函数:

df <- ldply(listOfDataFrames, data.frame)

这个比原来慢一点:

> system.time({ df <- do.call("rbind", listOfDataFrames) })
   user  system elapsed 
   0.25    0.00    0.25 
> system.time({ df2 <- ldply(listOfDataFrames, data.frame) })
   user  system elapsed 
   0.30    0.00    0.29
> identical(df, df2)
[1] TRUE

我的猜测是,使用do.call("rbind", ...) 将是您会发现的最快的方法,除非您可以执行以下操作(a)使用矩阵而不是 data.frames 和(b)预分配最终矩阵并分配而不是成长它。

编辑 1

根据 Hadley 的评论,这是来自 CRAN 的 rbind.fill 的最新版本:

> system.time({ df3 <- rbind.fill(listOfDataFrames) })
   user  system elapsed 
   0.24    0.00    0.23 
> identical(df, df3)
[1] TRUE

这比 rbind 更容易,而且速度稍快(这些时间在多次运行中都成立)。而且据我了解,the version of plyr on github 比这还要快。

【讨论】:

  • 最新版plyr中的rbind.fill比do.call和rbind快很多
  • 有趣。对我来说 rbind.fill 是最快的。很奇怪,do.call / rbind 没有返回相同的 TRUE,即使我能找到不同之处。其他两个相等,但 plyr 较慢。
  • I() 可以在您的ldply 通话中替换data.frame
  • reshape(2) 中还有 melt.list
  • bind_rows() 根据rmd's answer 是最快的,我认为这是最直接的。它还具有添加id column的功能
【解决方案3】:

为了完整起见,我认为这个问题的答案需要更新。 “我的猜测是,使用do.call("rbind", ...) 将是你会发现的最快的方法......” 2010 年 5 月和之后的一段时间可能是这样,但在 2011 年 9 月左右,引入了一个新函数 rbindlist data.table 软件包版本 1.8.2,并带有注释“这与 do.call("rbind",l) 相同,但速度更快”。快多少?

library(rbenchmark)
benchmark(
  do.call = do.call("rbind", listOfDataFrames),
  plyr_rbind.fill = plyr::rbind.fill(listOfDataFrames), 
  plyr_ldply = plyr::ldply(listOfDataFrames, data.frame),
  data.table_rbindlist = as.data.frame(data.table::rbindlist(listOfDataFrames)),
  replications = 100, order = "relative", 
  columns=c('test','replications', 'elapsed','relative')
  ) 

                  test replications elapsed relative
4 data.table_rbindlist          100    0.11    1.000
1              do.call          100    9.39   85.364
2      plyr_rbind.fill          100   12.08  109.818
3           plyr_ldply          100   15.14  137.636

【讨论】:

  • 非常感谢您——我的数据集对于ldplying 一堆长而熔化的数据帧来说变得太大了。无论如何,通过使用您的 rbindlist 建议,我得到了令人难以置信的加速。
  • 还有一个完整的:dplyr::rbind_all(listOfDataFrames) 也可以解决问题。
  • 是否有与rbindlist 等效的方法,但按列附加数据帧?类似 cbindlist 的东西?
  • @rafa.pereira 最近有一个功能请求:add function cbindlist
  • 我也是因为do.call()在数据帧列表上运行了18个小时,仍然没有完成,谢谢!!!
【解决方案4】:

代码:

library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
plyr::rbind.fill(dflist),
dplyr::bind_rows(dflist),
data.table::rbindlist(dflist),
plyr::ldply(dflist,data.frame),
do.call("rbind",dflist),
times=1000)

ggplot2::autoplot(mb)

会话:

R version 3.3.0 (2016-05-03)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

> packageVersion("plyr")
[1] ‘1.8.4’
> packageVersion("dplyr")
[1] ‘0.5.0’
> packageVersion("data.table")
[1] ‘1.9.6’

更新: 2018 年 1 月 31 日重播。在同一台计算机上运行。新版本的软件包。为种子爱好者添加了种子。

set.seed(21)
library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
  plyr::rbind.fill(dflist),
  dplyr::bind_rows(dflist),
  data.table::rbindlist(dflist),
  plyr::ldply(dflist,data.frame),
  do.call("rbind",dflist),
  times=1000)

ggplot2::autoplot(mb)+theme_bw()


R version 3.4.0 (2017-04-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

> packageVersion("plyr")
[1] ‘1.8.4’
> packageVersion("dplyr")
[1] ‘0.7.2’
> packageVersion("data.table")
[1] ‘1.10.4’

更新:2019 年 8 月 6 日重新运行。

set.seed(21)
library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
  plyr::rbind.fill(dflist),
  dplyr::bind_rows(dflist),
  data.table::rbindlist(dflist),
  plyr::ldply(dflist,data.frame),
  do.call("rbind",dflist),
  purrr::map_df(dflist,dplyr::bind_rows),
  times=1000)

ggplot2::autoplot(mb)+theme_bw()

R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so

packageVersion("plyr")
packageVersion("dplyr")
packageVersion("data.table")
packageVersion("purrr")

>> packageVersion("plyr")
[1] ‘1.8.4’
>> packageVersion("dplyr")
[1] ‘0.8.3’
>> packageVersion("data.table")
[1] ‘1.12.2’
>> packageVersion("purrr")
[1] ‘0.3.2’

更新:2021 年 11 月 18 日重新运行。

set.seed(21)
library(microbenchmark)

dflist <- vector(length=10,mode="list")
for(i in 1:100)
{
  dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                            c=rep(LETTERS,10),d=rep(LETTERS,10))
}


mb <- microbenchmark(
  plyr::rbind.fill(dflist),
  dplyr::bind_rows(dflist),
  data.table::rbindlist(dflist),
  plyr::ldply(dflist,data.frame),
  do.call("rbind",dflist),
  Reduce("rbind",dflist),
  purrr::map_df(dflist,dplyr::bind_rows),
  times=1000)

ggplot2::autoplot(mb)+theme_bw()

R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19043)

>packageVersion("plyr")
[1] ‘1.8.6’
> packageVersion("dplyr")
[1] ‘1.0.7’
> packageVersion("data.table")
[1] ‘1.14.2’
> packageVersion("purrr")
[1] ‘0.3.4’

【讨论】:

  • 这是一个很好的答案。我运行了同样的东西(同样的操作系统,同样的包,不同的随机化,因为你没有set.seed),但是在最坏情况下的性能上看到了一些差异。 rbindlist 在我的结果中实际上有最好的最坏情况和最好的典型情况
【解决方案5】:

dplyr中还有bind_rows(x, ...)

> system.time({ df.Base <- do.call("rbind", listOfDataFrames) })
   user  system elapsed 
   0.08    0.00    0.07 
> 
> system.time({ df.dplyr <- as.data.frame(bind_rows(listOfDataFrames)) })
   user  system elapsed 
   0.01    0.00    0.02 
> 
> identical(df.Base, df.dplyr)
[1] TRUE

【讨论】:

  • 从技术上讲,您不需要 as.data.frame - 所有这些都使它成为一个 data.frame,而不是 table_df(来自 deplyr)
【解决方案6】:

这是可以做到的另一种方法(只需将其添加到答案中,因为reduce 是一个非常有效的功能工具,它经常被忽视作为循环的替代品。在这种特殊情况下,这些都没有比做快得多.call)

使用基础 R:

df <- Reduce(rbind, listOfDataFrames)

或者,使用 tidyverse:

library(tidyverse) # or, library(dplyr); library(purrr)
df <- listOfDataFrames %>% reduce(bind_rows)

【讨论】:

    【解决方案7】:

    在tidyverse中应该怎么做:

    df.dplyr.purrr <- listOfDataFrames %>% map_df(bind_rows)
    

    【讨论】:

    • 如果bind_rows 可以获取数据帧列表,为什么还要使用map
    【解决方案8】:

    data.table 的解决方案唯一缺少的是标识符列,用于了解数据来自列表中的哪个数据框。

    类似这样的:

    df_id <- data.table::rbindlist(listOfDataFrames, idcol = TRUE)
    

    idcol 参数添加一列 (.id),标识列表中包含的数据框的来源。结果看起来像这样:

    .id a         b           c
    1   u   -0.05315128 -1.31975849 
    1   b   -1.00404849 1.15257952  
    1   y   1.17478229  -0.91043925 
    1   q   -1.65488899 0.05846295  
    1   c   -1.43730524 0.95245909  
    1   b   0.56434313  0.93813197  
    

    【讨论】:

      【解决方案9】:

      为那些想要比较最近的一些答案的人提供更新的视觉效果(我想将 purrr 与 dplyr 解决方案进行比较)。基本上我结合了@TheVTM 和@rmf 的答案。

      代码:

      library(microbenchmark)
      library(data.table)
      library(tidyverse)
      
      dflist <- vector(length=10,mode="list")
      for(i in 1:100)
      {
        dflist[[i]] <- data.frame(a=runif(n=260),b=runif(n=260),
                                  c=rep(LETTERS,10),d=rep(LETTERS,10))
      }
      
      
      mb <- microbenchmark(
        dplyr::bind_rows(dflist),
        data.table::rbindlist(dflist),
        purrr::map_df(dflist, bind_rows),
        do.call("rbind",dflist),
        times=500)
      
      ggplot2::autoplot(mb)
      

      会话信息:

      sessionInfo()
      R version 3.4.1 (2017-06-30)
      Platform: x86_64-w64-mingw32/x64 (64-bit)
      Running under: Windows 7 x64 (build 7601) Service Pack 1
      

      软件包版本:

      > packageVersion("tidyverse")
      [1] ‘1.1.1’
      > packageVersion("data.table")
      [1] ‘1.10.0’
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多