R语言 如何避免ggplots中重复的图形对象,例如有时在PDF输出中看到的图形对象?

j2datikz  于 2023-04-03  发布在  其他
关注(0)|答案(1)|浏览(150)

过去,我注意到ggplot(和基本图形)在创建PDF文件(Prevent PDF storing invisible map data)时绘制“不可见”数据。
我刚刚注意到,在创建PDF文件时,ggplot也会绘制不必要的重复图形元素。重复的数量与正在绘制的 Dataframe 中的观察值数量相对应。
例如,在下面的示例中,mpg数据集有234个观测值。

library(ggplot2)
nrow(mpg)
> 234
p <- ggplot(mpg, aes(class, hwy))
p + geom_boxplot()+
  geom_segment(aes(x = 5, y = 40, xend = 6, yend = 40))+
  geom_text(label = "***", x = 5.5, y = 41, size = 6)
ggsave("mtcars.pdf")

在创建的PDF中,234个“***”副本相互叠加。这是geom_text的正常行为,如帮助文件所述:geom_text() and geom_label() add labels for each row in the data .

然而,也创建了geom_segment的234个副本,尽管帮助文件并不建议这是正常行为。

这在PDF中实际上是不可见的(除了***和段看起来好像是粗体/加粗的),但它会创建不必要的对象,这些对象会无缘无故地增加PDF大小,特别是在处理大量离群值时。
我只检查了geom_segment:对于其它元件也是如此。
有没有人知道如何避免这种行为?

ryevplcw

ryevplcw1#

所描述的问题影响所有输出格式,而不仅仅是PDF。当然,当输出为矢量图形格式(如PDF)时,它比位图更明显。在极端情况下,它会减慢绘制到任何图形设备的速度。问题的根源在于误解了最近版本的'ggplot 2'中实现的 * 图形分层语法 * 的工作原理我认为这是一种令人惊讶的美学Map行为。
我展示了这个问题背后的三个解决方案,并解释了它背后的内容。第一个解决方案,同意评论中提供的第一个建议,第二个是一个替代方案,虽然需要更多的冗长代码,但适用于其他情况。第三个是从@stefan的评论中复制的。

library(ggplot2)

ggplot(data = mpg, mapping = aes(class, hwy)) + 
  geom_boxplot() +
  annotate(geom = "segment", x = 5, y = 40, xend = 6, yend = 40)+
  annotate(geom = "text", label = "***", x = 5.5, y = 41, size = 6)

ggsave("mtcars.pdf")
#> Saving 7 x 5 in image

创建于2023年3月30日,使用reprex v2.0.2
使用annotate(),可以通过使用上述方法传递长度〉1的向量来添加多个注解。然而,annotate()不“知道”面,因此在具有面的图的情况下,在每个面板中具有不同注解的唯一方法是直接使用几何体,但是将与用于其他几何体的 Dataframe 不同的 Dataframe 作为数据传递给它们。这种方法如下所示,在这种情况下,使用单个绘图面板和单个注解是多余的,但更通用,可以与facet一起使用。

library(ggplot2)

ggplot(mpg, aes(class, hwy)) + 
  geom_boxplot()+
  geom_segment(data = data.frame(x = 5, y = 40, xend = 6, yend = 40),
               mapping = aes(x, y, xend = xend, yend = yend))+
  geom_text(data = data.frame(label = "***", x = 5.5, y = 41),
            mapping = aes(x, y, label = label), size = 6)

ggsave("mtcars3.pdf")
#> Saving 7 x 5 in image

创建于2023-03-30带有reprex v2.0.2
第三种可能性是在设置Map时将常量值作为参数传递给aes(),如@stefan和@Jon Spring在其评论中所述,在整个图级别没有默认数据。

library(ggplot2)

ggplot() + 
  geom_boxplot(data = mpg, aes(class, hwy)) + 
  geom_segment(aes(x = 5, y = 40, xend = 6, yend = 40)) + 
  geom_text(aes(label = "***", x = 5.5, y = 41), size = 6)

ggsave("mtcars2.pdf")
#> Saving 7 x 5 in image

创建于2023-03-30使用reprex v2.0.2
这三种方法是等效的,因为它们阻止geom_segment()geom_text()mpg视为其数据输入。在第一种情况下,因为annotate()通过设计确保了这一点,在第二种情况下,因为我们使用本地数据参数覆盖了整个图的默认数据,而在第三个示例中,通过不在对ggplot()的调用中传递数据参数来避免创建默认值。
第三种方法在'ggplot 2'的历史上只有几年才开始使用。因为以前使用aes()来Map常量值是不允许的或有不同的解释。在aes()中Map常量值仍然让用户感到困惑,因为将美学Map到常量并不覆盖数据的整个图默认值,即使这些数据不再Map到几何中的任何美学,从而导致本问题中描述的令人惊讶的行为。
这个令人惊讶的行为,至少对我来说,可以用一个额外的,人为的例子更清楚地证明,基于调用data.frame()与默认参数,以构建一个“空” Dataframe 作为参数传递,以覆盖整个图数据。并且由于没有缺少美学Map而正确地生成绘图。基于未Map到美学的数据中的行数生成图形对象的多个副本是“令人惊讶的”行为。这种行为似乎源于“ggplot 2”开发期间aes()行为的变化。

library(ggplot2)

ggplot(data = mpg, aes(class, hwy)) + 
  geom_boxplot() + 
  geom_segment(data = data.frame(), aes(x = 5, y = 40, xend = 6, yend = 40)) + 
  geom_text(data = data.frame(), aes(label = "***", x = 5.5, y = 41), size = 6)

ggsave("mtcars2.pdf")
#> Saving 7 x 5 in image

创建于2023-03-30使用reprex v2.0.2
注意:有三种常用的几何图形是不寻常的,因为它们的行为取决于是否存在Mapgeom_hline()geom_vline()geom_abline()

相关问题