使用给定的宽高比保存绘图

时间:2022-08-27 10:46:44

I'm working with the really awesome library ggplot2. I figured out how to set the aspect ratio of a plot by using coord_fixed. Now, I'd like to save the plot to a PDF with a specified width (e.g 10 cm) and let required height get calculated. I did not figure out how to achieve this. Is this even possible?

我正在使用非常棒的库ggplot2。我想通过使用coord_fixed来设置如何设置绘图的宽高比。现在,我想将绘图保存为具有指定宽度(例如10 cm)的PDF,并计算所需的高度。我没弄清楚如何实现这一目标。这有可能吗?

5 个解决方案

#1


11  

You can use grid functions to calculate the full size of the ggplot grob, but there are (edit: at least) two caveats:

您可以使用网格函数来计算ggplot grob的完整大小,但有(编辑:至少)两个警告:

  • an extra device window will open, to do the unit conversion

    将打开一个额外的设备窗口,进行单位转换

  • the plot panel size will be 0 by default, as it is meant to be calculated on-the-fly according to the device (viewport) it lives in, not the opposite.

    默认情况下,绘图面板大小为0,因为它意味着根据它所在的设备(视口)即时计算,而不是相反。

That being said, the following function attempts to open a device that fits the ggplot exactly,

话虽这么说,以下函数试图打开一个完全符合ggplot的设备,

library(ggplot2)
library(grid)

sizeit <- function(p, panel.size = 2, default.ar=1){

  gb <- ggplot_build(p)
  # first check if theme sets an aspect ratio
  ar <- gb$plot$coordinates$ratio

  # second possibility: aspect ratio is set by the coordinates, which results in 
  # the use of 'null' units for the gtable layout. let's find out
  g <- ggplot_gtable(gb)
  nullw <- sapply(g$widths, attr, "unit")
  nullh <- sapply(g$heights, attr, "unit")

  # ugly hack to extract the aspect ratio from these weird units
  if(any(nullw == "null"))
    ar <- unlist(g$widths[nullw == "null"]) / unlist(g$heights[nullh == "null"])

  if(is.null(ar)) # if the aspect ratio wasn't specified by the plot
       ar <- default.ar

  # ensure that panel.size is always the larger dimension
  if(ar <= 1 ) panel.size <- panel.size / ar

  g$fullwidth <- convertWidth(sum(g$widths), "in", valueOnly=TRUE) + 
    panel.size
  g$fullheight <- convertHeight(sum(g$heights), "in", valueOnly=TRUE) + 
    panel.size / ar

  class(g) <- c("sizedgrob", class(g))
  g
}


print.sizedgrob <- function(x){
  # note: dev.new doesn't seem to respect those parameters
  # when called from Rstudio; in this case it 
  # may be replaced by x11 or quartz or ...
  dev.new(width=x$fullwidth, height=x$fullheight)
  grid.draw(x)
}


p1 <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed() +
  theme(plot.background = element_rect(colour = "red"))

p2 <- p1 + aes(x = mpg, y = wt)

# need for an explicit dummy device open, otherwise it's a bit off
# for no apparent reason that I can understand
dev.new() 

sizeit(p1, 0.1)

使用给定的宽高比保存绘图

sizeit(p2, 2)

使用给定的宽高比保存绘图

#2


2  

Not sure, but is something like this what you're after?

不确定,但是这就是你想要的东西吗?

ggplot(data.frame(x = seq(10), y = seq(10)), aes(x = x, y = y)) +
    geom_point() +
    coord_equal() +
    theme(aspect.ratio = 1)

This looks fine to me:

这看起来很好:

ggsave("test.pdf", width = 4, height = 4)

Too much white space, but the graphic itself has aspect ratio 1:

白色空间太多,但图形本身的宽高比为1:

ggsave("test2.pdf", width = 4)

Message: Saving 4 x 6.93 in image

消息:在图像中保存4 x 6.93

#3


2  

Based on baptiste's answer I stripped his code down to return the aspect ratio as suggested by geotheory. This was much more convenient for me, because I either wanted a fixed width or height and also passed everything through an existing wrapper function that also adds fonts to my pdf.

根据巴普蒂斯特的回答,我删除了他的代码以返回地理位置建议的宽高比。这对我来说更方便,因为我想要一个固定的宽度或高度,并通过现有的包装函数传递所有内容,该函数也为我的pdf添加了字体。

Oh, and if you used facets you need to take them into account manually. Divide by rows and multiply by columns. Not sure whether there is a better way.....

哦,如果你使用了facet,你需要手动考虑它们。按行划分并乘以列。不确定是否有更好的方法.....

ggGetAr <- function(p, default.ar=-1){

    gb <- ggplot_build(p)
    # first check if theme sets an aspect ratio
    ar <- gb$plot$coordinates$ratio

    # second possibility: aspect ratio is set by the coordinates, which results in 
    # the use of 'null' units for the gtable layout. let's find out
    g <- ggplot_gtable(gb)
    nullw <- sapply(g$widths, attr, "unit")
    nullh <- sapply(g$heights, attr, "unit")

    # ugly hack to extract the aspect ratio from these weird units
    if(any(nullw == "null"))
        ar <- unlist(g$widths[nullw == "null"]) / unlist(g$heights[nullh == "null"])

    if(is.null(ar)) # if the aspect ratio wasn't specified by the plot
        ar <- default.ar

    ar[1]
}

#4


0  

If you use ggsave you can simply specify the width and height of the graphics device. If you specify the aspect ratio of the plot itself, it is also good to have this aspect ratio (roughly) in your graphics device. The unit of height and width when saving pdf is inches:

如果使用ggsave,则只需指定图形设备的宽度和高度即可。如果指定绘图本身的纵横比,那么在图形设备中(粗略地)具有此纵横比也是很好的。保存pdf时的高度和宽度单位为英寸:

ggplot(...) # make a plot here
ggsave("plot.pdf", width = 10)

Now you only have to transform the 10 cm into inches. In addition, height is not forced to a certain aspect ratio if you do not specify it. If you want a 16:9 aspect ratio, you can easily calculate the height based on the width:

现在你只需要将10厘米变换成英寸。此外,如果不指定高度,则不会强制高度达到某个宽高比。如果您想要16:9的宽高比,您可以根据宽度轻松计算高度:

ggplot(...) # make plot
width = 10
height = (9/16) * width
ggsave("plot.pdf", width = width, height = height)

You could wrap this in a function if you really want to.

如果你真的想要,可以将它包装在一个函数中。


edit: The crux is to synchronize the aspect ratio of the plot (through coord_fixed()) and the aspect ratio of the graphics device. For example

编辑:关键是同步绘图的纵横比(通过coord_fixed())和图形设备的纵横比。例如

library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed()
ggsave("plt.png", width = 7, height = 7)

使用给定的宽高比保存绘图 leads to a lot of white space. While the following ggsave call, which has a much better fit in aspect ratio, does not have this amount of white space (sorry for the large picture, could not set the maximum size :)):

导致很多白色空间。虽然以下ggsave调用,它具有更好的宽高比,但没有这么大的空白区域(对于大图片而言,无法设置最大尺寸:)):

ggsave("plt.png", width = 2, height = 7)

使用给定的宽高比保存绘图

#5


0  

A more simplistic solution would be to save the plot with default margins and to trim the resulting png with ImageMagick.

更简单的解决方案是使用默认边距保存绘图并使用ImageMagick修剪生成的png。

require(ggplot2)
require(dplyr)

ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point() + coord_fixed(0.3)
ggsave("untrimmed.png")


system("convert untrimmed.png -trim -bordercolor white -border 20  reframed.png")

For sure the trimming will differ depending on the used output device. E.g. in case of pdf you could use pdfcrop as described here.

确保修剪因使用的输出设备而异。例如。如果是pdf,您可以使用pdfcrop,如此处所述。

#1


11  

You can use grid functions to calculate the full size of the ggplot grob, but there are (edit: at least) two caveats:

您可以使用网格函数来计算ggplot grob的完整大小,但有(编辑:至少)两个警告:

  • an extra device window will open, to do the unit conversion

    将打开一个额外的设备窗口,进行单位转换

  • the plot panel size will be 0 by default, as it is meant to be calculated on-the-fly according to the device (viewport) it lives in, not the opposite.

    默认情况下,绘图面板大小为0,因为它意味着根据它所在的设备(视口)即时计算,而不是相反。

That being said, the following function attempts to open a device that fits the ggplot exactly,

话虽这么说,以下函数试图打开一个完全符合ggplot的设备,

library(ggplot2)
library(grid)

sizeit <- function(p, panel.size = 2, default.ar=1){

  gb <- ggplot_build(p)
  # first check if theme sets an aspect ratio
  ar <- gb$plot$coordinates$ratio

  # second possibility: aspect ratio is set by the coordinates, which results in 
  # the use of 'null' units for the gtable layout. let's find out
  g <- ggplot_gtable(gb)
  nullw <- sapply(g$widths, attr, "unit")
  nullh <- sapply(g$heights, attr, "unit")

  # ugly hack to extract the aspect ratio from these weird units
  if(any(nullw == "null"))
    ar <- unlist(g$widths[nullw == "null"]) / unlist(g$heights[nullh == "null"])

  if(is.null(ar)) # if the aspect ratio wasn't specified by the plot
       ar <- default.ar

  # ensure that panel.size is always the larger dimension
  if(ar <= 1 ) panel.size <- panel.size / ar

  g$fullwidth <- convertWidth(sum(g$widths), "in", valueOnly=TRUE) + 
    panel.size
  g$fullheight <- convertHeight(sum(g$heights), "in", valueOnly=TRUE) + 
    panel.size / ar

  class(g) <- c("sizedgrob", class(g))
  g
}


print.sizedgrob <- function(x){
  # note: dev.new doesn't seem to respect those parameters
  # when called from Rstudio; in this case it 
  # may be replaced by x11 or quartz or ...
  dev.new(width=x$fullwidth, height=x$fullheight)
  grid.draw(x)
}


p1 <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed() +
  theme(plot.background = element_rect(colour = "red"))

p2 <- p1 + aes(x = mpg, y = wt)

# need for an explicit dummy device open, otherwise it's a bit off
# for no apparent reason that I can understand
dev.new() 

sizeit(p1, 0.1)

使用给定的宽高比保存绘图

sizeit(p2, 2)

使用给定的宽高比保存绘图

#2


2  

Not sure, but is something like this what you're after?

不确定,但是这就是你想要的东西吗?

ggplot(data.frame(x = seq(10), y = seq(10)), aes(x = x, y = y)) +
    geom_point() +
    coord_equal() +
    theme(aspect.ratio = 1)

This looks fine to me:

这看起来很好:

ggsave("test.pdf", width = 4, height = 4)

Too much white space, but the graphic itself has aspect ratio 1:

白色空间太多,但图形本身的宽高比为1:

ggsave("test2.pdf", width = 4)

Message: Saving 4 x 6.93 in image

消息:在图像中保存4 x 6.93

#3


2  

Based on baptiste's answer I stripped his code down to return the aspect ratio as suggested by geotheory. This was much more convenient for me, because I either wanted a fixed width or height and also passed everything through an existing wrapper function that also adds fonts to my pdf.

根据巴普蒂斯特的回答,我删除了他的代码以返回地理位置建议的宽高比。这对我来说更方便,因为我想要一个固定的宽度或高度,并通过现有的包装函数传递所有内容,该函数也为我的pdf添加了字体。

Oh, and if you used facets you need to take them into account manually. Divide by rows and multiply by columns. Not sure whether there is a better way.....

哦,如果你使用了facet,你需要手动考虑它们。按行划分并乘以列。不确定是否有更好的方法.....

ggGetAr <- function(p, default.ar=-1){

    gb <- ggplot_build(p)
    # first check if theme sets an aspect ratio
    ar <- gb$plot$coordinates$ratio

    # second possibility: aspect ratio is set by the coordinates, which results in 
    # the use of 'null' units for the gtable layout. let's find out
    g <- ggplot_gtable(gb)
    nullw <- sapply(g$widths, attr, "unit")
    nullh <- sapply(g$heights, attr, "unit")

    # ugly hack to extract the aspect ratio from these weird units
    if(any(nullw == "null"))
        ar <- unlist(g$widths[nullw == "null"]) / unlist(g$heights[nullh == "null"])

    if(is.null(ar)) # if the aspect ratio wasn't specified by the plot
        ar <- default.ar

    ar[1]
}

#4


0  

If you use ggsave you can simply specify the width and height of the graphics device. If you specify the aspect ratio of the plot itself, it is also good to have this aspect ratio (roughly) in your graphics device. The unit of height and width when saving pdf is inches:

如果使用ggsave,则只需指定图形设备的宽度和高度即可。如果指定绘图本身的纵横比,那么在图形设备中(粗略地)具有此纵横比也是很好的。保存pdf时的高度和宽度单位为英寸:

ggplot(...) # make a plot here
ggsave("plot.pdf", width = 10)

Now you only have to transform the 10 cm into inches. In addition, height is not forced to a certain aspect ratio if you do not specify it. If you want a 16:9 aspect ratio, you can easily calculate the height based on the width:

现在你只需要将10厘米变换成英寸。此外,如果不指定高度,则不会强制高度达到某个宽高比。如果您想要16:9的宽高比,您可以根据宽度轻松计算高度:

ggplot(...) # make plot
width = 10
height = (9/16) * width
ggsave("plot.pdf", width = width, height = height)

You could wrap this in a function if you really want to.

如果你真的想要,可以将它包装在一个函数中。


edit: The crux is to synchronize the aspect ratio of the plot (through coord_fixed()) and the aspect ratio of the graphics device. For example

编辑:关键是同步绘图的纵横比(通过coord_fixed())和图形设备的纵横比。例如

library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed()
ggsave("plt.png", width = 7, height = 7)

使用给定的宽高比保存绘图 leads to a lot of white space. While the following ggsave call, which has a much better fit in aspect ratio, does not have this amount of white space (sorry for the large picture, could not set the maximum size :)):

导致很多白色空间。虽然以下ggsave调用,它具有更好的宽高比,但没有这么大的空白区域(对于大图片而言,无法设置最大尺寸:)):

ggsave("plt.png", width = 2, height = 7)

使用给定的宽高比保存绘图

#5


0  

A more simplistic solution would be to save the plot with default margins and to trim the resulting png with ImageMagick.

更简单的解决方案是使用默认边距保存绘图并使用ImageMagick修剪生成的png。

require(ggplot2)
require(dplyr)

ggplot(iris, aes(Sepal.Length, Sepal.Width)) + geom_point() + coord_fixed(0.3)
ggsave("untrimmed.png")


system("convert untrimmed.png -trim -bordercolor white -border 20  reframed.png")

For sure the trimming will differ depending on the used output device. E.g. in case of pdf you could use pdfcrop as described here.

确保修剪因使用的输出设备而异。例如。如果是pdf,您可以使用pdfcrop,如此处所述。