如何在R中创建一个数据加载进度条?

26

使用 load() 在 R 中加载数据,能否创建一个进度条?

对于一个大型数据分析项目,从 .RData 文件中加载大矩阵需要几分钟的时间。我想要一个进度条来监控数据加载还有多久完成。R 已经内置了很好的 进度条 功能,但是 load() 没有钩子函数来监视已读取的数据量。如果不能直接使用 load,是否有间接的方式可以创建这样一个进度条?也许将 .RData 文件分块加载并在 R 中进行合并。大家对此有什么想法或建议吗?


我不知道你如何制作进度条,但你是否考虑过至少显示一个计时器?我发现运行中的计时器可以让等待时间更快地过去,然后我就知道程序仍在响应。你可以显示一条消息,例如“您已经等待了1:32,通常需要等待约3分钟。去拿杯咖啡吧!” - Tommy O'Dell
之前有两个相关的问题:https://dev59.com/u2035IYBdhLWcg3wbPfc#6170107 和 https://dev59.com/5FDTa4cB1Zd3GeqPMMyb,建议使用 txtProgressBargtkProgressBar。后者是来自 RGtk2 包。这些是否符合您的需求? - jthetzel
抱歉,我没有注意到您已经知道 txtProgressBar 函数了,而且您的问题实际上是关于加载 .Rdata 文件的。 - jthetzel
加载还没有进度条的挂钩但是 - R是开源的,所以你可以通过一点编程来添加它们... - Spacedman
2个回答

13

我想出了以下解决方案,适用于小于2^32-1字节的文件大小。

R对象需要进行序列化并保存到文件中,可以使用以下代码来实现。

saveObj <- function(object, file.name){
    outfile <- file(file.name, "wb")
    serialize(object, outfile)
    close(outfile)
}

然后我们分块读取二进制数据,记录已经读取了多少并相应地更新进度条。

loadObj <- function(file.name){
    library(foreach)
    filesize <- file.info(file.name)$size
    chunksize <- ceiling(filesize / 100)
    pb <- txtProgressBar(min = 0, max = 100, style=3)
    infile <- file(file.name, "rb")
    data <- foreach(it = icount(100), .combine = c) %do% {
        setTxtProgressBar(pb, it)
        readBin(infile, "raw", chunksize)
    }
    close(infile)
    close(pb)
    return(unserialize(data))
}

这段代码可以按以下方式运行:

> a <- 1:100000000
> saveObj(a, "temp.RData")
> b <- loadObj("temp.RData")
  |======================================================================| 100%
> all.equal(b, a)
[1] TRUE

如果我们将进度条方法与一次读取整个文件相比较,我们会发现进度条方法稍微慢了一些,但这并不值得担心。

> system.time(unserialize(readBin(infile, "raw", file.info("temp.RData")$size)))
   user  system elapsed
  2.710   0.340   3.062
> system.time(b <- loadObj("temp.RData"))
  |======================================================================| 100%
   user  system elapsed
  3.750   0.400   4.154

虽然上述方法是可行的,但由于文件大小限制,我认为它是完全无用的。进度条只对需要长时间读取的大文件有用。

如果有人能提出比这个解决方案更好的方法,那就太好了!


3
我可以建议加快加载(和保存)时间,这样就不需要进度条了。如果读取一个矩阵很“快”,那么您可以在每个读取矩阵之间报告进度(如果有多个矩阵)。
这里是一些测量数据。通过简单地设置compress=FALSE,加载速度会加倍。但是通过编写一个简单的矩阵序列化程序,加载速度几乎快了20倍。
x <- matrix(runif(1e7), 1e5) # Matrix with 100k rows and 100 columns

system.time( save('x', file='c:/foo.bin') ) # 13.26 seconds
system.time( load(file='c:/foo.bin') ) # 2.03 seconds

system.time( save('x', file='c:/foo.bin', compress=FALSE) ) # 0.86 seconds
system.time( load(file='c:/foo.bin') ) # 0.92 seconds

system.time( saveMatrix(x, 'c:/foo.bin') ) # 0.70 seconds
system.time( y <- loadMatrix('c:/foo.bin') ) # 0.11 seconds !!!
identical(x,y)

saveMatrix/loadMatrix的定义如下。它们目前不处理dimnames和其他属性,但这很容易添加。

saveMatrix <- function(m, fileName) {
    con <- file(fileName, 'wb')
    on.exit(close(con))
    writeBin(dim(m), con)
    writeBin(typeof(m), con)
    writeBin(c(m), con)
}

loadMatrix <- function(fileName) {
    con <- file(fileName, 'rb')
    on.exit(close(con))
    d <- readBin(con, 'integer', 2)
    type <- readBin(con, 'character', 1)
    structure(readBin(con, type, prod(d)), dim=d)
}

我们只需要加载一个尺寸为几个(3+) gigabytes的矩阵。我考虑将矩阵分成几部分,然后分别读取它们,但这是一种丑陋的解决方案, 并不值得为了一个进度条增加复杂性。 - Nixuz
...那么使用上面的loadMatrix应该会显著加快速度...你试过了吗? - Tommy
bigmemory库用于保存和加载非常大的数值矩阵,也是一个很好且快速的解决方案。 - Nixuz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接