Tambahkan Legenda umum untuk ggplots gabungan

143

Saya memiliki dua ggplots yang saya sejajarkan secara horizontal grid.arrange. Saya telah melihat-lihat banyak posting forum, tetapi semua yang saya coba tampaknya merupakan perintah yang sekarang diperbarui dan diberi nama yang lain.

Data saya terlihat seperti ini;

# Data plot 1                                   
        axis1     axis2   
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.417117 -0.002592
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.186860 -0.203273

# Data plot 2   
        axis1     axis2
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988

#And I run this:
library(ggplot2)
library(gridExtra)


groups=c('group1','group2','group3','group4','group1','group2','group3','group4')

x1=data1[,1]
y1=data1[,2]

x2=data2[,1]
y2=data2[,2]

p1=ggplot(data1, aes(x=x1, y=y1,colour=groups)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)

p2=ggplot(data2, aes(x=x2, y=y2,colour=groups)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)

#Combine plots
p3=grid.arrange(
p1 + theme(legend.position="none"), p2+ theme(legend.position="none"), nrow=1, widths = unit(c(10.,10), "cm"), heights = unit(rep(8, 1), "cm")))

Bagaimana cara mengekstrak legenda dari salah satu plot ini dan menambahkannya ke bagian bawah / tengah plot gabungan?

jO.
sumber
3
Saya terkadang mengalami masalah ini. Jika Anda tidak ingin memilah-milah plot, solusi termudah yang saya tahu adalah hanya menyimpannya dengan legenda kemudian gunakan Photoshop / Ilustrator untuk menempelkannya ke plot legenda kosong. Aku tahu tidak elegan - tapi praktis, cepat dan mudah.
Stephen Henderson
@StephenHenderson Itulah jawabannya. Faset atau proses pasca dengan editor gfx.
Brandon Bertelsen

Jawaban:

108

Perbarui 2015-Feb

Lihat jawaban Steven di bawah


df1 <- read.table(text="group   x     y   
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.417117 -0.002592
group1 -0.212201  0.358867
group2 -0.279756 -0.126194
group3  0.186860 -0.203273
group4  0.186860 -0.203273",header=TRUE)

df2 <- read.table(text="group   x     y   
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988
group1  0.211826 -0.306214
group2 -0.072626  0.104988
group3 -0.072626  0.104988
group4 -0.072626  0.104988",header=TRUE)


library(ggplot2)
library(gridExtra)

p1 <- ggplot(df1, aes(x=x, y=y,colour=group)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8) + theme(legend.position="bottom")

p2 <- ggplot(df2, aes(x=x, y=y,colour=group)) + geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8)

#extract legend
#https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
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)}

mylegend<-g_legend(p1)

p3 <- grid.arrange(arrangeGrob(p1 + theme(legend.position="none"),
                         p2 + theme(legend.position="none"),
                         nrow=1),
             mylegend, nrow=2,heights=c(10, 1))

Berikut adalah plot yang dihasilkan: 2 plot dengan legenda umum

Roland
sumber
3
kedua jawaban mengarah ke halaman wiki yang sama yang dapat diperbarui karena versi baru ggplot2 memecahkan kode.
baptiste
Lebih dari enam tahun kemudian, jawaban ini menyelesaikan masalah saya. Terima kasih!
SPK.z
1
Ini mungkin mudah bagi sebagian / kebanyakan orang, tetapi saya tidak langsung mendapatkannya jadi saya pikir saya akan berkomentar. Jika Anda ingin legenda umum di atas plot (bukan di bawah), yang perlu Anda lakukan hanyalah mengganti argumen. Dalam contoh di atas, mylegend pergi sebelumnya arrangeGrob(). Anda juga perlu membalik ketinggian (yaituheights=c(1,10)
ljh2001
122

Anda juga dapat menggunakan ggarrange dari paket ggpubr dan menyetel "common.legend = TRUE":

library(ggpubr)

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data = dsamp, colour = clarity)
p2 <- qplot(cut, price, data = dsamp, colour = clarity)
p3 <- qplot(color, price, data = dsamp, colour = clarity)
p4 <- qplot(depth, price, data = dsamp, colour = clarity) 

ggarrange(p1, p2, p3, p4, ncol=2, nrow=2, common.legend = TRUE, legend="bottom")

masukkan deskripsi gambar di sini

Huiyan Wan
sumber
1
Apakah mungkin ini tidak berfungsi di dalam aplikasi mengkilap (atau flexdashboard) dengan renderPlot ()? Ini berfungsi dengan baik dalam skrip R normal dengan plot normal. Tetapi ketika saya melakukan hal yang sama persis dengan plot yang dibuat dengan renderPlot () di flexdashboard saya, tidak ada yang muncul.
Tingolfin
1
Terima kasih untuk ini - saya pikir ini adalah solusi termudah untuk apa yang saya cari
Komal Rathi
Ini luar biasa! Terima kasih!
yanes
@Tingolfin Saya kadang-kadang harus membungkus print(ggarrangeobject)salah satu ggarrangeobjek saya ketika saya membutuhkannya untuk diplot oleh beberapa fungsi lain, yang mungkin mirip dengan solusi untuk Anda renderPlot()?
Brandon
common.legend = TRUEhanya itu yang saya butuhkan!
Aryo
63

Jawaban Roland perlu diperbarui. Lihat: https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs

Metode ini telah diperbarui untuk ggplot2 v1.0.0.

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


grid_arrange_shared_legend <- function(...) {
    plots <- list(...)
    g <- ggplotGrob(plots[[1]] + theme(legend.position="bottom"))$grobs
    legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
    lheight <- sum(legend$height)
    grid.arrange(
        do.call(arrangeGrob, lapply(plots, function(x)
            x + theme(legend.position="none"))),
        legend,
        ncol = 1,
        heights = unit.c(unit(1, "npc") - lheight, lheight))
}

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data=dsamp, colour=clarity)
p2 <- qplot(cut, price, data=dsamp, colour=clarity)
p3 <- qplot(color, price, data=dsamp, colour=clarity)
p4 <- qplot(depth, price, data=dsamp, colour=clarity)
grid_arrange_shared_legend(p1, p2, p3, p4)

Perhatikan kekurangan ggplot_gtabledan ggplot_build. ggplotGrobdigunakan sebagai gantinya. Contoh ini sedikit lebih berbelit-belit daripada solusi di atas tetapi masih menyelesaikannya untuk saya.

Steven Lockton
sumber
10
Hai, saya punya 6 plot, dan saya ingin menyusun 6 plot sebagai 2 baris × 3 col dan menggambar legenda di atas, jadi bagaimana cara mengubah fungsinya grid_arrange_shared_legend? Terima kasih!
just_rookie
4
@just_rookie apakah Anda menemukan solusi bagaimana mengubah fungsi sehingga seseorang dapat menggunakan pengaturan ncol dan nrow yang berbeda daripada hanya ncol = 1?
Giuseppe
Hai, saya mencoba solusi ini, ini berfungsi dengan baik, namun saat mencetaknya, saya mendapat 2 halaman pdf, bukan hanya 1, yang pertama kosong sedangkan yang berikutnya berisi plot saya, mengapa saya mendapat perilaku seperti itu? terima kasih,
HanniBaL90
bagi siapa saja bagaimana mendapatkan isse yang sama dengan saya, berikut adalah solusinya: stackoverflow.com/questions/12481267/…
HanniBaL90
1
Barang bagus di sini. Ada ide bagaimana seseorang dapat menambahkan satu judul untuk semua plot?
Pertinax
31

Solusi baru yang menarik sedang digunakan patchwork. Sintaksnya sangat sederhana:

library(ggplot2)
library(patchwork)

p1 <- ggplot(df1, aes(x = x, y = y, colour = group)) + 
  geom_point(position = position_jitter(w = 0.04, h = 0.02), size = 1.8)
p2 <- ggplot(df2, aes(x = x, y = y, colour = group)) + 
  geom_point(position = position_jitter(w = 0.04, h = 0.02), size = 1.8)

combined <- p1 + p2 & theme(legend.position = "bottom")
combined + plot_layout(guides = "collect")

Dibuat pada 2019-12-13 oleh paket reprex (v0.2.1)

MSR
sumber
2
Jika Anda sedikit mengubah urutan perintah, Anda dapat melakukan ini dalam satu baris: combined <- p1 + p2 + plot_layout(guides = "collect") & theme(legend.position = "bottom")
mlcyo
19

Saya sarankan menggunakan cowplot. Dari sketsa R mereka :

# load cowplot
library(cowplot)

# down-sampled diamonds data set
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]

# Make three plots.
# We set left and right margins to 0 to remove unnecessary spacing in the
# final plot arrangement.
p1 <- qplot(carat, price, data=dsamp, colour=clarity) +
   theme(plot.margin = unit(c(6,0,6,0), "pt"))
p2 <- qplot(depth, price, data=dsamp, colour=clarity) +
   theme(plot.margin = unit(c(6,0,6,0), "pt")) + ylab("")
p3 <- qplot(color, price, data=dsamp, colour=clarity) +
   theme(plot.margin = unit(c(6,0,6,0), "pt")) + ylab("")

# arrange the three plots in a single row
prow <- plot_grid( p1 + theme(legend.position="none"),
           p2 + theme(legend.position="none"),
           p3 + theme(legend.position="none"),
           align = 'vh',
           labels = c("A", "B", "C"),
           hjust = -1,
           nrow = 1
           )

# extract the legend from one of the plots
# (clearly the whole thing only makes sense if all plots
# have the same legend, so we can arbitrarily pick one.)
legend_b <- get_legend(p1 + theme(legend.position="bottom"))

# add the legend underneath the row we made earlier. Give it 10% of the height
# of one plot (via rel_heights).
p <- plot_grid( prow, legend_b, ncol = 1, rel_heights = c(1, .2))
p

plot gabungan dengan legenda di bagian bawah

Gregor Sturm
sumber
Ini hanya cara, yang memungkinkan untuk menempatkan legenda manual dalam plot saya dengan annotate_figure(ggarrange()), menggunakan legend_b (). Terima kasih banyak, tuhan memberkati Anda!
Jean Karlos
13

@Giuseppe, Anda mungkin ingin mempertimbangkan ini untuk spesifikasi yang fleksibel dari pengaturan plot (dimodifikasi dari sini ):

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

grid_arrange_shared_legend <- function(..., nrow = 1, ncol = length(list(...)), position = c("bottom", "right")) {

  plots <- list(...)
  position <- match.arg(position)
  g <- ggplotGrob(plots[[1]] + theme(legend.position = position))$grobs
  legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
  lheight <- sum(legend$height)
  lwidth <- sum(legend$width)
  gl <- lapply(plots, function(x) x + theme(legend.position = "none"))
  gl <- c(gl, nrow = nrow, ncol = ncol)

  combined <- switch(position,
                     "bottom" = arrangeGrob(do.call(arrangeGrob, gl),
                                            legend,
                                            ncol = 1,
                                            heights = unit.c(unit(1, "npc") - lheight, lheight)),
                     "right" = arrangeGrob(do.call(arrangeGrob, gl),
                                           legend,
                                           ncol = 2,
                                           widths = unit.c(unit(1, "npc") - lwidth, lwidth)))
  grid.newpage()
  grid.draw(combined)

}

Argumen tambahan nrowdan ncolkontrol tata letak plot yang diatur:

dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data = dsamp, colour = clarity)
p2 <- qplot(cut, price, data = dsamp, colour = clarity)
p3 <- qplot(color, price, data = dsamp, colour = clarity)
p4 <- qplot(depth, price, data = dsamp, colour = clarity)
grid_arrange_shared_legend(p1, p2, p3, p4, nrow = 1, ncol = 4)
grid_arrange_shared_legend(p1, p2, p3, p4, nrow = 2, ncol = 2)

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

epsilone
sumber
Sama seperti solusi lainnya, saya mencobanya, berfungsi dengan baik, namun saat mencetaknya, saya mendapat 2 halaman pdf, bukan hanya 1, yang pertama kosong sedangkan yang berikutnya berisi plot saya, mengapa saya mendapat perilaku seperti itu? terima kasih,
HanniBaL90
bagi siapa saja bagaimana mendapatkan isse yang sama dengan saya, berikut adalah solusinya: stackoverflow.com/questions/12481267/…
HanniBaL90
Bisakah seseorang menjelaskan saya solusinya? Bagaimana saya bisa menempatkan legenda di atas, bukan di bawah? Terima kasih
HanniBaL90
9

Jika Anda memplot variabel yang sama di kedua plot, cara paling sederhana adalah menggabungkan bingkai data menjadi satu, lalu gunakan facet_wrap.

Sebagai contoh Anda:

big_df <- rbind(df1,df2)

big_df <- data.frame(big_df,Df = rep(c("df1","df2"),
times=c(nrow(df1),nrow(df2))))

ggplot(big_df,aes(x=x, y=y,colour=group)) 
+ geom_point(position=position_jitter(w=0.04,h=0.02),size=1.8) 
+ facet_wrap(~Df)

Plot 1

Contoh lain menggunakan kumpulan data berlian. Ini menunjukkan bahwa Anda bahkan dapat membuatnya berfungsi jika Anda hanya memiliki satu variabel yang sama di antara plot Anda.

diamonds_reshaped <- data.frame(price = diamonds$price,
independent.variable = c(diamonds$carat,diamonds$cut,diamonds$color,diamonds$depth),
Clarity = rep(diamonds$clarity,times=4),
Variable.name = rep(c("Carat","Cut","Color","Depth"),each=nrow(diamonds)))

ggplot(diamonds_reshaped,aes(independent.variable,price,colour=Clarity)) + 
geom_point(size=2) + facet_wrap(~Variable.name,scales="free_x") + 
xlab("")

Plot 2

Hanya hal rumit dengan contoh kedua adalah bahwa variabel faktor dipaksa menjadi numerik saat Anda menggabungkan semuanya ke dalam satu bingkai data. Jadi idealnya, Anda akan melakukan ini terutama ketika semua variabel yang Anda minati memiliki tipe yang sama.

hmgeiger.dll
sumber
2

@Bayu_joo

Saya tidak tahu Grobs dll apa pun, tetapi saya meretas solusi untuk dua plot, harus memungkinkan untuk diperluas ke nomor arbitrer tetapi tidak dalam fungsi seksi:

plots <- list(p1, p2)
g <- ggplotGrob(plots[[1]] + theme(legend.position="bottom"))$grobs
legend <- g[[which(sapply(g, function(x) x$name) == "guide-box")]]
lheight <- sum(legend$height)
tmp <- arrangeGrob(p1 + theme(legend.position = "none"), p2 + theme(legend.position = "none"), layout_matrix = matrix(c(1, 2), nrow = 1))
grid.arrange(tmp, legend, ncol = 1, heights = unit.c(unit(1, "npc") - lheight, lheight))
Mendongkrak
sumber
2

Jika legenda sama untuk kedua plot, ada solusi sederhana yang digunakan grid.arrange(dengan asumsi Anda ingin legenda Anda sejajar dengan kedua plot baik secara vertikal maupun horizontal). Cukup pertahankan legenda untuk plot paling bawah atau paling kanan sambil menghilangkan legenda untuk plot lainnya. Namun, menambahkan legenda ke satu plot saja, mengubah ukuran satu plot relatif terhadap plot lainnya. Untuk menghindari hal ini, gunakan heightsperintah untuk menyesuaikan secara manual dan menyimpannya dalam ukuran yang sama. Anda bahkan dapat menggunakan grid.arrangeuntuk membuat judul sumbu yang umum. Perhatikan bahwa ini akan membutuhkan library(grid)tambahan library(gridExtra). Untuk plot vertikal:

y_title <- expression(paste(italic("E. coli"), " (CFU/100mL)"))

grid.arrange(arrangeGrob(p1, theme(legend.position="none"), ncol=1), arrangeGrob(p2, theme(legend.position="bottom"), ncol=1), heights=c(1,1.2), left=textGrob(y_title, rot=90, gp=gpar(fontsize=20)))

Berikut adalah hasil untuk grafik serupa untuk proyek yang saya kerjakan: masukkan deskripsi gambar di sini

Wesley Lozano
sumber