r - rbindlist 列表长度和性能
问题描述
我正在运行一个脚本来从弹性搜索数据库下载大量数据,并且想知道将这些数据组合成单个数据帧的最有效方法是什么。
我正在使用 rbindlist 并注意到在要绑定的列表的长度(即一次绑定的 data.tables 的数量)和数据总量之间似乎有一个“最佳位置”。一方面,我试图避免将 data.tables 列表增长得太长,因为这会对性能产生不利影响。另一方面,太短的 data.tables 列表会使目标 data.table 增量增长且效率低下。对于大约 130.000 个条目的测试数据,我得到的最好结果(通过列表长度的反复试验)是在 11 分钟内下载并合并所有数据,一次绑定 1.000 个 data.tables。下载的数据以每帧 10 行为一组(通过 Elastic Search 滚动搜索),因此我们总共讨论了 13.000 个数据帧。不批量绑定(即
由于最终可能一次下载的完整数据目前大约有 24 个 mio 条目并且还在增加,我非常有兴趣使这个过程尽可能高效。
由于对 Elastic Search 数据的访问受到限制,因此我无法共享任何真实数据。被绑定的 data.frames 很小,大约有 10 行和 9 列。每次调用的行号可能不同(例如,当搜索结束时),列数取决于请求的数据。如果您需要有关已处理数据的更多信息,请告诉我,然后我可以尝试举个例子。这是我当前正在运行的代码(减去一些改进,例如进度包的 progress_bar 用于显示进度和当前下载的文件的数量,这些在这里并不重要):
res <- Search(conn, asdf = T, body = body, time_scroll = time_scroll)
out <- res$hits$hits
hits <- 1
list <- list()
list_length = 1000
while (hits != 0) { # keep going while there are still more results from the Search
res <- scroll(conn = conn, x = res$`_scroll_id`, asdf = T)
hits <- length(res$hits$hits)
if (hits > 0) {
list[length(list)+1] <- list(res$hits$hits) # make list of results to bind later
}
if (length(list)==list_length) { # bind results every x calls (~10 datapoints per call)
out <- rbindlist(c(list(out), list), fill = T)
list <- list()
}
if (length(list) <= list_length & hits == 0) { # bind leftovers
out <- rbindlist(c(list(out), list), fill = T)
list <- list()
}
}
所以我的问题有两个:
当使用 rbindlist() 为大量数据绑定数据帧时,是否有人对列表长度与数据总量的比率有任何经验?我的假设是这个比率会随着数据的变化而变化,因此任何经验(甚至可能是固定比率!)都会非常受欢迎。
关于这个具体的代码,有没有办法加快速度?由于最终 data.table 的总行数是已知的(Elastic 给出了命中总数),我认为它可能是预先分配最终 data.table 大小的一个选项。但是,我不知道如何在上述代码中实现这样的预分配,或者在使用 rbindlist 时是否有必要这样做。
解决方案
我已经对预分配进行了一些测试。data.frame 被填满的次数越多,它的速度似乎就越慢。所以 rbindlist() 似乎优于预分配数据。那个或我的预分配代码效率低下。
代码:
# initial search, sets query
res <- Search(conn, asdf = T, size = size, body = body, time_scroll = time_scroll)
# preallocate rows of data.frame
out <- res$hits$hits
out[nrow(out) + (total - nrow(res$hits$hits)),] <- NA
hits <- 1
while (hits != 0) {
res <- scroll(conn = conn, x = res$`_scroll_id`, asdf = T)
hits <- length(res$hits$hits)
out[(nrow(out[!is.na(out$"_id"),])+1):(nrow(out[!is.na(out$"_id"),]) + nrow(res$hits$hits)),] <- res$hits$hits # add results
}