2014-10-02 10 views
7

Ich versuche, mit ggplot ein Diagramm zu erstellen, das die absoluten Werte zweier Variablen vergleicht und auch das Verhältnis zwischen ihnen anzeigt. Da das Verhältnis unitless ist und die Werte nicht, kann ich sie nicht auf der gleichen y-Achse zeigen, also würde ich gerne als zwei separate Graphen mit ausgerichteten x-Achsen stapeln.Mehrere ggplot-Diagramme mit und ohne Legenden ausrichten

Hier ist, was ich bisher habe:

enter image description here

library(ggplot2) 
library(dplyr) 
library(gridExtra) 

# Prepare some sample data. 
results <- data.frame(index=(1:20)) 
results$control <- 50 * results$index 
results$value <- results$index * 50 + 2.5*results$index^2 - results$index^3/8 
results$ratio <- results$value/results$control 

# Plot absolute values 
plot_values <- ggplot(results, aes(x=index)) + 
    geom_point(aes(y=value, color="value")) + 
    geom_point(aes(y=control, color="control")) 

# Plot ratios between values 
plot_ratios <- ggplot(results, aes(x=index, y=ratio)) + 
    geom_point() 

# Arrange the two plots above each other 
grid.arrange(plot_values, plot_ratios, ncol=1, nrow=2) 

Das große Problem ist, dass die Legende auf der rechten Seite des ersten Plot macht es eine andere Größe. Ein kleines Problem ist, dass ich lieber nicht den Namen und die Häkchen der X-Achse auf dem obersten Diagramm zeige, um Unordnung zu vermeiden und klar zu machen, dass sie die gleiche Achse haben.

Ich habe in dieser Frage und ihre Antworten sah:

Align plot areas in ggplot

Leider keine Antwort gibt funktioniert gut für mich. Faceting scheint nicht gut zu passen, da ich für meine beiden Graphen völlig unterschiedliche y-Skalen haben möchte. Die Manipulation der von ggplot_gtable zurückgegebenen Dimensionen erscheint vielversprechender, aber ich weiß nicht, wie ich die Tatsache umgehen kann, dass die beiden Graphen eine andere Anzahl von Zellen haben. Naiv, diesen Code zu kopieren scheint die resultierenden Diagrammdimensionen für meinen Fall nicht zu ändern.

Hier ist eine andere ähnliche Frage:

The perils of aligning plots in ggplot

Die Frage selbst scheint eine gute Option vorschlagen, aber rbind.gtable beschwert sich, wenn die Tabellen unterschiedliche Anzahl von Spalten haben, was hier der Fall ist aufgrund der Legende. Vielleicht gibt es eine Möglichkeit, eine extra leere Spalte in die zweite Tabelle einzufügen? Oder eine Möglichkeit, die Legende im ersten Diagramm zu unterdrücken und sie dann dem kombinierten Diagramm wieder hinzuzufügen?

+0

Ich würde die rbind_gtable Ansatz verwenden, aber wie Sie beachten, müssen Sie die Gtables haben die gleiche Anzahl von Spalten. Es ist [einfach genug] (http://stackoverflow.com/questions/21529926/arrange-ggplots-together-in-custom-ratios-and-spacing/21531303#21531303), mit gtable_add_cols – baptiste

+0

[siehe auch dieses] (http://stackoverflow.com/questions/25893673/how-to-arrange-plots-with-shared-axes/25923349#25923349) – baptiste

+0

Warum nicht einfach entfernen Sie die Facettentitel beim Erstellen einer facettierten Darstellung? Siehe meine Antwort für ein Beispiel. – Jaap

Antwort

7

Hier ist eine Lösung, die keine explizite Verwendung von Rastergrafiken erfordert. Es verwendet Facetten und blendet den Legendeneintrag für "Ratio" aus (mit einer Technik von https://stackoverflow.com/a/21802022).

library(reshape2) 

results_long <- melt(results, id.vars="index") 
results_long$facet <- ifelse(results_long$variable=="ratio", "ratio", "values") 
results_long$facet <- factor(results_long$facet, levels=c("values", "ratio")) 

ggplot(results_long, aes(x=index, y=value, colour=variable)) + 
    geom_point() + 
    facet_grid(facet ~ ., scales="free_y") + 
    scale_colour_manual(breaks=c("control","value"), 
         values=c("#1B9E77", "#D95F02", "#7570B3")) + 
    theme(legend.justification=c(0,1), legend.position=c(0,1)) + 
    guides(colour=guide_legend(title=NULL)) + 
    theme(axis.title.y = element_blank()) 

plot with legend for only one facet

4

Ermutigt durch Baptistes Kommentar, hier ist, was ich am Ende tat

Two graphs: the first shows two series rising together, one linearly, the other non-linear; the second show the relative ratio between the two series, rising then falling over time

library(ggplot2) 
library(dplyr) 
library(gridExtra) 

# Prepare some sample data. 
results <- data.frame(index=(1:20)) 
results$control <- 50 * results$index 
results$value <- results$index * 50 + 2.5*results$index^2 - results$index^3/8 
results$ratio <- results$value/results$control 

# Plot ratios between values 
plot_ratios <- ggplot(results, aes(x=index, y=ratio)) + 
    geom_point() 


# Plot absolute values 
remove_x_axis = 
    theme(
    axis.ticks.x = element_blank(), 
    axis.text.x = element_blank(), 
    axis.title.x = element_blank()) 

plot_values <- ggplot(results, aes(x=index)) + 
    geom_point(aes(y=value, color="value")) + 
    geom_point(aes(y=control, color="control")) + 
    remove_x_axis 

# Arrange the two plots above each other 
grob_ratios <- ggplotGrob(plot_ratios) 
grob_values <- ggplotGrob(plot_values) 
legend_column <- 5 
legend_width <- grob_values$widths[legend_column] 
grob_ratios <- gtable_add_cols(grob_ratios, legend_width, legend_column-1) 
grob_combined <- gtable:::rbind_gtable(grob_values, grob_ratios, "first") 
grob_combined <- gtable_add_rows(
    grob_combined,unit(-1.2,"cm"), pos=nrow(grob_values)) 
grid.draw(grob_combined) 

(ich später erkannte ich nicht einmal das zu extrahieren brauchte Legendenbreite, da das Argument size="first" zu rbind sagt, dass es nur die eine hat, die die andere überschreibt.)

Es fühlt sich ein bisschen chaotisch an, aber es ist genau das Layout, auf das ich gehofft hatte.

2

Eine alternative & ganz einfache Lösung ist wie folgt:

# loading needed packages 
library(ggplot2) 
library(dplyr) 
library(tidyr) 

# Prepare some sample data 
results <- data.frame(index=(1:20)) 
results$control <- 50 * results$index 
results$value <- results$index * 50 + 2.5*results$index^2 - results$index^3/8 
results$ratio <- results$value/results$control 

# reshape into long format 
long <- results %>% 
    gather(variable, value, -index) %>% 
    mutate(facet = ifelse(variable=="ratio", "ratio", "values")) 
long$facet <- factor(long$facet, levels=c("values", "ratio")) 

# create the plot & remove facet labels with theme() elements 
ggplot(long, aes(x=index, y=value, colour=variable)) + 
    geom_point() + 
    facet_grid(facet ~ ., scales="free_y") + 
    scale_colour_manual(breaks=c("control","value"), values=c("green", "red", "blue")) + 
    theme(axis.title.y=element_blank(), strip.text=element_blank(), strip.background=element_blank()) 

die gibt: diese enter image description here

4

Versuchen:

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

AlignPlots <- function(...) { 
    LegendWidth <- function(x) x$grobs[[8]]$grobs[[1]]$widths[[4]] 

    plots.grobs <- lapply(list(...), ggplotGrob) 

    max.widths <- do.call(unit.pmax, lapply(plots.grobs, "[[", "widths")) 
    plots.grobs.eq.widths <- lapply(plots.grobs, function(x) { 
    x$widths <- max.widths 
    x 
    }) 

    legends.widths <- lapply(plots.grobs, LegendWidth) 
    max.legends.width <- do.call(max, legends.widths) 
    plots.grobs.eq.widths.aligned <- lapply(plots.grobs.eq.widths, function(x) { 
    if (is.gtable(x$grobs[[8]])) { 
     x$grobs[[8]] <- gtable_add_cols(x$grobs[[8]], 
             unit(abs(diff(c(LegendWidth(x), 
                 max.legends.width))), 
              "mm")) 
    } 
    x 
    }) 

    plots.grobs.eq.widths.aligned 
} 

df <- data.frame(x = c(1:5, 1:5), 
       y = c(1:5, seq.int(5,1)), 
       type = factor(c(rep_len("t1", 5), rep_len("t2", 5)))) 

p1.1 <- ggplot(diamonds, aes(clarity, fill = cut)) + geom_bar() 
p1.2 <- ggplot(df, aes(x = x, y = y, colour = type)) + geom_line() 
plots1 <- AlignPlots(p1.1, p1.2) 
do.call(grid.arrange, plots1) 

p2.1 <- ggplot(diamonds, aes(clarity, fill = cut)) + geom_bar() 
p2.2 <- ggplot(df, aes(x = x, y = y)) + geom_line() 
plots2 <- AlignPlots(p2.1, p2.2) 
do.call(grid.arrange, plots2) 

Produziert dies: With legends Without one legend

// Basierend auf mehreren Antworten Baptistes

+0

Danke. Dies funktionierte gut für mich mit einem 2x2 Array von Grundstücken, wo ein Grundstück keine Legende hat. – michael

Verwandte Themen