2013-05-07 15 views
17

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

10

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

enter image description here

sizeit(p2, 2) 

enter image description here

+0

la risposta esatta alla domanda è lasciata come esercizio al lettore (questa grigliatura sta diventando troppo brutta, ho bisogno di una pausa) – baptiste

+0

Grazie per la tua risposta, battista. Sembra che non ci sia una soluzione carina. – marsl

+0

Ho accettato la tua risposta in quanto ritengo che non esista una soluzione migliore. Grazie a tutti. – marsl

0

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) 

enter image description here 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) 

enter image description here

+1

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

+0

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

2

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

+1

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

2

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] 
} 
0

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.