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

coc*_*mas 5 import zip r

有没有理由我无法直接从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")
Run Code Online (Sandbox Code Playgroud)

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

iris2 <- read.csv(unz("datasets.zip", "iris.csv"))
Run Code Online (Sandbox Code Playgroud)

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

iris3 <- readRDS(unz("datasets.zip", "iris.rds"))
Run Code Online (Sandbox Code Playgroud)

这给了我错误:

Error: unknown input format
Run Code Online (Sandbox Code Playgroud)

我也想知道为什么会这样.我知道我可以做以下事情,就像这个问题一样:

path <- unzip("datasets.zip", "iris.rds")
iris4 <- readRDS(path)
file.remove(path)
Run Code Online (Sandbox Code Playgroud)

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

Rei*_*son 9

这是一个有点棘手的追踪,直到我读到的身体readRDS().你需要做的是

  1. 使用打开与.zip存档及其中文件的连接unz()
  2. 使用GZIP解压缩到此连接 gzcon()
  3. 最后将这个解压缩的连接传递给readRDS().

下面是一个示例,用于说明mat在zip存档中使用以下序列化矩阵matrix.zip

mat <- matrix(1:9, ncol = 3)
saveRDS(mat, "matrix.rds")
zip("matrix.zip", "matrix.rds")
Run Code Online (Sandbox Code Playgroud)

打开连接 matrix.zip

con <- unz("matrix.zip", filename = "matrix.rds")
Run Code Online (Sandbox Code Playgroud)

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

con2 <- gzcon(con)
Run Code Online (Sandbox Code Playgroud)

最后,从连接中读取

mat2 <- readRDS(con2)
Run Code Online (Sandbox Code Playgroud)

完全我们有

con <- unz("matrix.zip", filename = "matrix.rds")
con2 <- gzcon(con)
mat2 <- readRDS(con2)
close(con2)
Run Code Online (Sandbox Code Playgroud)

这给了

> 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
Run Code Online (Sandbox Code Playgroud)

为什么?

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

压缩由file文件名时打开的连接处理,因此只有在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>
Run Code Online (Sandbox Code Playgroud)

如果file是文件名的字符串,则使用该对象解压缩gzile()以创建与.rds我们要读取的连接.请注意,如果您file按照自己的意愿传递连接,则R在任何时候都不会解压缩连接.file只是分配给con然后传递给内部函数unserializeFromConn.因此缠绕工作gzcon()创建的连接unz.

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