内部创建的ggplot2对象与外部函数之间的RDS文件大小差异

bc_*_*ana 4 r ggplot2

我正在尝试构建一个使用函数生成多个ggplot2对象的R项目.但是,我注意到,将这些对象保存为RDS文件时,文件大小比我预期的要大得多.我意识到,保存使用函数生成的RDS对象以及全局环境中的相同图,尽管在R会话中占用了相同的内存,但仍会提供两种截然不同的文件大小.例如:

library(ggplot2)
data <- data.frame(x = rnorm(1e6))

p1 <- ggplot(data) + 
  geom_histogram(aes(x = x))

plot_fun <- function(y) {
  p <- ggplot(y) +
    geom_histogram(aes(x = x))
  return(p)
}

p2 <- plot_fun(data)

object.size(p1) # 8 Mb
object.size(p2) # 8 Mb

saveRDS(p1, "plot1.rds")
saveRDS(p2, "plot2.rds")

file.info("plot1.rds", "plot2.rds")
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么会这样?我是否从函数中错误地返回了对象?

Ben*_*ker 7

这个很棘手.我最初的建议是使用pryr::object_size(),它更全面地包括存储在对象环境中的对象的大小,但是这显示了两个ggplot对象之间的微小差异.

但是,ggplot对象包含一个环境,该$plot_env组件的内容将与该对象一起存储.

p2$plot_env对应于函数内部的环境:

ls(p2$plot_env)
# [1] "p" "y"
Run Code Online (Sandbox Code Playgroud)

而环境p1$plot_env全球环境,其中包含数据的副本以及其他绘图对象......

ls(p1$plot_env)
# [1] "data"     "p1"       "p2"       "plot_fun"
Run Code Online (Sandbox Code Playgroud)

但这对我来说似乎仍然有些神秘.p1(在其环境中有更多对象)创建较小的文件大小(7.4M),而p2(使用较少的对象)创建较大的文件大小(22M),并且p1天真地似乎存储了更多的东西:

sapply(p1$plot_env,object.size)
## plot_fun       p1       p2     data 
##     6568  8004632  8004632  8000728 
sapply(p2$plot_env,object.size)
##       p       y 
## 8004632 8000728 
Run Code Online (Sandbox Code Playgroud)

这是某种递归噩梦,环境引用其他环境,所有环境都必须存储?正如@Chris所说:

p2环境有一个全球环境的父环境,而p1环境就是全球环境......我想知道当R需要序列化一个继承自另一个环境的环境时(即,一个父env),它保存父env和孩子.这可以解释为什么保存p1会导致较小的文件大小p2

如果我用p2全局环境替换绘图环境,文件大小确实会变小......我认为我没有打破绘图对象.

p2$plot_env <- p1$plot_env
saveRDS(p2, "plot2.rds")
system("ls -lht plot?.rds")
## -rw-r--r--  1 bolker  staff   7.4M 15 Jun 20:15 plot2.rds
## -rw-r--r--  1 bolker  staff   7.4M 15 Jun 20:14 plot1.rds
Run Code Online (Sandbox Code Playgroud)

如果您的工作流程允许,您可以考虑存储这些图的渲染版本(如PDF/SVG /无论如何)而不是绘图对象本身......尽管绘图对象肯定更灵活.