了解 R 如何将绘图分配为对象

Understanding how R assigns plots as objects

我在这里看到很多与我的问题无关的帖子,但没有完全切中要害。对不起,如果我错过了其他地方的解决方案。

我注意到 R 似乎将不同类型的图分配为不同类型的对象或分配为 NULL,但我不确定为什么。你能帮我打开包装吗?目标是简单地将绘图保存为一个对象,稍后可以在各种降价文件中显示,但我不需要比基本打印更复杂的东西——没有格子等。

例如

# Toy data setup 
set.seed(63)
d <- rnorm(100)

基本直方图在单独调用对象时不显示,但在包装在 plot() 函数中时显示。

hist(d)       # works as expected
p1 <- hist(d) # saves as a "List of 6" with class "histogram"
p1; print(p1) # both print all data in the list but no histogram plot
plot(p1)      # shows histogram plot

使用 plot 函数的变化保存为 NULL

plot(d, type="h") # works as expected
p2 <- plot(d) # saves as "NULL (empty)"
p2; print(p2) # both print NULL as expected given how it saved
plot(p2)      # generates error message, as expected

ggplot 方法也可以按预期保存和工作

library(tidyverse)
ggplot() +aes(d) + geom_histogram()  # works as expected with message info
p3 <- ggplot() +aes(d) + geom_histogram()  # saves as "List of 9" with class "gg" and "ggplot"
p3  # shows histogram as expected

所以有 3 种不同的方式来显示基本直方图(如)图,并且在尝试将图分配为稍后调用的对象时所有行为都不同。即使保存为对象的 hist() 和 ggplot() 在调用时显示不同,即使它们都保存为列表。

我错过了什么?

为了让事情变得更复杂一点,我注意到如果我将图(如下面的直方图)重叠成一个图,则分配的对象不会同时包含两个图。我可以看到这对于保存拟合线、添加的点或文本等也很有用。

我需要做什么才能将添加的绘图保存到绘图对象中? 以下示例代码来自R Bloggers.

#Random numbers
h2<-rnorm(1000,4)
h1<-rnorm(1000,6)
# Histogram Colored (blue and red)
hist(h1, col=rgb(1,0,0,0.5),xlim=c(0,10), ylim=c(0,200), main="Overlapping Histogram", xlab="Variable")
hist(h2, col=rgb(0,0,1,0.5), add=T)

它们按预期工作。但是,如果我在将第一个分配给对象后用分号连接它们,则第一个直方图修改消失并且重叠直方图丢失。重叠直方图消失是有道理的,因为第二个命令实际上并未应用于保存的直方图对象,但我很好奇如何添加它。

具有创建图表的副作用的函数可能会也可能不会 return 什么。例如,对于 ggplot2 包,它 return 是一个复杂的列表结构,具有足够的信息和预定义的属性,(1) print 将生成一个图形对象 ("grob"), 或 (2) 可以添加更多层或更改此列表结构的属性。例如,

library(ggplot2)
gg <- ggplot(mtcars, aes(mpg,disp)) + geom_point()
str(gg, max.level=1)
# List of 9
#  $ data       :'data.frame':  32 obs. of  11 variables:
#  $ layers     :List of 1
#  $ scales     :Classes 'ScalesList', 'ggproto', 'gg' <ggproto object: Class ScalesList, gg>
#     add: function
#     clone: function
#     find: function
#     get_scales: function
#     has_scale: function
#     input: function
#     n: function
#     non_position_scales: function
#     scales: list
#     super:  <ggproto object: Class ScalesList, gg> 
#  $ mapping    :List of 2
#   ..- attr(*, "class")= chr "uneval"
#  $ theme      : list()
#  $ coordinates:Classes 'CoordCartesian', 'Coord', 'ggproto', 'gg' <ggproto object: Class CoordCartesian, Coord, gg>
#     aspect: function
#     backtransform_range: function
#     clip: on
#     default: TRUE
#     distance: function
#     expand: TRUE
#     is_free: function
#     is_linear: function
#     labels: function
#     limits: list
#     modify_scales: function
#     range: function
#     render_axis_h: function
#     render_axis_v: function
#     render_bg: function
#     render_fg: function
#     setup_data: function
#     setup_layout: function
#     setup_panel_params: function
#     setup_params: function
#     transform: function
#     super:  <ggproto object: Class CoordCartesian, Coord, gg> 
#  $ facet      :Classes 'FacetNull', 'Facet', 'ggproto', 'gg' <ggproto object: Class FacetNull, Facet, gg>
#     compute_layout: function
#     draw_back: function
#     draw_front: function
#     draw_labels: function
#     draw_panels: function
#     finish_data: function
#     init_scales: function
#     map_data: function
#     params: list
#     setup_data: function
#     setup_params: function
#     shrink: TRUE
#     train_scales: function
#     vars: function
#     super:  <ggproto object: Class FacetNull, Facet, gg> 
#  $ plot_env   :<environment: R_GlobalEnv> 
#  $ labels     :List of 2
#  - attr(*, "class")= chr [1:2] "gg" "ggplot"

"Just a list."

非网格图形函数有时采用类似的策略。例如,hist 总是 return 一个命名的 list;如果您使用默认值 plot=TRUE,那么它 return 这个列表 不可见 并且一个副作用是创建一个情节。然而,对于 hist(..., plot=FALSE)list 被明显地 return 编辑并且没有创建绘图。类似于 ggplot2 用于 printggplot2:::print.ggplot2 和朋友)的 S3 方法,有一个用于 hist 的 return 对象的 S3 方法(class histogram), 命名为 graphics:::plot.histogram, 所以如果你这样做

h <- hist(mtcars$disp, plot = FALSE)
str(h)
# List of 6
#  $ breaks  : int [1:10] 50 100 150 200 250 300 350 400 450 500
#  $ counts  : int [1:9] 5 7 4 1 4 4 4 1 2
#  $ density : num [1:9] 0.003125 0.004375 0.0025 0.000625 0.0025 ...
#  $ mids    : num [1:9] 75 125 175 225 275 325 375 425 475
#  $ xname   : chr "mtcars$disp"
#  $ equidist: logi TRUE
#  - attr(*, "class")= chr "histogram"

然后一个简单的 plot(h) 将生成情节。 (即使您第一次使用默认值 h <- hist(..., plot=TRUE) 绘制它,您也可以稍后使用 plot(h) 重新生成该图。)

但不是所有的绘图函数或它们的附属函数return。 linespoints,例如,总是 return NULL。您不能 "capture" 来自 lines 的输出并在以后(重新)应用它。

但据我所知,没有基础 R 函数 "returns" 绘图对象。 grid 函数可能,特别是那些修改 grobs 的函数。

如果你想 "save" 绘图本身(而不是 list 或用于创建它的组件),然后查看 ?recordPlot,它可以是 运行 紧跟在任何基本图形函数之后(包括附属函数 linespoints 等)。