# ratio - save ggplot from r

## Save plot with a given aspect ratio (4)

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?

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

``````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.

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.

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.....

``````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
}
``````

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:

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

Message: Saving 4 x 6.93 in image

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

• 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.

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

``````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)
`````` 