Подтвердить что ты не робот

Сохранить участок с заданным соотношением сторон

Я работаю с действительно потрясающей библиотекой ggplot2. Я выяснил, как установить соотношение сторон графика с помощью coord_fixed. Теперь я хотел бы сохранить график в PDF с указанной шириной (например, 10 см) и позволить рассчитывать требуемую высоту. Я не понял, как это сделать. Возможно ли это?

4b9b3361

Ответ 1

Вы можете использовать функции сетки для вычисления полного размера 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 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 a bit off
# for no apparent reason that I can understand
dev.new() 

sizeit(p1, 0.1)

enter image description here

sizeit(p2, 2)

enter image description here

Ответ 2

Не уверен, но что-то вроде этого, что вы после?

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 на изображении

Ответ 3

Основываясь на баптистском ответе, я удалил его код, чтобы вернуть соотношение сторон, как это предлагает геотеатр. Это было гораздо удобнее для меня, потому что я либо хотел фиксированную ширину или высоту, а также передал все через существующую функцию обертки, которая также добавляет шрифты в мой 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 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

Если вы используете ggsave, вы можете просто указать ширину и высоту графического устройства. Если вы укажете соотношение сторон самого сюжета, также хорошо иметь это соотношение сторон (примерно) в графическом устройстве. Единица height и width при сохранении pdf равна дюймам:

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)

enter image description here приводит к большому количеству пробелов. В то время как следующий вызов ggsave, который имеет гораздо лучшую форму в пропорции, не имеет этого количества пробелов (извините за большое изображение, не удалось установить максимальный размер:)):

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

enter image description here

Ответ 5

Более упрощенным решением было бы сохранить график с полями по умолчанию и обрезать полученный png с помощью 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")

Конечно, обрезка будет отличаться в зависимости от используемого устройства вывода. Например. в случае pdf вы можете использовать pdfcrop, как описано здесь.