解决网页缩放排版问题_关于ggplot2缩放问题的解决

大家好,我是一名刚毕业的统计学专业的学生,喜欢可视化和数据处理,喜欢ggplot2这个包,但是一直备受ggplot2这个缩放的困扰,前几天刚好之间看到@thomasp85写的文章关于如何控制ggplot2缩放问题,这个问题我也遇到过,就是当更改ggplot2的长度、宽度、dpi的时候,图里面的文本和图形的大小会发生变化,没想到终于看到官网的方法了。

8ba6b3ebf013d4e637d723c1cdb44cf0.png

下面文字为我写的翻译,没有获得作者允许,翻译的不太行或者有问题,欢迎大家指出。

原文

https://www.tidyverse.org/blog/2020/08/taking-control-of-plot-scaling/​www.tidyverse.org

翻译:曾经在我写新版本的ggplot2书的时候,曾经在推特上问过用户,老用户对ggplot2的哪一个功能感到困惑,回答最多的是确保输出的文件具有正确的文本大小、行间距等。

在最新版本中将使这个问题成为过去式,让我们来看看怎么做:

一些基础知识:

在解决这个问题之前,我们了解关于图形和尺寸的一些相关理念。

Absolute size(绝对尺寸):这个是从物理维度的尺寸(更加准确地来说,是预期物理维度),单位通常是厘米、英尺等绝对长度单位。

Pixel size(像素尺寸):对于栅格格式的输出,图形被编码成带有颜色值的矩阵,矩阵中的每一个单元是一个像素,像素大小是矩阵的行数和列数,像素没有任何固定的物理尺寸大小。

Resolution(解析度、分辨率):这个值是将绝对尺寸和像素联系在一起,常见都是以ppi(pixels per inch)或者dpi(dot per inch),ppi或者dpi这两个单位是可以相互转换的. 分辨率为72ppi的意思为一英寸长相当于72的个像素长。

Pointsize(点大小):这是和文本尺寸相关的度量方式,当设置文本尺寸为12的时候,他指的是点。实际上点的大小的度量在历史上是一直在变化的,过去很不统一。现在普遍默认为1pt=1/72英寸(R也是采用的这个标准)。因为点的大小是绝对尺寸,因此输出的分辨率取决于对应的分辨率。

存在的问题:

回归主题,这个问题就是确保一个正确的尺寸而设置正确的缩放。

我们在画图的时候,会使用计算机不断地微调,同时获得预览,当达到合适的效果后,会按照要求尺寸将这个图形保存。接下来就是一个模拟,制作一个图的过程。

下面就是代码,会输出图

library(ggplot2)
library(ragg)
library(palmerpenguins)
# plot adapted from allisonhorst/palmerpenguins
p <- ggplot(na.omit(penguins), aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(
    aes(color = species, shape = species),
    size = 3,
    alpha = 0.8
  ) + 
  ggforce::geom_mark_ellipse(
    aes(filter = species == "Gentoo", 
        description = "Gentoo penguins are generally bigger in size")
  ) + 
  labs(x = "Flipper Length [mm]", y = "Body Mass [g]",  colour = "Species", 
       shape = "Species")

pngfile <- fs::path(knitr::fig_path(),  "basis.png")

# I'm explicitly calling the device functions so you can see the dimensions 
# used
agg_png(pngfile, width = 20, height = 12, units = "cm", res = 300)
plot(p)
invisible(dev.off())
knitr::include_graphics(pngfile)

运行上面的代码会在工作目录出现一个名叫-1的文件夹,然后里面包含下面的一个图:

bc9e6d8d15cdeb1a5df6b421fe9e1fa0.png

这个图看起来还不错,但是我们希望让他作为一个海报,假设我们眼睛距离计算机屏幕是50cm,通常人观察海报的距离是1.5m,因此如果希望海报在我们眼球里投影的大小和计算机里图在眼球的投影大小相同,我们的海报尺寸应该为电脑里的图片尺寸的3倍。

90b51ea2cfdf4af1a8f44c39ab00288e.png

基于上面的计算,调整尺寸后,绘制出的图如下:

pngfile <- fs::path(knitr::fig_path(),  "large_basis.png")
agg_png(pngfile, width = 60, height = 36, units = "cm", res = 300)
plot(p)
invisible(dev.off())
knitr::include_graphics(pngfile)

上面这个图看起来并不比上面那个大,因为微信将这个大小给限制了,可以下载这个图像,然后用编辑器打开,网页可以模拟出看海报的感觉,但是效果并不理想,所有东西都太小了。

------------------------------------------------------------------------------

插个话:这里我要解释一下,我是用R画出结果,然后将计算缩放到100%,发现看到的果然是一样大小的

b8760a42e5af73faa85341ec28c5136d.png

图1(也就是左边小的),图2 (也就是下面大的)。我将这两个照片都缩放到100%,会发现,输出的文本果然都是一样大的。阅读我的推文的也可以尝试运行,然后都将两个照片放缩到100%。发现文本、点都是一样大的。

827217bb47c7ea83655e79cfef7c9bd2.png

------------------------------------------------------------------------------

为什么会出现上面的问题(放大尺寸,所有东西都变小了)?因为输出的图里面的元素的大小包含绝对尺寸和相对尺寸,当我们放大输出的尺寸,绝对尺寸没有变(文本、点、横线、网格都没有变),这意味着当图的尺寸更大的时候,绝对尺寸对象的都没有变,导致他们看起来都变小了。同样的,当你缩小图的尺寸,会发现点、文本、网格、边界等都看起来变大了,占据了很多空间。

如何解决这个问题

尝试方法1:使用矢量图形

上面的讨论和网格文件有很大的关系,因为栅栏文件的尺寸不会自动的调整,因为得到合适的尺寸和大小至关重要,因此解决的方法是不输出栅栏文件,可以使用pdf()或者svglite()等矢量图形。这样的方法,你就可以是简单的讲图形渲染出所需要的大小,然后将输出调整为所需要的大小。输出矢量文件也有缺点,比如不同设备打开出现的结果不同,自定义的字体在不同操作系统出现的也不一样;或者图包含大量的元素,导致矢量文件过大。如果你不会遇到矢量文件的上述问题,那么使用矢量文件保存你的图形是最好的方式,如果你有上述的问题,而且就是希望将图形保存为png,那你就接着阅读,看看如何解决缩放问题。

尝试方法2:主题

解决这个问题一种方法是使用ggplot2的主题函数,可以在更大尺寸有更好的效果。

p1 <- p + 
  theme_gray(base_size = 33)
pngfile <- fs::path(knitr::fig_path(),  "theming.png")
agg_png(pngfile, width = 60, height = 36, units = "cm", res = 300)
plot(p1)
invisible(dev.off())
knitr::include_graphics(pngfile)

a0632158a6a7eaaf09b85ec50f50111a.png

上述方法有个硬伤,

第一点:就是需要调整的参数太多了,如果你自己再加更多的元素、加入更多的主题,会导致你的参数混乱,调整过于麻烦,而且仔细上图图例中的关键词,大小并没有发生变化。

第二点:所有theme不能调整的元素大小都不对(也就是图中layer部分)。因此重做上面的图才能获得正确的效果。

p1 <- ggplot(na.omit(penguins), aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(
    aes(color = species, shape = species),
    size = 9,
    alpha = 0.8
  ) + 
  ggforce::geom_mark_ellipse(
    aes(filter = species == "Gentoo", 
        description = "Gentoo penguins are generally bigger in size"),
    size = 1.5,
    label.fontsize = 36
  ) + 
  labs(x = "Flipper Length [mm]", y = "Body Mass [g]",  colour = "Species", 
       shape = "Species") + 
  theme_gray(base_size = 33)
pngfile <- fs::path(knitr::fig_path(),  "theming2.png")
agg_png(pngfile, width = 60, height = 36, units = "cm", res = 300)
plot(p1)
invisible(dev.off())
knitr::include_graphics(pngfile)

16167ccd6e332dc8b33b4da100932c67.png

可以看到,要达到相同的效果,真的是太麻烦了(我上面代码都不想看)。特别是后面部分,要求具备在几何中关于绝对尺寸编码的所有不同设置的知识,对于标记几何,我们只是固定了椭圆线宽和文本大小,但是还需要更多的参数去调整,而且文本框也出现怪异的形状,后面还需要调整更多的代码才能达到相同的效果。还有一个问题,就是当尺寸改变,我们需要调整的代码更多。简直是太麻烦了。

尝试方法3:分辨率缩放

因为分辨率是像素尺寸和物理尺寸(绝对尺寸)的一个比值参数,因此分辨率可以作为缩放因子,但是需要一些不是显而易见的调整。

我们需要做的第一件事是转换物理尺寸为像素尺寸,希望我们的海报参数是300ppi,物理大小是60x36cm。

c(60, 36) *
  0.3937 * # convert to inch (厘米变成英寸)
  300 # convert to pixels
#> [1] 7086.60 4251.96

使用上面计算的值,修改设备的值和分辨率,让设备输出更大的尺寸。

pngfile <- fs::path(knitr::fig_path(),  "resolution.png")
agg_png(pngfile, width = 7087, height = 4252, units = "px", res = 900)
plot(p)
invisible(dev.off())
knitr::include_graphics(pngfile)

5b439c9c51695a6bc4484b547ebe013e.png

上面这个代码输出的结果完全符合我们的要求,所有的图形元素的比例都很正确,这可能是一个完美的解决方案,但是上面要手算大小真的让我不舒服。另外还存在一个缺点,那就是画图的尺寸编码不对,后面还需要进一步调整,才能在不同的设备中有相同的效果。

终极解决方法:

看到没有一个终极的工具来解决这个问题,因此我在ragg包里加入这个参数,参数名称为sacling。这个参数将用在所有绝对尺寸上,而且不会干扰输出的编码尺寸。由于我们将尺寸增加了3倍,只要设置scaling=3即可保存其相对尺寸。

pngfile <- fs::path(knitr::fig_path(),  "scaling.png")
agg_png(pngfile, width = 60, height = 36, units = "cm", res = 300, scaling = 3)
plot(p)
invisible(dev.off())
knitr::include_graphics(pngfile)

478bdb9851b019ea81a40a2655ab5898.png

可以看出这个参数非常容易调整缩放,希望这将消除论文、海报、演示文档的一些问题。你需要确保所有图形使用相同的缩放比值,使他们有相同的大小,除此之外,可以轻松的微调你创建的媒体的外观。

上面的缩放因子3是我们经过深思熟虑而计算出来的,不是瞎调整的。另外创建一个这个图的小版本。直接使用ggsave保存图像:

pngfile <- fs::path(knitr::fig_path(),  "small.png")
ggsave(
  pngfile, 
  p, 
  device = agg_png, 
  width = 10, height = 6, units = "cm", res = 300
)
knitr::include_graphics(pngfile)

9bc7a15469086d36dee410dcbcea4286.png

啊啊啊!!有些东西竟然没有了。

如果我们加个scaling=0.5试一试:

pngfile <- fs::path(knitr::fig_path(),  "downscaling.png")
ggsave(
  pngfile, 
  p, 
  device = agg_png, 
  width = 10, height = 6, units = "cm", res = 300,
  scaling = 0.5
)
knitr::include_graphics(pngfile)

966f723d0f28b83162b53c59b8c792f9.png

完美的呈现!!!!

附录:

1. 为网络准备图形会带来额外的障碍,HTML规定网页的分辨率是96ppi,因为当时的分辨率是这样的,现在这个规定依然没有变,虽然现在的屏幕分辨率变得更高了(操作系统可能会调整)。这就是为什么shiny、blogdown、hugodown呈现的图会看起来略小的原因。只需要将分辨率设置为96ppi,并对输出使用像素尺寸,就可以呈现出正确的缩放比例。

2. 使用Rmarkdown渲染图像也需要格外小心,因为每一块输出的图像尺寸以英寸为单位,还包含一缩放比例,以决定渲染的图形在文档中显示的比例,R for Data Science有更多的信息

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值