加快从 R 中的 ggplot 渲染大型热图

Speed up rendering of large heatmap from ggplot in R

我正在尝试在 R 中绘制一个用 ggplot 生成的大型热图。最终,我想 'polish' 使用 Illustrator 绘制此热图。

示例代码:

# Load packages (tidyverse)
library(tidyverse)

# Create dataframe
df <- expand.grid(x = seq(1,100000), y = seq(1,100000))

# add variable: performance
set.seed(123)
df$z <- rnorm(nrow(df))

ggplot(data = df, aes(x = x, y = y)) +
  geom_raster(aes(fill = z)) 

虽然我将绘图保存为矢量化图像(.pdf;不是那么大),但打开时 pdf 加载速度非常慢。我希望在打开文件时呈现数据框中的每个单独点。

我读过其他使用 image() 可视化矩阵的帖子(例如 ),但是我想使用 ggplot 修改图像。

问题:如何加速这个情节的渲染?有没有办法(除了降低绘图的分辨率),同时保持图像矢量化,以加快这个过程?是否可以对矢量化的 ggplot 进行下采样?

您的可重现示例仅显示噪音,因此很难知道您想要哪种输出。

一种方法是遵循 并使用 geom_hex 显示聚合数据。

另一种方法,如您所问 "Is it possible to downsample a vectorized ggplot?",是在 geom_raster 的数据参数中使用 dplyr::sample_fracdplyr::sample_n。不过,我必须采用比您的示例更小的样本,否则我无法构建 df。

library(tidyverse)

# Create dataframe
df <- expand.grid(x = seq(1,1000), y = seq(1,1000))

# add variable: performance
set.seed(123)
df$z <- rnorm(nrow(df))

ggplot(data = df, aes(x = x, y = y)) +
  geom_raster(aes(fill = z), . %>% sample_frac(0.1)) 

如果你想从你的高分辨率 ggplot 对象开始,你可以做同样的效果:

gg <- ggplot(data = df, aes(x = x, y = y)) +
  geom_raster(aes(fill = z)) 

gg$data <-  sample_frac(gg$data,0.1)
gg

我尝试的第一件事是 stat_summary_2d 获得平均分箱,但它看起来很慢并且还在右边缘和上边缘产生了一些伪像:

library(tidyverse)
df <- expand.grid(x = seq(1,1000), y = seq(1,1000))
set.seed(123)
df$z <- rnorm(nrow(df))
print(object.size(df), units = "Mb")
#15.4 Mb

ggplot(data = df, aes(x = x, y = y, z = z)) +
  stat_summary_2d(bins = c(100,100)) +  #10x downsample, in this case
  scale_x_continuous(breaks = 100*0:10) +
  labs(title = "stat_summary_2d, 1000x1000 downsampled to 100x100")  

尽管这比您建议的数据小得多,但在我的机器上绘制仍然需要大约 3 秒,并且在顶部和右侧边缘有伪影,我推测是因为这些箱子从边缘开始变得更小了, 留下更多变化。

当我像您要求的那样尝试更大的网格时,它从那里变慢了。

(顺便说一句,与光栅图形不同,像 PDF 这样的矢量图形文件可以在不损失分辨率的情况下调整大小。但是,在这个用例中,输出是 10,000 兆像素的光栅文件,远远 超出了人类感知的限制,它被导出为矢量格式,其中每个 "pixel" 变成 PDF 中的一个非常小的矩形。矢量的使用format 在某些不寻常的情况下可能很有用,例如,如果您需要在不损失分辨率的情况下将热图放大到巨大的表面上,例如足球场。但听起来在这种情况下它可能不是这项工作的错误工具,因为您将大量数据放入矢量文件中,而这些文件是无法察觉的。)

更有效的方法是在 ggplot 之前对 dplyr 进行平均。有了它,我可以采用 10k x 10k 数组并在发送到 ggplot 之前将其下采样 100 倍。这必然会降低分辨率,但我不明白在这个用例中保留超出人类感知能力的分辨率的价值。

下面是一些我们自己进行分桶然后绘制下采样版本的代码:

# Using 10k x 10k array, 1527.1 Mb when initialized
downsample <- 100
df2 <- df %>%
  group_by(x = downsample * round(x / downsample),
           y = downsample * round(y / downsample)) %>%
  summarise(z = mean(z))

ggplot(df2, aes(x = x, y = y)) +
  geom_raster(aes(fill = z)) +
  scale_x_continuous(breaks = 1000*0:10) +
  labs(title = "10,000x10,000 downsampled to 100x100")