如何使用 saveRDS(..., refhook = ) 参数?

akr*_*ica 9 serialization r

我有一个复杂的列表对象,一个建模函数 (asreml) 的输出。该对象包含各种数据类型,包括附加了环境的函数和公式。我不想将环境保存到 RDS,因为它们非常大而且我保存了很多模型。

refhook=serializesaveRDS函数中遇到了参数。文档说:

refhook 函数可用于自定义非系统引用对象(所有外部指针和弱引用,以及命名空间和包环境以及 .GlobalEnv 以外的所有环境)的处理。serialize 的钩子函数应该为它要处理的引用返回一个字符向量;否则它应该返回NULL。

鉴于此示例模型

e <- new.env()
e$a = rnorm(10)
l <- list(a = e, b = 42)
Run Code Online (Sandbox Code Playgroud)

refhook 函数确实显示了一些效果。当我定义一个返回字符的函数时,输出变小,表明环境没有得到保存:

length(serialize(l, connection = NULL))
[1] 338

s <- serialize(l, 
  connection = NULL, 
  refhook = function(x) "")
length(s)
[1] 109
Run Code Online (Sandbox Code Playgroud)

但是,我无法读取结果对象:

unserialize(s)

Error in unserialize(s) : 
  no restore method available
Run Code Online (Sandbox Code Playgroud)

我还尝试了原始向量输出,怀疑 refhook 可能会提供替代的序列化输出,但这不起作用:

s2 <- serialize(l,
  connection = NULL, 
  refhook = function(x) 
    serialize("env", connection = NULL)))

Error in serialize(l, con = NULL, refhook = function(x) serialize("env",  : 
  assertion 'TYPEOF(t) == STRSXP && LENGTH(t) > 0' failed: file 'serialize.c', line 982
Run Code Online (Sandbox Code Playgroud)

我如何使用refhook=?这个函数期望输出什么字符?

akr*_*ica 8

啊,我自己发现了。错误“没有可用的恢复方法”意味着您忘记为该unserialize函数添加重新挂钩。你需要两个,重新挂接serializeunserialize

serialize在返回什么字符串中的 refhook是完全自由的。唯一需要了解结果的是unserialize.

示例:序列化和还原包含集中存储的环境的列表

生成环境存储库。让我们假设这些来自外部源并且它们的内容不需要被序列化。要恢复它们,只需重新读取外部数据源。

repo <- list()
for(i in 1:10){
  repo[[i]] <- new.env()
  repo[[i]]$a <- rnorm(1e6)
}
Run Code Online (Sandbox Code Playgroud)

一种环境是 8 MB 大。我们不想在序列化输出中包含所有这些数据,因为它已经永久保存在repo.

object.size(repo[[1]]$a)
Run Code Online (Sandbox Code Playgroud)

这是我们要序列化的列表。它包含存储库中的第二个环境。我们只想存储数值b。对于环境,我们只想存储它是存储库中的环境 2。我们不想序列化内容,因为存储库已经有了它们。

l <- list(a = repo[[2]], b = 42)
Run Code Online (Sandbox Code Playgroud)

这是序列化的重新挂钩。它在索引中查找环境并只存储索引。

ser <- function(e){
  for(i in seq_along(repo)){
    if(identical(e, repo[[i]])){
      message("Identified environment #",i)
      return(as.character(i)) # Just save the 
    }
  }
  message("Environment not found in the repository")
  return(NULL)
}
Run Code Online (Sandbox Code Playgroud)

反序列化的相应 refhook 获取索引并从repo以下位置加载相应的环境:

unser <- function(s){
  i <- as.numeric(s)
  return(repo[[i]])
}
Run Code Online (Sandbox Code Playgroud)

这在序列化输出中节省了大量空间

反序列化时从数据库加载环境

u <- unserialize(s, refhook = unser)
## $a
## <environment: 0x000000001c91a118>
## 
## $b
## [1] 42
Run Code Online (Sandbox Code Playgroud)

  • 这非常有用,但是如何将它与 readRDS() 或 saveRDS() 一起使用? (2认同)
  • @RaphaelK refhook 的用法完全相同。区别仅在于,serialize 返回一个 R 对象(原始向量),但 saveRDS 写入一个文件。相反,unserialize 需要原始向量作为第一个参数,而 readRDS 需要文件名 (2认同)