我正在尝试构建一个使用函数生成多个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")
有谁知道为什么会这样?我是否从函数中错误地返回了对象?
答案 0 :(得分:6)
这个很棘手。我最初的建议是使用pryr::object_size()
,它更全面地包含存储在对象环境中的对象的大小,但这只显示了两个ggplot
对象之间的微小差异。
但是,ggplot
个对象包含一个环境,$plot_env
组件,其内容将与对象一起存储。
p2$plot_env
的环境与您的函数内部对应:
ls(p2$plot_env)
# [1] "p" "y"
而p1$plot_env
的环境是全局环境,其中包含数据的副本以及其他绘图对象......
ls(p1$plot_env)
# [1] "data" "p1" "p2" "plot_fun"
但这对我来说似乎仍然有些神秘。 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
这是某种递归的噩梦吗?环境引用其他环境,所有环境都必须存储?正如@Chris所说:
相比,保存
p2
的环境具有全球环境的父环境,而p1
的环境是全球环境......我想知道什么是发生的是,当R需要序列化从另一个env(即父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
如果你的工作流允许,你可以考虑存储这些图的渲染版本(如PDF / SVG /无论什么)而不是绘图对象本身......虽然绘图对象肯定更灵活。
答案 1 :(得分:1)
如果要获得对象的准确尺寸,请使用:
length(serialize(p1,NULL))
。
如上所述,这种差异来自环境。