加快从 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_frac
或 dplyr::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")
我正在尝试在 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 进行下采样?
您的可重现示例仅显示噪音,因此很难知道您想要哪种输出。
一种方法是遵循 geom_hex
显示聚合数据。
另一种方法,如您所问 "Is it possible to downsample a vectorized ggplot?",是在 geom_raster
的数据参数中使用 dplyr::sample_frac
或 dplyr::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")