【问题标题】:How to rasterize a single layer of a ggplot?如何栅格化ggplot的单层?
【发布时间】:2017-11-10 12:25:43
【问题描述】:

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 Reader DC 需要约 1 秒来渲染图形。下面你可以看到一个.png 版本:

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

【问题讨论】:

  • 这可能是一个真正的问题:考虑一篇科学文章的两个版本:arxiv.org/abs/1501.01332v2(所有数字矢量)vs arxiv.org/abs/1501.01332v3(所有数字光栅化)。第一个可能会卡住您的打印机或 pdf 查看器,而第二个则不那么清晰,但文件大小要大得多。
  • 作为一种解决方法,使用dpi=600 甚至dpi=1200 将整个绘图保存为png 应该可以提供相当清晰的光栅图像,而不会生成巨大的文件。 png 是专为线条图形设计的。
  • @YAK 您有想要保存的示例图吗?我的直接想法是建议您看看 grConvert 和 grImport2。 stat.auckland.ac.nz/~paul/R/grImport2/grImport2.pdf。一个示例图会有所帮助,因为我们可以映射到您的示例。
  • @Technophobe01 现在添加了一个。修改生成的矢量图形的有趣建议(如果这是您的意思)。您将如何保留图层从属关系?

标签: r ggplot2


【解决方案1】:

感谢ggrastr package by Viktor Petukhov & Evan Biederstedt现在可以光栅化单个图层。但是,目前 (2018-08-13),仅支持 geom_point 和 geom_tile。work by Teun van den Brand 现在可以通过将任何单个 ggplot 图层包装在 ggrastr::rasterise() 中来栅格化它:

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

df %>% ggplot(aes(x=x, y=y)) +
      # this layer will be rasterized:
      ggrastr::rasterise(geom_point(size=0.1, alpha=0.1)) +
      # this one will still be "vector":
      geom_smooth()

以前,仅支持几个几何图形: 要使用它,您必须将 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 图层作为栅格,其他所有内容都作为矢量图形。总体而言,该图看起来与问题中的一样,但放大显示差异: 将此与全光栅图形进行比较:

【讨论】:

    【解决方案2】:

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

    我希望答案能够提供对ggplot2 的扩展,它允许导出带有栅格化图层的绘图,而对现有绘图命令的更改最少,即作为geom_... 命令的包装器或作为这些或@ 命令的附加参数987654328@ 命令,它需要一个绘图命令的未评估部分列表(每秒被光栅化),而不是链接问题中提供的 hacky 解决方法。

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

    球员

    让我们从我们将要处理的主要参与者开始。最高层是ggplot2 库。它需要数据帧并将它们转换为数字。不过,ggplot2 本身对低级绘图一无所知。它只处理线条、多边形、文本等,并以图形对象 (grobs) 的形式传递给 grid 库。

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

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

    理想的高级接口

    高级接口应包含ggplot2layer() 函数中的选项rasterize。通过这种方式,可以简单地编写,例如,geom_point(rasterize = TRUE) 来栅格化点层。这将对所有几何和统计数据透明地工作,因为它们都调用layer()

    可能的实现

    我看到了四种可能的实现途径,从最不可能到最不可能。

    1。理想情况下,layer() 函数将简单地将rasterize 选项传递给grid 库,后者会将其传递给图形设备,以告诉它要栅格化绘图的哪些部分。这种方法需要对图形设备 API 进行重大更改。我看不到这种情况发生。至少在我有生之年不会。

    2。或者,可以编写一种新的 grob 类型,该类型可以采用任意 grob 并在 grob 绘制在图形设备上时按需对其进行光栅化。这种方法不需要更改图形设备 API,但需要详细了解 grid 库的低级实现。它还可能使此类数字的交互式查看变得非常缓慢。

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

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

    技术障碍

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

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

    最后,我们必须让ggplot2 库接受光栅化代码,因为我们需要替换layer() 函数,我认为没有办法从单独的包中做到这一点。考虑到光栅化解决方案将有多么骇人听闻(参见前两段),这可能是一项艰巨的任务。

    【讨论】:

    • 很好的分析!希望这对 s.b. 来说是一种动力。实施解决方案比我的问题!你知道在哪里可以找到graphics device API 的文档吗?当你说“至少在我的一生中不会”。你真的是说:“不是在 R 核心团队的有生之年”;或者这种悲观说法的原因是什么?
    • 更改图形设备的API是一项艰巨的任务,所有设备驱动程序和所有客户端代码都需要更改。这不会轻易发生。我们已经遗漏了很多明显重要的东西,例如渐变填充、图案填充、图像填充等。光栅化是一个过于具体的问题,不能被视为高优先级。响应可能是:在更高级别进行光栅化,然后将光栅化图像交给图形设备。这并非完全不合理。
    • 我认为方法#2 可能是最通用和最可行的。我相信它主要涉及从rastergrob 复制代码并重新实现drawDetails function,因此它会即时光栅化一个grob,然后将光栅化图像发送到低级图形设备。 @baptiste 可能比我更了解这一点,并且可能会看到一些我不知道的陷阱。
    • (如果它还没有出现在你的推特上)你可能想查看ggrastr 包,它为一些类似于@baptiste 的geoms 提供了一种解决方法,但基于grid::grid.cap.
    • ggraster 现在适用于所有带有ggrastr::rasterize 的几何图形,似乎是implementing 方法4。
    猜你喜欢
    • 2011-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    • 1970-01-01
    • 2011-10-24
    • 1970-01-01
    • 2014-11-01
    相关资源
    最近更新 更多