提问人:JD Long 提问时间:5/18/2010 最后编辑:HenrikJD Long 更新时间:7/10/2023 访问量:345689
将数据框列表逐行合并为一个数据帧
Combine a list of data frames into one data frame by row
问:
我的代码在一个地方最终得到了一个数据帧列表,我真的想将其转换为单个大数据帧。
我从之前的一个问题中得到了一些建议,该问题试图做一些类似但更复杂的事情。
这是我开始的一个例子(为了说明这一点,这被大大简化了):
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)
答:
另一种选择是使用 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
我的猜测是,除非您可以执行以下操作,否则使用将是您将找到的最快的方法:(a) 使用矩阵而不是 data.frames,以及 (b) 预分配最终矩阵并分配给它而不是增长它。do.call("rbind", ...)
编辑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
版本甚至比这更快。
评论
I()
可以在您的通话中替换data.frame
ldply
melt.list
为了完整起见,我认为这个问题的答案需要更新。“我的猜测是,使用将是你会发现的最快的方法......”2010 年 5 月及之后的一段时间可能是正确的,但在 2011 年 9 月左右,软件包版本 1.8.2 中引入了一个新函数,并注释“这与 相同,但速度要快得多”。快多少?do.call("rbind", ...)
rbindlist
data.table
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
评论
ldply
rbindlist
dplyr::rbind_all(listOfDataFrames)
rbindlist
do.call()
还有。bind_rows(x, ...)
dplyr
> 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’
更新: 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’
更新:重新运行06-Aug-2019。
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’
更新:2023 年 12 月 14 日重新运行。R 和包的更新版本,但同一台计算机。还添加了 Mael 的答案和包。collapse
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),
collapse::unlist2d(dflist),
times=1000)
ggplot2::autoplot(mb)+ggplot2::theme_bw()
Unit: microseconds
expr min lq mean median uq max neval
plyr::rbind.fill(dflist) 7146.5 7514.00 8270.0111 7753.80 8112.95 23541.9 1000
dplyr::bind_rows(dflist) 1319.7 1476.35 1772.7105 1566.60 1668.40 13891.0 1000
data.table::rbindlist(dflist) 466.8 603.20 828.2240 661.35 713.50 85147.2 1000
plyr::ldply(dflist, data.frame) 14537.2 15161.20 16701.7973 15556.20 16417.50 101030.9 1000
do.call("rbind", dflist) 9345.5 9851.10 12995.3883 10408.55 16046.65 110697.6 1000
Reduce("rbind", dflist) 71055.8 78980.90 97294.2492 83496.15 91894.60 285057.9 1000
purrr::map_df(dflist, dplyr::bind_rows) 15097.9 15654.65 17361.9197 16020.10 16681.60 660603.4 1000
collapse::unlist2d(dflist) 186.7 254.80 528.7272 312.90 351.90 88946.8 1000
R version 4.3.2 (2023-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19045)
> packageVersion("plyr")
[1] ‘1.8.9’
> packageVersion("dplyr")
[1] ‘1.1.4’
> packageVersion("data.table")
[1] ‘1.14.10’
> packageVersion("purrr")
[1] ‘1.0.2’
> packageVersion("collapse")
[1] ‘2.0.7’
> packageVersion("microbenchmark")
[1] ‘1.4.10’
评论
set.seed
rbindlist
在整洁中应该如何做:
df.dplyr.purrr <- listOfDataFrames %>% map_df(bind_rows)
评论
map
bind_rows
*_df()
这是另一种可以完成的方法(只是将其添加到答案中,因为这是一个非常有效的功能工具,经常被忽视为循环的替代品。在这种特殊情况下,它们都不比 do.call 快得多)reduce
使用基数 R:
df <- Reduce(rbind, listOfDataFrames)
或者,使用 Tidyverse:
library(tidyverse) # or, library(dplyr); library(purrr)
df <- listOfDataFrames %>% reduce(bind_rows)
评论
解决方案唯一缺少的是标识符列,用于了解数据来自列表中的哪个数据帧。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
对于那些想要比较一些最近的答案的人来说,这是一个更新的视觉对象(我想将咕噜声与 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’
从 dplyr 包中使用:bind_rows()
bind_rows(list_of_dataframes, .id = "column_label")
评论
.id = "column_label"
dplyr
一个非常快速的选项是 C 编写的:collapse::unlist2d
library(collapse)
unlist2d(listOfDataFrames)
由于 ,另一个选项是 (取代 和 (部分) ):purrr 1.0.0
list_rbind
flatten_dfr
map_dfr
library(purrr)
list_rbind(listOfDataFrames, names_to = "column_label")
unlist2d
比任何其他选项都快:
library(microbenchmark)
mb <- microbenchmark(
bind_rows = dplyr::bind_rows(listOfDataFrames),
rbindlist = data.table::rbindlist(listOfDataFrames),
do.call = do.call("rbind", listOfDataFrames),
list_rbind = list_rbind(listOfDataFrames),
unlist2d = unlist2d(listOfDataFrames), times = 1000)
# Unit: microseconds
# expr min lq mean median uq max neval
# bind_rows 1590.601 2139.7010 2807.0167 2335.2510 2717.3510 51773.4 1000
# rbindlist 613.401 890.6015 1438.0510 1012.7505 1318.3015 13893.4 1000
# do.call 12009.201 19973.7010 25526.6986 22102.8005 25239.9510 151914.2 1000
# list_rbind 1287.401 1781.1510 2510.0104 1970.5515 2282.3515 115803.6 1000
# unlist2d 245.401 400.3015 937.1731 488.3005 690.5015 12683.0 1000
下一个:测试字符串中是否包含字符
评论
do.call("rbind", list)
unlist