2012-10-30 2 views
15

Il titolo lo copre piuttosto bene.Come posizionare due legende in modo indipendente in ggplot

Ho due legende, relative alla dimensione e al colore, e desidero avere una, diciamo, in alto e una all'interno del grafico.

questo è possibile e, se sì, come

TIA

+0

Non penso sia possibile (dove le legende sono posizionate è controllato dal tema, e quelle parti che definiscono la posizione della legenda prendono solo un singolo valore). Tuttavia, non sono così sicuro che non sia possibile dare una risposta. –

+0

Indovina se lo fosse, uno degli esperti sarebbe stato già operativo – pssguy

+0

@pssguy, può essere fatto con un po 'di giocherellare. Per avere il controllo sulle leggende, è necessario estrarre legende separate, quindi possono essere disposte in un grafico che inizialmente non contiene alcuna legenda. –

risposta

3

Dalla mia comprensione, in fondo non c'è un controllo molto limitato su leggende in ggplot2. Ecco un paragrafo del libro di Hadley (pagina 111):

ggplot2 tenta di utilizzare il minor numero possibile di legende che trasmetta accuratamente l'estetica utilizzata nella trama. Lo fa combinando le leggende se una variabile viene utilizzata con più di un'estetica. La Figura 6.14 mostra un esempio di questo per la geom dei punti: se sia il colore che la forma sono mappati sulla stessa variabile, è necessaria solo una singola legenda. Affinché le legende vengano unite, devono avere lo stesso nome (lo stesso titolo della legenda). Per questo motivo, se cambi il nome di una delle legende unite, dovrai cambiarlo per tutte.

+0

Grazie per l'estratto. Tutti gli esempi mostrano leggende nella stessa posizione – pssguy

28

È possibile estrarre legende separate dai grafici, quindi disporre le legende nella trama pertinente. Il codice qui utilizza le funzioni del pacchetto gtable per eseguire l'estrazione, quindi funziona dal pacchetto gridExtra per eseguire l'arrangiamento. L'obiettivo è quello di avere una trama che contenga una legenda dei colori e una legenda delle dimensioni. Innanzitutto, estrai la legenda del colore da un grafico che contiene solo la legenda del colore. In secondo luogo, estrai la legenda delle dimensioni da un grafico che contiene solo la legenda delle dimensioni. Terzo, disegna una trama che non contenga legenda. In quarto luogo, organizza la trama e le due leggende in una trama nuova.

# Some data 
df <- data.frame(
    x = 1:10, 
    y = 1:10, 
    colour = factor(sample(1:3, 10, replace = TRUE)), 
    size = factor(sample(1:3, 10, replace = TRUE))) 

library(ggplot2) 
library(gridExtra) 
library(gtable) 
library(grid) 

    ### Step 1 
# Draw a plot with the colour legend 
(p1 <- ggplot(data = df, aes(x=x, y=y)) + 
    geom_point(aes(colour = colour)) + 
    theme_bw() + 
    theme(legend.position = "top")) 

# Extract the colour legend - leg1 
leg1 <- gtable_filter(ggplot_gtable(ggplot_build(p1)), "guide-box") 

    ### Step 2 
# Draw a plot with the size legend 
(p2 <- ggplot(data = df, aes(x=x, y=y)) + 
    geom_point(aes(size = size)) + 
    theme_bw()) 

# Extract the size legend - leg2 
leg2 <- gtable_filter(ggplot_gtable(ggplot_build(p2)), "guide-box") 

    # Step 3 
# Draw a plot with no legends - plot 
(plot <- ggplot(data = df, aes(x=x, y=y)) + 
    geom_point(aes(size = size, colour = colour)) + 
    theme_bw() + 
    theme(legend.position = "none")) 

    ### Step 4 
# Arrange the three components (plot, leg1, leg2) 
# The two legends are positioned outside the plot: 
# one at the top and the other to the side. 
plotNew <- arrangeGrob(leg1, plot, 
     heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1) 

plotNew <- arrangeGrob(plotNew, leg2, 
      widths = unit.c(unit(1, "npc") - leg2$width, leg2$width), nrow = 1) 

grid.newpage() 
grid.draw(plotNew) 

# OR, arrange one legend at the top and the other inside the plot. 
plotNew <- plot + 
     annotation_custom(grob = leg2, xmin = 7, xmax = 10, ymin = 0, ymax = 4) 

plotNew <- arrangeGrob(leg1, plotNew, 
    heights = unit.c(leg1$height, unit(1, "npc") - leg1$height), ncol = 1) 

grid.newpage() 
grid.draw(plotNew) 

enter image description here

enter image description here

+0

Ciao Sandy. Questo è un esempio impressionante. Puoi rompere la logica sull'uso dell'argomento 'heights' su' arrangeGrob', per esempio in 'plotNew <- arrangeGrob (leg1, plot, heights = unit.c (leg1 $ height, unit (1," npc ") - leg1 $ height), ncol = 1) '? Ho capito che 'heights' è passato come argomento a' grid.layout', ma ho difficoltà a vedere come viene usato qui. Grazie. –

+0

Salve @Faheem, ci sono due trame da disporre verticalmente: 'leg1' e' plot'; e quindi due altezze nella funzione 'unit.c()'. 'leg1' ha un'altezza assoluta ed è dato da' leg1 $ height'. La seconda altezza, 'unit (1," npc ") - leg1 $ height', sottrae l'altezza della legenda dall'altezza del dispositivo per dare l'altezza disponibile per il' plot'. Nel primo esempio, una logica simile si applica alle larghezze. Ci sono due grobs da tracciare: la trama e la leggenda. Quindi due larghezze nella funzione 'unit.c()'; uno per 'plotNew' e il secondo per' leg2'. –

+0

Grazie, Sandy. Un'altra domanda: come sai che l'altezza del dispositivo è 'unità (1," npc ")'? Sarebbe utile se hai aggiunto alcune di queste informazioni alla tua domanda. Si potrebbe citare (a) l'argomento 'heights' e gli argomenti' widths' sono passati a 'grid.layout', (b) che' unit (1, "npc") 'è l'altezza del dispositivo, e quindi (c) per esempio 'leg1 $ height' e' unit (1, "npc") - leg1 $ height' sono le altezze rispettivamente della legenda e della trama principale. Posso aggiungere questo se vuoi. –

6

Ecco un'altra soluzione utilizzando ggplot2 e cowplot (= estensione ggplot2) pacchetti.

L'approccio è simile a quello di Sandys in quanto elimina la legenda come oggetti separati e consente di eseguire la posizione in modo indipendente. Era progettato principalmente per più leggende che appartengono a due o più grafici in una griglia di trame.

La funzione g_legend, che viene utilizzata, è stata presa da questo answer.

L'idea è la seguente:

  1. Creare Plot1, Plot2, ..., PlotX senza leggende
  2. Creare Plot1, Plot2, ..., PlotX con leggende
  3. Estratto leggende dal punto 2 in oggetti separati
  4. Impostare griglia di leggenda e organizzare le leggende che modo si vogliono
  5. Crea griglia unendo trame e leggende

Sembra un pò complicato e time/code comsuming ma impostato una volta, è possibile adattarlo e usarlo per ogni tipo di personalizzazione trama/legenda.

library(ggplot2) 
library(cowplot) 

# set up function 
g_legend<-function(a.gplot){ 
    tmp <- ggplot_gtable(ggplot_build(a.gplot)) 
    leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box") 
    legend <- tmp$grobs[[leg]] 
    return(legend) } 

# Some data 
df <- data.frame(
    Name = factor(rep(c("A", "B", "C"), 12)), 
    Month = factor(rep(1:12, each=3)), 
    Temp = sample(0:40, 12), 
    Precip = sample(50:400, 12)) 

# create plot1 
plot1 <- ggplot(df, aes(Month, Temp, fill = Name)) + 
    geom_point(show.legend = F, aes(group = Name, colour = Name), 
     size = 3, shape = 17) + 
    geom_smooth(method = "loess", se = F, 
     aes(group = Name, colour = Name), 
     show.legend = F, size = 0.5, linetype = "dashed") 

# create plot2 
plot2 <- ggplot(df, aes(Month, Precip, fill = Name)) + 
    geom_bar(stat = "identity", position = "dodge", show.legend = F) + 
    geom_smooth(method = "loess", se = F, 
     aes(group = Name, colour = Name), 
     show.legend = F, size = 1, linetype = "dashed") + 
    scale_fill_grey() 

# create legend1 
legend1 <- ggplot(df, aes(Month, Temp)) + 
    geom_point(show.legend = T, aes(group = Name, colour = Name), 
     size = 3, shape = 17) + 
    geom_smooth(method = "loess", se = F,aes(group = Name, colour = Name), 
     show.legend = T, size = 0.5, linetype = "dashed") + 
    labs(colour = "Station") + 
    theme(legend.text=element_text(size=8), 
      legend.title = element_text(face = "italic", 
       angle = -0, size = 10)) 

# create legend2 
legend2 <- ggplot(df, aes(Month, Precip, fill = Name)) + 
    geom_bar(stat = "identity", position = "dodge", show.legend = T) + 
    scale_fill_grey() + 
    guides(fill = 
     guide_legend(title = "", 
       title.theme = element_text(face = "italic", 
        angle = -0, size = 10))) + 
    theme(legend.text=element_text(size=8)) 


# extract "legends only" from ggplot object 
legend1 <- g_legend(legend1) 
legend2 <- g_legend(legend2) 

# setup legends grid 
legend1_grid <- cowplot::plot_grid(legend1, align = "v", nrow = 2) 

# add second legend to grid, specifying its location 
legends <- legend1_grid + 
    ggplot2::annotation_custom(grob = legend2, 
      xmin = 0.5, xmax = 0.5, ymin = 0.55, ymax = 0.55) 

# plot "plots" + "legends" (with legends in between plots) 
cowplot::plot_grid(plot1, legends, plot2, ncol = 3, 
    rel_widths = c(0.45, 0.1, 0.45)) 

Esempi:

Example http://i65.tinypic.com/jl1lef.png

Modifica dell'ordine del plot_grid() chiamata finale sposta le legende verso destra:

cowplot::plot_grid(plot1, plot2, legends, ncol = 3, 
        rel_widths = c(0.45, 0.45, 0.1)) 

Example2 http://i68.tinypic.com/314yn9i.png