【发布时间】:2013-05-01 15:08:48
【问题描述】:
我正在使用非常棒的库 ggplot2。我想出了如何使用coord_fixed 设置绘图的纵横比。现在,我想将绘图保存为具有指定宽度(例如 10 厘米)的 PDF,并计算所需的高度。我不知道如何实现这一目标。这甚至可能吗?
【问题讨论】:
我正在使用非常棒的库 ggplot2。我想出了如何使用coord_fixed 设置绘图的纵横比。现在,我想将绘图保存为具有指定宽度(例如 10 厘米)的 PDF,并计算所需的高度。我不知道如何实现这一目标。这甚至可能吗?
【问题讨论】:
您可以使用网格函数来计算 ggplot grob 的完整大小,但有(编辑: 至少)两个警告:
将打开一个额外的设备窗口,进行单位转换
默认情况下,绘图面板大小为 0,因为它是根据它所在的设备(视口)动态计算的,而不是相反。
话虽如此,下面的函数会尝试打开一个完全适合 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)
【讨论】:
sizeit(p1, 0.1) 绘图时,输出奇怪地转置了轴。我认为最好使用返回用于确定 ggsave 宽度和高度参数的纵横比的函数。
更简单的解决方案是使用默认边距保存绘图并使用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")
【讨论】:
根据 baptiste 的回答,我剥离了他的代码以返回地球理论所建议的纵横比。这对我来说更方便,因为我要么想要一个固定的宽度或高度,而且还通过一个现有的包装函数传递所有内容,该函数还向我的 pdf 添加字体。
哦,如果您使用了构面,则需要手动考虑它们。除以行并乘以列。不知道有没有更好的办法.....
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]
}
【讨论】:
不确定,但你追求的是这样的东西吗?
ggplot(data.frame(x = seq(10), y = seq(10)), aes(x = x, y = y)) +
geom_point() +
coord_equal() +
theme(aspect.ratio = 1)
这对我来说很好:
ggsave("test.pdf", width = 4, height = 4)
空白太多,但图形本身的纵横比为 1:
ggsave("test2.pdf", width = 4)
消息: 在图像中节省 4 x 6.93
【讨论】:
ggsave 默认只占用当前绘图窗口的大小。
如果您使用ggsave,您可以简单地指定图形设备的宽度和高度。如果您指定绘图本身的纵横比,那么在图形设备中(大致)具有此纵横比也很好。保存pdf时height和width的单位是英寸:
ggplot(...) # make a plot here
ggsave("plot.pdf", width = 10)
现在您只需将 10 厘米转换为英寸。另外,height 如果不指定,则不会强制为某个纵横比。如果你想要 16:9 的宽高比,你可以很容易地根据宽度计算高度:
ggplot(...) # make plot
width = 10
height = (9/16) * width
ggsave("plot.pdf", width = width, height = height)
如果你真的想的话,你可以把它包装在一个函数中。
edit:关键是要同步绘图的纵横比(通过coord_fixed())和图形设备的纵横比。例如
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed()
ggsave("plt.png", width = 7, height = 7)
导致大量空白。虽然下面的ggsave 调用更适合纵横比,但没有这么多的空白(抱歉,大图,无法设置最大尺寸:)):
ggsave("plt.png", width = 2, height = 7)
【讨论】:
coord_fixed() 绘图区域本身就不会填满可用空间。除了在ggsave() 不指定宽度的情况下调整绘图窗口的大小之外,我不知道有什么好的解决方法可以为您提供 10 厘米宽的图像。