将dataframe列表转换为一个dataframe

我有一个代码,在一个地方结束了一个dataframe列表,我真的想要转换成一个单一的大数据框架。

我从一个早期的问题中得到了一些指导,试图做类似的事情,但是更复杂一些。

这里是我开始的一个例子(这是非常简单的说明):

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) 

另外一个select是使用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)使用matrix而不是data.frames和(b)预先分配最终matrix并分配给它,而不是增长它。

编辑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更容易,而且稍微更快(这些时间超过多次运行)。 据我所知, github上的plyr版本甚至比这更快。

为了完整起见,我认为这个问题的答案需要更新。 “我的猜测是,使用do.call("rbind", ...)将会是最快的方法,你会发现…”2010年5月和一段时间以后,但大概在2011年9月左右在data.table package 1.8.2中引入了一个新的函数rbindlist ,其中有一句话“ do.call("rbind",l) ,但是速度要快得多。 快多less?

 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 

bind_rows(x, ...)中还有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 

绑定积

码:

 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' 

还有一种方法可以完成(只是将其添加到答案中,因为reduce是一个非常有效的函数工具,通常被忽略为循环的替代品,在这种情况下,这两个函数都不比do.call快得多)

使用基数R:

 df <- Reduce(rbind, listOfDataFrames) 

或者使用反转:

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

应该如何做到这一点:

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

data.table解决schemedata.table缺less的是标识符列,以了解列表中哪些dataframe来自数据。

像这样的东西:

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

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

 .id abc 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 

对于那些想要比较一些最近的答案(我想比较purrr与dplyr解决scheme)的更新视觉。 基本上我结合了@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'