2016-01-15 7 views
9

ich eine kombinierte Handlung von zwei Parzellen + ihre Legende wie diese wollen:ggplot2: mehrere Plots in einer einzigen Zeile mit einer einzigen Legende

library(ggplot2) 
library(grid) 
library(gridExtra) 
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]  
p1 <- qplot(price, carat, data=dsamp, colour=clarity) 
p2 <- qplot(price, depth, data=dsamp, colour=clarity) 
g <- ggplotGrob(p1 + theme(legend.position="bottom"))$grobs 
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]] 
grid.arrange(arrangeGrob(p1+theme(legend.position="right"),p2+theme(legend.position="none"),legend,ncol=3,widths=c(3/7,3/7,1/7))) 

expected output

Allerdings möchte ich nicht über die Breite erraten der Plots und Legenden (und spezifizieren ncol), aber haben es aus p1 und p2as shown here extrahiert.

Also erwarte ich, ich würde (von der Verbindung angepasst Code) so etwas wie dieser benötigen:

grid_arrange_shared_legend_row <- function(...) { 
    plots <- list(...) 
    g <- ggplotGrob(plots[[1]] + theme(legend.position="right"))$grobs 
    legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]] 
    lwidth <- sum(legend$width) 
    grid.arrange(
    do.call(arrangeGrob, lapply(plots, function(x) 
     x + theme(legend.position="none"))), 
    legend, 
    ncol = length(plots)+1, 
    widths = unit.c(rep(unit(1, "npc") - lwidth, length(plots)), lwidth)) 
} 
grid_arrange_shared_legend_row(p1, p2) 

aber dies ist die zwei Grundstücke in einer Zeile nicht arrangieren, sondern eine Spalte:

not what I want

Diese Frage ist ähnlich to this one here aber anders, dass ich für die angepassten Breiten auch frage. Ich verwende Code-Extrakte sowohl aus dieser Frage + Antwort als auch aus dem GitHub.

Antwort

8

Warum verwenden Sie keine Facetten?

library(reshape2) 
dmelt <- melt(dsamp, id.vars = c("price", "clarity"), measure.vars = c("carat", "depth")) 
ggplot(dmelt, aes(x = price, y = value, color = clarity)) + 
    geom_point() + 
    facet_wrap(~ variable, scales = "free") 

resulting plot

+0

Vielen Dank, das funktioniert wirklich gut. Vielleicht solltest du auch die andere Frage mit dem gleichen Code beantworten? Es war ziemlich hoch meine Google-Ergebnisse und wäre eine gute Referenz für zukünftige Nutzer! – mts

2

Normalerweise verwende ich facet_wrap oder facet_grid als @Roland vorgeschlagen.

Vor einiger Zeit musste ich die grid.arrange (I die Etiketten auf der y-Achse wollte in einer bestimmten Reihenfolge zu färbenden) und dies ist die Funktion, die ich kam mit:

ggplot_shared_info <- function(...) { 
    plots <- list(...) 
    g <- ggplotGrob(plots[[1]])$grobs 
    legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]] 
    title <- g[[grep("plot.title", sapply(g, function(x) x$name))]] 
    xaxis <- g[[grep("axis.title.x", sapply(g, function(x) x$name))]] 
    yaxis <- g[[grep("axis.title.y", sapply(g, function(x) x$name))]] 

    lwidth <- sum(legend$width) 
    theight <- sum(title$height) 
    xheight <- sum(xaxis$height) 
    ywidth <- sum(yaxis$width) 

    grid.arrange(
    title, 
    arrangeGrob(
     yaxis, 
     do.call(arrangeGrob, c(lapply(plots, function(x) 
     x + theme(legend.position="none", 
        plot.title = element_blank(), 
        axis.title = element_blank())), 
     nrow = 1)), 
     legend, 
     nrow = 1, 
     widths = grid::unit.c(ywidth, unit(1, "npc") - ywidth - lwidth, lwidth) 
    ), 
    xaxis, 
    heights = grid::unit.c(theight, unit(1, "npc") - theight - xheight, xheight), 
    ncol = 1 
) 
} 

bearbeiten : Jetzt kann der Benutzer bestimmen, welches der aufgelisteten Plotelemente "verbunden" werden soll.

ggplot_shared_info <- function(..., elements = c('legend', 'title', 'yaxis', 'xaxis')) { 
    plots <- list(...) 
    g <- ggplotGrob(plots[[1]])$grobs 

    legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]] 
    lwidth <- sum(legend$width) 
    title <- g[[grep("plot.title", sapply(g, function(x) x$name))]] 
    theight <- sum(title$height) 
    xaxis <- g[[grep("axis.title.x", sapply(g, function(x) x$name))]] 
    xheight <- sum(xaxis$height) 
    yaxis <- g[[grep("axis.title.y", sapply(g, function(x) x$name))]] 
    ywidth <- sum(yaxis$width) 

    plots <- lapply(plots, function(x, elements = elements){ 
    if('legend' %in% elements) x <- x + theme(legend.position="none") 
    if('title' %in% elements) x <- x + theme(plot.title = element_blank()) 
    if('xaxis' %in% elements) x <- x + theme(axis.title.x = element_blank()) 
    if('yaxis' %in% elements) x <- x + theme(axis.title.y = element_blank()) 
    x 
    }, elements = elements) 
    plots <- do.call(arrangeGrob, c(plots, nrow = 1)) 

    if('legend' %in% elements) 
    plots <- arrangeGrob(plots, legend, nrow = 1, widths = grid::unit.c(unit(1, "npc") - lwidth, lwidth)) 
    if('yaxis' %in% elements) 
    plots <- arrangeGrob(yaxis, plots, nrow = 1, widths = grid::unit.c(ywidth, unit(1, "npc") - ywidth)) 
    if('title' %in% elements) 
    plots <- arrangeGrob(title, plots, ncol = 1, heights = grid::unit.c(theight, unit(1, "npc") - theight)) 
    if('xaxis' %in% elements)  
    plots <- arrangeGrob(plots, xaxis, ncol = 1, heights = grid::unit.c(unit(1, "npc") - xheight, xheight)) 
    grid.arrange(plots) 
} 
+1

Das funktioniert gut, und sollte noch allgemeiner sein, danke! Beachten Sie, dass ein fehlender Titel zu einem Fehler führt, d. H. Für das angegebene Beispiel wie folgt aufgerufen werden sollte: 'ggplot_shared_info (p1 + ggtitle (" p1 "), p2)' – mts

+0

Hätte nicht darüber nachgedacht! Ich werde versuchen, meinen Code zu bearbeiten, damit der Benutzer bestimmen kann, welche Elemente des Plots er "geteilt" möchte. – user5029763