我正在尝试构建一个使用函数生成多个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)
有谁知道为什么会这样?我是否从函数中错误地返回了对象?
这个很棘手.我最初的建议是使用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 /无论如何)而不是绘图对象本身......尽管绘图对象肯定更灵活.