在zip文件中读取RDS文件而不解压缩到磁盘

时间:2015-10-22 20:20:31

标签: r import zip

有没有理由我无法直接从zip文件中读取RDS文件,而不必先将其解压缩到磁盘上的临时文件中?

假设这是zip文件:

saveRDS(cars, "cars.rds")
saveRDS(iris, "iris.rds")
write.csv(iris, "iris.csv")
zip("datasets.zip", c("cars.rds", "iris.rds", "iris.csv"))
file.remove("cars.rds", "iris.rds", "iris.csv")

对于csv文件,我可以直接读取它:

iris2 <- read.csv(unz("datasets.zip", "iris.csv"))

但是,我不明白为什么我不能unz()直接使用readRDS()

iris3 <- readRDS(unz("datasets.zip", "iris.rds"))

这给了我错误:

Error: unknown input format

我也想了解为什么会这样。我知道我可以执行以下操作,如this question

path <- unzip("datasets.zip", "iris.rds")
iris4 <- readRDS(path)
file.remove(path)

但这似乎并不高效,而且我需要经常为大量文件执行此操作,因此I / O效率低下很重要。是否有任何解决方法来读取rds文件而不将其提取到磁盘?

2 个答案:

答案 0 :(得分:8)

在我阅读readRDS()的正文之前,这有点棘手。你似乎需要做的是

  1. 使用.zip
  2. 打开与unz()存档及其中文件的连接
  3. 使用gzcon()
  4. 将GZIP解压缩应用于此连接
  5. 最后将此解压缩连接传递给readRDS()
  6. 以下是一个示例,用于说明在zip存档中使用以下序列化矩阵mat matrix.zip

    mat <- matrix(1:9, ncol = 3)
    saveRDS(mat, "matrix.rds")
    zip("matrix.zip", "matrix.rds")
    

    打开与matrix.zip

    的连接
    con <- unz("matrix.zip", filename = "matrix.rds")
    

    现在,使用gzcon(),将GZIP解压缩应用于此连接

    con2 <- gzcon(con)
    

    最后,从连接中读取

    mat2 <- readRDS(con2)
    

    完全我们有

    con <- unz("matrix.zip", filename = "matrix.rds")
    con2 <- gzcon(con)
    mat2 <- readRDS(con2)
    close(con2)
    

    这给出了

    > con <- unz("matrix.zip", filename = "matrix.rds")
    > con2 <- gzcon(con)
    > mat2 <- readRDS(con2)
    > close(con2)
    > mat2
         [,1] [,2] [,3]
    [1,]    1    4    7
    [2,]    2    5    8
    [3,]    3    6    9
    > all.equal(mat, mat2)
    [1] TRUE
    

    为什么?

    为什么你必须经历这个复杂的额外步骤(我认为)在?readRDS中描述:

      

    压缩由file为a时打开的连接处理        文件名,只有当file是连接时才可以        由连接处理。所以例如需要url个连接        被打包到gzcon

    如果你看一下readRDS()的内部,我们就会看到:

    > readRDS
    function (file, refhook = NULL) 
    {
        if (is.character(file)) {
            con <- gzfile(file, "rb")
            on.exit(close(con))
        }
        else if (inherits(file, "connection")) 
            con <- file
        else stop("bad 'file' argument")
        .Internal(unserializeFromConn(con, refhook))
    }
    <bytecode: 0x2841998>
    <environment: namespace:base>
    

    如果file是文件名的字符串,则使用gzile()解压缩对象,以创建与我们要读取的.rds的连接。请注意,如果您按照file的方式传递连接,则R在任何时候都不会对连接进行解压缩。 file刚刚分配到con,然后传递给内部函数unserializeFromConn。因此,在gzcon()创建的连接周围包裹unz

    基本上,当unserializeFromConn从连接读取时,它期望它被解压缩,但是当您传递readRDS()文件名而不是连接时,解压缩只会自动发生。

答案 1 :(得分:1)

readRDS()的签名是

saveRDS(object, file = "", ascii = FALSE, version = NULL,
    compress = TRUE, refhook = NULL)

但是,令人沮丧的是,readRDS的签名中没有任何内容。然而,当你阅读readRDS的文档时,你会得到这个小宝石

## or examine the object via a connection, which will be opened as needed.
con <- gzfile("women.rds")
readRDS(con)
close(con)

以及

## Less convenient ways to restore the object
## which demonstrate compatibility with unserialize()
con <- gzfile("women.rds", "rb")
identical(unserialize(con), women)
close(con)
con <- gzfile("women.rds", "rb")
wm <- readBin(con, "raw", n = 1e4) # size is a guess
close(con)
identical(unserialize(wm), women)

另一件需要考虑的事情是你将在RDS对象上使用压缩的收益。考虑

X <- matrix(rnorm(1e7), ncol=10)
saveRDS(X, file = "X.rds")
system("cp X.rds XZ.rds")
system("gzip XZ.rds")
uncomp <- file.info("X.rds")$size
comp <- file.info("XZ.rds.gz")$size
savings <- (1 - comp/uncomp)
# [1] -0.00030541

所以,对于一个非典型的例子,压缩RDS对象会耗费我们的空间。