2014-10-02 85 views
7

我想用ggplot繪製一個圖來比較兩個變量的絕對值,並且還顯示它們之間的比率。由於比例是無單位的,並且數值不是,所以我不能在同一個y軸上顯示它們,所以我想垂直堆疊爲兩個帶有對齊x軸的獨立圖形。對齊多個ggplot圖有無圖例

這裏是我到目前爲止有:

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) 

最大的問題是,在第一條曲線右側的傳說使得它不同的大小。一個小問題是,我寧願不在頂部圖上顯示X軸名稱和刻度標記,以避免混亂,並明確表示它們共享同一個軸。

我看這個問題,它的答案:

Align plot areas in ggplot

不幸的是,答案也很適合我。刻面看起來不太合適,因爲我想爲我的兩個圖表製作完全不同的y刻度。操縱ggplot_gtable返回的維度似乎更有希望,但我不知道如何解決兩個圖形具有不同數量的單元格這一事實。天真地複製該代碼似乎並沒有改變我的情況下生成的圖形尺寸。

這裏還有一個類似的問題:

The perils of aligning plots in ggplot

這個問題本身似乎暗示一個不錯的選擇,但rbind.gtable抱怨,如果表中有列數不同,在這裏的話,由於傳說。也許有一種方法可以在第二個表中插入額外的空列?或者一種方法來抑制第一個圖中的圖例,然後將其重新添加到組合圖中?

+0

我會使用rbind_gtable的方法,但正如你所說的,你需要讓gtables具有相同數量的cols。它[雖然很容易](http://stackoverflow.com/questions/21529926/arrange-ggplots-together-in-custom-ratios-and-spacing/21531303#21531303),與gtable_add_cols – baptiste 2014-10-02 11:29:13

+0

[也見這一個]( http://stackoverflow.com/questions/25893673/how-to-arrange-plots-with-shared-axes/25923349#25923349) – baptiste 2014-10-02 12:00:39

+0

爲什麼不創建一個多面情節時刪除facet-titles?以我的答案爲例。 – Jaap 2014-10-02 20:53:53

回答

7

這是一個不需要顯式使用網格圖形的解決方案。它使用方面,並隱藏「比例」的圖例條目(使用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

通過巴蒂斯特的評論的鼓舞,這是我最後還是沒買:

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) 

(後來我才意識到,我甚至沒有需要解壓圖例寬度,因爲rbind的size="first"參數告訴它只是讓那個覆蓋另一個。)

這感覺有點凌亂,但它正是我期望的佈局。

2

一種替代&相當容易的解決方案如下:

# 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()) 

其給出: enter image description here

4

嘗試這種情況:

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) 

產生以下: With legends Without one legend

//基於多個baptiste的回答

+0

謝謝。這對我來說很好,用一個2×2的情節陣列,其中一個情節沒有傳說。 – michael 2015-08-18 01:11:48