如何栅格化ggplot的单层?

时间:2017-11-10 12:25:44

标签: r ggplot2

Matplotlib允许栅格化绘图的各个元素并将其另存为混合像素/矢量图形(.pdf)(参见例如this answer)。如何在R ggplot2中实现同样的目标?

以下是玩具问题,我想仅栅格化geom_point图层。

set.seed(1)
x <- rlnorm(10000,4)
y <- 1+rpois(length(x),lambda=x/10+1/x)
z <- sample(letters[1:2],length(x), replace=TRUE)

p <- ggplot(data.frame(x,y,z),aes(x=x,y=y)) +
  facet_wrap("z") +
  geom_point(size=0.1,alpha=0.1) +
  scale_x_log10()+scale_y_log10() +
  geom_smooth(method="gam",formula = y ~ s(x, bs = "cs"))
print(p)
ggsave("out.pdf", p)

当保存为.pdf时,Adobe Reader DC需要~1秒来渲染图形。您可以在下面看到.png版本: out.png

当然,通常可以通过不绘制原始数据来避免这个问题

2 个答案:

答案 0 :(得分:4)

我认为你已经让自己没有回答这个问题。你写道:

  

我希望得到一个答案,为ggplot2提供一个扩展,允许导出带有栅格化图层的图​​,对现有的绘图命令进行最小的更改,即作为geom_...命令的包装或作为附加参数这些或ggsave命令需要一个绘图命令的未评估部分列表(每秒都要光栅化),而不是链接问题中提供的hacky变通方法。

这是一项重大的开发工作,很容易需要数周或更多的高技能开发人员的努力。由于Stack Overflow问题,任何人都不太可能这样做。我将在此处描述如何实现您所要求的内容以及为什么它具有相当的挑战性。

球员

让我们从我们将要处理的关键人物开始吧。最高级别是ggplot2库。它需要数据帧并将其转换为数字。 ggplot2本身对低级绘图一无所知。它只处理线条,多边形,文本等,它以图形对象(凹凸)的形式传递给grid库。

grid库本身就是一个相当高级的库。它对低级绘图也不太了解。它主要处理线条,多边形,文本等,并将其交给R图形设备。设备进行实际绘图。

有许多不同的R图形设备。在R命令行中输入?Devices以查看不完整的列表。有矢量图形设备,例如pdfpostscriptsvg,光栅设备,例如pngjpegtiff ,以及X11quartz等互动设备。显然,光栅化作为一种​​概念只对矢量图形设备有意义,因为光栅设备无论如何都会光栅化。重要的是,ggplot2grid都不知道或关心您当前使用的图形设备。它们处理可以在任何设备上绘制的图形对象。

理想的高级接口

高级界面应包含rasterize layer()函数中的ggplot2选项。以这种方式,人们可以简单地编写例如geom_point(rasterize = TRUE)来光栅化点层。这将对所有geoms和stats透明地工作,因为它们都调用layer()

可能的实施

我看到了四种可能的实施路径,从最不可能到最不可能的顺序排列。

1。理想情况下,layer()函数只需将rasterize选项移交给grid库,然后将其移交给图形设备,告诉它要对栅格化的哪些部分进行栅格化。这种方法需要对图形设备API进行重大更改。我没有看到这种情况发生。不是在我的一生中,至少。

2。或者,可以编写一个新的grob类型,可以在图形设备上绘制grob时按任意grob并按需栅格化它。这种方法不需要更改图形设备API,但需要详细了解grid库的低级实现。它也可能使这些数字的交互式观看非常缓慢。

3。稍微简单一点的替代方法是在grob构造上仅对一次任意grob进行栅格化,然后在绘制grob时重用。这在交互式图形设备上会快得多,但如果纵横比以交互方式更改,绘图会变形。然而,由于此功能的主要用途是生成pdf输出(我假设),此选项可能就足够了。

4。最后,光栅化也可能发生在layer()函数中,该函数可以简单地将常规栅格grob放入grob树中。该解决方案类似于技术described here.从技术上讲,它与3没有太大区别。无论哪种方式,都需要编写代码来栅格化grob树,然后用栅格grob替换它。

技术障碍

要栅格化grob树的某些部分,我们必须将它们发送到R栅格图形设备进行渲染。但是,没有一个能够呈现内存。因此,必须渲染到临时文件(例如,使用png()),然后重新读取文件。这可能但很难看。它还取决于每个R安装都不能保证可用的功能(例如png())。

其次,要将grob树的部分与整体渲染分开渲染,除了当前打开的图形设备之外,我们还必须打开一个新的图形设备。这是可能的,但可能导致意外的错误。我一直在处理这些错误,请参阅例如herehere与使用此技术的代码相关的问题。实现光栅化功能的人必须处理此类问题。

最后,我们必须将光栅化代码接受到ggplot2库中,因为我们需要替换layer()函数,而我并不认为这样做了。从单独的包中做到这一点的方法。考虑到光栅化解决方案将会变得多么暗淡(参见前两段),这可能是一个很高的要求。

答案 1 :(得分:1)

由于ggrastr package by Viktor Petukhov,现在可以栅格化各个图层。但是,当前(2018-08-13)仅支持geom_point和geom_tile。

要使用它,只需将geom_point替换为ggrastr::geom_point_rast

例如:

# install.packages('devtools')
# devtools::install_github('VPetukhov/ggrastr')
library(ggplot2)

set.seed(1)
x <- rlnorm(10000,4)
y <- 1+rpois(length(x),lambda=x/10+1/x)
z <- sample(letters[1:2],length(x), replace=TRUE)

ggplot(data.frame(x,y,z),aes(x=x,y=y)) +
  facet_wrap("z") +
  ggrastr::geom_point_rast(size=0.1,alpha=0.1) +
  scale_x_log10()+scale_y_log10() +
  geom_smooth(method="gam",formula = y ~ s(x, bs = "cs"))
ggsave("out.pdf")

这将产生一个pdf,其中仅包含geom_point图层作为栅格,而其他所有内容作为矢量图形。总体而言,该数字看起来像是问题中的那个,但放大可以看到不同之处: zoom-in view of example picture 将此与所有栅格图形进行比较: all-raster for comparison