我有一个要合并的 tibbles 列表(长度 = 5000)。他们都有相同的列,所以我想合并使用dplyr::bind_rows. 从表面上看,每个添加的小标题绑定行非常快,但是随着添加更多小标题,执行时间呈指数增长而不是线性增长。做了一些谷歌搜索后,它非常像这里观察到的错误:https : //github.com/tidyverse/dplyr/issues/1396。尽管应该在 bind_rows 内部修复了该错误,但我仍然看到每个 tibble 所用的时间呈指数增长。
library(foreach)
library(tidyverse)
set.seed(123456)
tibbles <- foreach(i = 1:200) %do% {
tibble(a = rnorm(10000),
b = rep(letters[1:25], 400),
c = rnorm(10000))
}
times <- foreach(i = 1:200) %do% {
system.time(tibbles[1:i] %>%
purrr::reduce(bind_rows))
}
times %>%
map_dbl(.f = ~.x[3]) %>%
plot(ylab = "time [s] per added tibble")
Run Code Online (Sandbox Code Playgroud)
任何想法为什么会出现这种情况以及如何解决它?
谢谢。
我的猜测是,每次调用时rbind,R 都必须分配一组新的列并复制数据。这将导致时间成倍增加。
尝试预先分配列:
system.time({
n <- vapply(tibbles, nrow, 0)
ntot <- sum(n)
cols <- list(a = numeric(ntot), b = character(ntot), c = numeric(ntot))
off <- 0
for (i in seq_along(tibbles)) {
ix <- off + seq_len(n[[i]])
for (j in seq_along(cols)) {
cols[[j]][ix] <- tibbles[[i]][[j]]
}
off <- off + n[[i]]
}
result <- as_tibble(cols)
})
#> user system elapsed
#> 0.073 0.012 0.085
Run Code Online (Sandbox Code Playgroud)
与方法比较purrr::reduce:
system.time(tibbles[1:200] %>% purrr::reduce(bind_rows))
#> user system elapsed
#> 4.888 2.013 6.928
Run Code Online (Sandbox Code Playgroud)
尽管,正如 aosmith 指出的,在你的情况下最好只使用bind_rows:
system.time(result <- bind_rows(tibbles))
#> user system elapsed
#> 0.039 0.005 0.044
Run Code Online (Sandbox Code Playgroud)