marrangeGrob but can't add legends - r

I used marrangeGrob() instead of facet_wrap() to produce my plots from a list of plots. However, I can't seem to add a legend.
I already extracted my legend using
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)}
However, I am unable to add it it my plot.
Does anyone know a way?

Here's an example using the built-in diamonds data frame:
library(ggplot2)
library(gridExtra)
library(dplyr)
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)}
First we'll create two plots to lay out together:
df <- count(diamonds, cut)
p1 = ggplot(df, aes(x=cut, y=n, label=format(n, big.mark=","), fill=cut)) +
geom_bar(stat="identity") +
geom_text(aes(y=0.5*n), colour="white") +
coord_flip() +
theme(legend.position="bottom")
p2 = ggplot(diamonds %>% sample_n(1000), aes(x=carat, y=price, colour=cut)) +
geom_point()
Now save the legend from p1 as a separate grob:
leg = g_legend(p1)
Lay out the two plots side-by-side using arrangeGrob and then use marrangeGrob to lay out the two-plot layout and the legend beneath it. Note that we also remove the legends from the original plots.
marrangeGrob(grobs=list(
arrangeGrob(grobs=lapply(list(p1,p2), function(p) {
p + guides(colour=FALSE, fill=FALSE)
}), ncol=2),
leg), ncol=1, nrow=2, heights=c(20,1))

Related

ggplot2 grid_arrange_shared_legend share axis labels

I am using grid_arrange_shared_legend to combine plots with the same legend into one plot with one legend. Is there a way to edit the function slightly so it also shares the x and y axis labels? For example, if I made 3 graphs and wanted a 3x1 grid of those graphs made with grid_arrange_shared_legend, and wanted the shared x axis label on the very bottom and the shared y axis label on the left, how would I do that?
This is the code I am using for the function:
grid_arrange_shared_legend <- function(..., ncol=1, mytitle="mytitle") {
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, c(ncol=ncol, lapply(plots, function(x)
x + theme(legend.position="none")))),
legend,
ncol = 1,
heights = unit.c(unit(.95, "npc") - lheight, lheight),
top=textGrob(mytitle, gp = gpar(fontsize=16)))
}
I had the same problem.
My solution was to add a blank x-axis title to each plot (to save space to add an axis title):
p1 <- p1 + xlab(" ")
and then having created the plots with shared legends:
grid_arrange_shared_legend(p1, p2, p3, position = "right")
I then added new text as a new axis title:
grid.text("My title", y= 0.02, x=0.43)

Common legend and identical plot width for multi-panel plot

I was wondering if there is a way to have a common legend and at the same time identical plot widths for a multi-panel plot. I know how to get identical plot widths without a legend, or with a legend that is not vertically centered.
Here's Hadley's fix that I use for the common legend (https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs):
library(ggplot2)
library(gridExtra)
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
p1 <- qplot(carat, price, data=dsamp, colour=clarity)
p2 <- qplot(carat, price, data=dsamp, colour=clarity, geom="path")
g_legend<-function(p){
tmp <- ggplotGrob(p)
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}
legend <- g_legend(p1)
lwidth <- sum(legend$width)
## using grid.arrange for convenience
## could also manually push viewports
grid.arrange(arrangeGrob(p1 + theme(legend.position="none"),
p2 + theme(legend.position="none"),
main ="this is a title",
left = "This is my global Y-axis title"), legend,
widths=unit.c(unit(1, "npc") - lwidth, lwidth), nrow=1)
To illustrate my point, I changed p2 by adding two other variables of the same dataset keeping clarity as the common legend:
p1 <- qplot(carat, price, data=dsamp, colour=clarity)
p2 <- qplot(depth, table, data=dsamp, colour=clarity, geom="path")
g_legend<-function(p){
tmp <- ggplotGrob(p)
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)}
legend <- g_legend(p1)
lwidth <- sum(legend$width)
## using grid.arrange for convenience
## could also manually push viewports
grid.arrange(arrangeGrob(p1 + theme(legend.position="none"),
p2 + theme(legend.position="none"),
main ="this is a title",
left = "This is my global Y-axis title"), legend,
widths=unit.c(unit(1, "npc") - lwidth, lwidth), nrow=1)
This may not make sense in this example, but I have four different measurements on the same experimental units, which I want to graphically display in 2 plots with same plot widths and a common vertically centered legend.
I think you can achieve your goal better with faceting:
ggplot(dsamp, aes(x = carat, y = price, colour = clarity)) +
geom_point(data = cbind(dsamp, group = 1)) +
geom_path(data = cbind(dsamp, group = 2)) +
facet_grid(group ~ .) +
theme(strip.text.y = element_blank(),
strip.background = element_blank()) +
ggtitle("this is a title")
I've assumed that you have identical variables in your plots as in your example. Otherwise, I've had to use a lot of tinkering when I needed to align plots myself: See scatterplot with alpha transparent histograms in R for an example.

questions on understanding the function of extracting grobs structure using sapply

I am trying to understand the following segment, especially the one, sapply(tmp$grobs, function(x) x$name
require(gridExtra)
require(ggplot2)
my_hist <- ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar()
tmp <- ggplot_gtable(ggplot_build(my_hist))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
I think this function is used to extract the name of tmp$grobs, which is shown as follows
Then I tried. First question, how to understand the structure, like [[1]]; further, why (tmp$grobs[1])$name just does not work.
this isn't an answer to your question, but a coding suggestion
require(ggplot2)
my_hist <- ggplot(diamonds, aes(clarity, fill=cut)) + geom_bar()
tmp <- ggplotGrob(my_hist)
legend <- tmp$grobs[[grep("guide-box", tmp$layout$name)]]

Saving legend from grid.draw without space next to legend

I would like to exctact the legend from a plot:
a<-rnorm(100)
b<-runif(100)
ba<-cbind(a,b)
colnames(ba)<-c("a","b")
ba<-melt(ba,id.vars=1:1)
colnames(ba)<-c("c","variable","value")
plot1<-ggplot(q,aes(x=c,y=value,colour=variable,size=variable))+geom_point()+theme(legend.position="right")
Now I extract the legend from the grob:
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)}
legend<-g_legend(plot1)
I can save this legend by using grid.draw:
pdf("plot.pdf")
grid.draw(legend)
dev.off()
The resulting image will contain the legend with a lot of white space. Is there a way to plot only the legend but not to have so much space next to it?
gg <- gtable::gtable_filter(x=ggplotGrob(plot1),
pattern="guide", trim=TRUE)[["grobs"]][[1]][["grobs"]][[1]]
pdf("legend.pdf",
width=convertWidth(sum(gg$width), "in", valueOnly=TRUE),
height=convertHeight(sum(gg$heights), "in", valueOnly=TRUE))
grid.draw(gg)
dev.off()

How to plot just the legends in ggplot2?

I'm currently working with igraph and have colour labelled my vertices. I would like to add a legend Indicating what each colour represents.
What I can think of at this point is to use ggplot2 to print only the legend and hide a bar plot.
Is there a way to just output the legend?
Here are 2 approaches:
Set Up Plot
library(ggplot2)
library(grid)
library(gridExtra)
my_hist <- ggplot(diamonds, aes(clarity, fill = cut)) +
geom_bar()
Cowplot approach
# Using the cowplot package
legend <- cowplot::get_legend(my_hist)
grid.newpage()
grid.draw(legend)
Home grown approach
Shamelessly stolen from: Inserting a table under the legend in a ggplot2 histogram
## Function to extract legend
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]]
legend
}
legend <- g_legend(my_hist)
grid.newpage()
grid.draw(legend)
Created on 2018-05-31 by the reprex package (v0.2.0).
Cowplot handily adds a function to extract the legend. The following is taken directly from the manual.
library(ggplot2)
library(cowplot)
p1 <- ggplot(mtcars, aes(mpg, disp)) + geom_line()
plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + geom_point(size=2.5)
# Note that these cannot be aligned vertically due to the legend in the plot.mpg
ggdraw(plot_grid(p1, plot.mpg, ncol=1, align='v'))
# now extract the legend
legend <- get_legend(plot.mpg)
# and replot suppressing the legend
plot.mpg <- plot.mpg + theme(legend.position='none')
# Now plots are aligned vertically with the legend to the right
ggdraw(plot_grid(plot_grid(p1, plot.mpg, ncol=1, align='v'),
plot_grid(NULL, legend, ncol=1),
rel_widths=c(1, 0.2)))
I used the ggpubr package - makes it very easy!
https://rpkgs.datanovia.com/ggpubr/reference/get_legend.html
# Extract the legend. Returns a gtable
leg <- get_legend(p)
# Convert to a ggplot and print
as_ggplot(leg)
I was color coding the vertices in the graph and wanted to generate a legend as simply , elegantly and as quickly as I can.
The fastest way to do this I've come to believe is generate the legend separately using ggplot2 before "pasting" the legend into the same plot as igraph using viewport and layout()
In this method there is no need to call the rescale or asp arguements in the plot.igraph() function.
Using the g_legend function on a data.frame, leg, with 2 columns, x being the appropriate vertex attribute and y being the hex colour code used in my igraph plot, I've done the following.
My igraph object is t8g
legend <- g_legend(leg)
vpleg <- viewport(width = 0.1, height = 0.1, x=0.85,y=0.5)
layout(matrix(c(1,2),1,2,byrow=T),widths=c(3,1))
plot(t8g,edge.width=1,edge.arrow.size=0.1,vertex.label.cex=0.2,main="b2_top10")
pushViewport(vpleg)
grid.draw(legend)

Resources