首页 > 解决方案 > 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()
        }
}

所以我的问题有两个:

  1. 当使用 rbindlist() 为大量数据绑定数据帧时,是否有人对列表长度与数据总量的比率有任何经验?我的假设是这个比率会随着数据的变化而变化,因此任何经验(甚至可能是固定比率!)都会非常受欢迎。

  2. 关于这个具体的代码,有没有办法加快速度?由于最终 data.table 的总行数是已知的(Elastic 给出了命中总数),我认为它可能是预先分配最终 data.table 大小的一个选项。但是,我不知道如何在上述代码中实现这样的预分配,或者在使用 rbindlist 时是否有必要这样做。

标签: relasticsearchdata.table

解决方案


我已经对预分配进行了一些测试。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

    }

推荐阅读