如何栅格化ggplot的单层?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何栅格化ggplot的单层?相关的知识,希望对你有一定的参考价值。

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


以下是玩具问题,我想仅栅格化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读取器DC需要~1s来渲染图形。下面你可以看到.png版本:out.png

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

答案

我想你已经让自己没有回答这个问题了。你写:

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

这是一项重大的开发工作,高技能的开发人员可能需要数周或更长的时间才能完成。由于Stack Overflow问题,任何人都不太可能这样做。代替一个有效的实施,我将在这里描述一个人如何实现你所要求的以及为什么它具有相当的挑战性。

The players

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

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

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

Ideal high-level interface

高级接口应该包含rasterizelayer()函数中的ggplot2选项。以这种方式,人们可以简单地编写例如geom_point(rasterize = TRUE)来栅格化点层。这对于所有的geoms和stats都是透明的,因为它们都叫layer()

Possible implementations

我看到了四种可能的实施路线,从最不可能到最不可能。

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替换它。

Technical hurdles

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

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

最后,我们必须将光栅化代码接受到ggplot2库中,因为我们需要替换layer()函数,我认为没有办法从单独的包中做到这一点。鉴于栅格化解决方案将会变得多么强大(参见前两段),这可能是一个很高的要求。

另一答案

由于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

以上是关于如何栅格化ggplot的单层?的主要内容,如果未能解决你的问题,请参考以下文章

ggplot如何在绘制栅格时设置不存在的整数的填充值[复制]

数据可视化应用实现空间栅格(附R语言代码)

OpenGL线段光栅化规范

如何使用栅格化系统构建响应式设计

如何将 geom_sf 生成的地图放在 ggmap 生成的栅格之上

如何在 python 中并行化以下代码片段?