Sto lavorando con la fantastica libreria ggplot2. Ho capito come impostare le proporzioni di un grafico usando coord_fixed
. Ora, vorrei salvare la trama in un PDF con una larghezza specificata (ad esempio 10 cm) e calcolare l'altezza richiesta. Non ho capito come ottenere questo. È possibile?Salva trama con un determinato formato
risposta
È possibile utilizzare le funzioni di griglia per calcolare l'intera dimensione del ggplot grob, ma lì sono (modifica: almeno) due accorgimenti:
aprirà una finestra ulteriore dispositivo, per effettuare la conversione dell'unità
la trama dimensione del pannello sarà 0 per difetto, in quanto è destinato da calcolare al volo secondo il dispositivo (viewport) in cui vive, non il contrario.
Detto questo, la seguente funzione tenta di aprire un dispositivo che si inserisce il ggplot esattamente,
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)
Se si utilizza ggsave
, è possibile specificare semplicemente la larghezza e l'altezza del dispositivo grafico. Se si specificano le proporzioni della trama stessa, è anche utile avere questo rapporto (approssimativamente) nel dispositivo grafico. L'unità di height
e width
durante il salvataggio pdf
è pollici:
ggplot(...) # make a plot here
ggsave("plot.pdf", width = 10)
Ora hai solo di trasformare i 10 cm in pollici. Inoltre, height
non è forzato a un determinato rapporto aspetto se non lo si specifica. Se si desidera un rapporto di 16: 9, si può facilmente calcolare l'altezza in base alla larghezza:
ggplot(...) # make plot
width = 10
height = (9/16) * width
ggsave("plot.pdf", width = width, height = height)
Si potrebbe avvolgere questo in una funzione, se si vuole veramente.
edit: Il punto cruciale è quello di sincronizzare il rapporto di aspetto della trama (attraverso coord_fixed()
) e il rapporto di aspetto del dispositivo grafico. Per esempio
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point() + coord_fixed()
ggsave("plt.png", width = 7, height = 7)
porta ad un sacco di spazio bianco. Mentre il seguente ggsave
chiamata, che ha una forma molto migliore nel rapporto di aspetto, non ha questa quantità di spazio bianco (scusate per il grande quadro, non ha potuto impostare la dimensione massima :)):
ggsave("plt.png", width = 2, height = 7)
Ho pensato a Paul, ma quella è la dimensione del * dispositivo *, non la trama. Una volta aggiunto il titolo, le etichette degli assi e la legenda, la regione di stampa stessa non può essere quadrata e con 'coord_fixed()' la regione di stampa stessa non riempie lo spazio disponibile. Non conosco una buona soluzione se non quella di ridimensionare la finestra di disegno fino a quando 'ggsave()' senza specificare la larghezza ti dà un'immagine di 10 cm di larghezza. –
Grazie Paul per la tua risposta. Gavin, è esattamente come hai sottolineato nel tuo commento. Penso che questo sia un bel problema, dovendo ritagliare manualmente ogni trama. Aspettiamo di vedere se qualcuno presenta una soluzione. – marsl
Non sicuro, ma è qualcosa di simile quello che stai cercando?
ggplot(data.frame(x = seq(10), y = seq(10)), aes(x = x, y = y)) +
geom_point() +
coord_equal() +
theme(aspect.ratio = 1)
questo sembra bene a me:
ggsave("test.pdf", width = 4, height = 4)
Troppo spazio bianco, ma l'immagine stessa ha aspect ratio 1:
ggsave("test2.pdf", width = 4)
Messaggio: Salvataggio 4 x 6.93 in immagine
Grazie per la tua risposta Dennis. Sfortunatamente non è appropriato dato che 'ggsave' prende solo le dimensioni della finestra di disegno corrente per impostazione predefinita. – marsl
Sulla base di risposta di Baptiste I spogliato il suo codice per restituire le proporzioni come suggerito da g eotheory. Questo era molto più conveniente per me, perché volevo sia una larghezza o altezza fissa e anche passato tutto attraverso una funzione wrapper esistente che aggiunge anche caratteri al mio pdf.
Oh, e se hai usato sfaccettature devi prenderle in considerazione manualmente. Dividi per righe e moltiplica per colonne. Non so se ci sia un modo migliore .....
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]
}
Una soluzione più semplice sarebbe quella di salvare la trama con margini predefiniti e per tagliare la risultante png con 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")
Di sicuro il taglio sarà diverso a seconda del dispositivo di uscita utilizzato. Per esempio. in caso di pdf è possibile utilizzare pdfcrop come descritto here.
la risposta esatta alla domanda è lasciata come esercizio al lettore (questa grigliatura sta diventando troppo brutta, ho bisogno di una pausa) – baptiste
Grazie per la tua risposta, battista. Sembra che non ci sia una soluzione carina. – marsl
Ho accettato la tua risposta in quanto ritengo che non esista una soluzione migliore. Grazie a tutti. – marsl