g_legend
grid_arrange_shared_legend
reposition_legend
ggplot2
by default places the legend in the margin of the entire plot. This is in many instances a nice solution. If this is not desired, theme(legend.position)
can be used to place the legend in relative measures on the entire plot:
library(ggplot2)
library(grid)
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
(d <- ggplot(dsamp, aes(carat, price)) +
geom_point(aes(colour = clarity)) +
theme(legend.position = c(0.06, 0.75))
)
Imprecise positioning of legend with theme(legend.position)
.
This is however prone to badly positioning, if e.g. the plot is resized or font size changed:
Left: Base font size set to 22 pt. Right: Zoom on plot that is plotted at 150% size.
With our function, we can specify exactly how we want it in the plotting area:
library(lemon)
reposition_legend(d, 'top left')
Exact positioning of legend in the main panel.
And it stays there.
Left: Base font size set to 22 pt. Right: Zoom on plot that is plotted at 150% size.
The left plot is printed in full size at the end of this document.
For our final trick in this act, we reposition a legend with multiple guides. For this, use theme(legend.box.background)
to put a background around the entire legend, not just the individual guides.
d2 <- d + aes(shape=cut) +
theme(legend.box.background = element_rect(fill='#fffafa'),
legend.background = element_blank())
reposition_legend(d2, 'left')
Legend with multple guides on a tacky ‘snow’ background.
To our knowledge, there exists two methods for extracting the legend:
g1 <- function(a.gplot){
if (!gtable::is.gtable(a.gplot))
a.gplot <- ggplotGrob(a.gplot)
leg <- which(sapply(a.gplot$grobs, function(x) x$name) == "guide-box")
a.gplot$grobs[[leg]]
}
g2 <- function(a.gplot){
if (!gtable::is.gtable(a.gplot))
a.gplot <- ggplotGrob(a.gplot)
gtable::gtable_filter(a.gplot, 'guide-box', fixed=TRUE)
}
There is very little difference between them, as the latter essentially does the same as the former. The latter however encapsulated the former in a gtable. This is even more evident with multiple guides:
(da <- ggplot(dsamp, aes(carat, price)) +
geom_point(aes(colour = clarity, shape=cut)) +
theme(legend.box = 'horizontal')
)
Two guides in a single legend, in a grossly undersized figure.
print(g1(da))
## TableGrob (5 x 7) "guide-box": 3 grobs
## z cells name
## 99_86c3b14d399915779b8ee8f468a98f91 1 (3-3,3-3) guides
## 99_e48d95bbf9bfaae50e1c3ba1663f2112 2 (3-3,5-5) guides
## 0 (2-4,2-6) legend.box.background
## grob
## 99_86c3b14d399915779b8ee8f468a98f91 gtable[layout]
## 99_e48d95bbf9bfaae50e1c3ba1663f2112 gtable[layout]
## zeroGrob[NULL]
print(g2(da))
## TableGrob (1 x 1) "layout": 1 grobs
## z cells name grob
## 1 14 (1-1,1-1) guide-box gtable[guide-box]
The function reposition_legend
assumes the method given in g1
, which is also given in g_legend
.
The above demonstration finds the panel named panel
. This is default. If using facetting, the panels are typically named panel-{column}-{row}
. We use gtable_show_names
to display the names of the facetted panels.
d2 <- d + facet_grid(.~cut)
gtable_show_names(d2)
Facetted panels’ names.
So to place the legend in a specific panel, give its name:
reposition_legend(d2, 'top left', panel = 'panel-3-1')
Placing the legend in a facet panel.
Likewise for facet_wrap
. Incidentally, empty panels are also named here:
reposition_legend(d + facet_wrap(~cut, ncol=3), 'top left', panel='panel-3-2')
Placing the legend in an empty panel when using facet_wrap
.
Modifying the legend is done via usual routines of ggplot2:
d3 <- d + facet_wrap(~cut, ncol=3) + scale_color_discrete(guide=guide_legend(ncol=3))
reposition_legend(d3, 'center', panel='panel-3-2')
The looks of the legend is modified with usual ggplot2 options.
Also supports spanning multiple panels:
d4 <- d + facet_wrap(~cut, ncol=4) + scale_color_discrete(guide=guide_legend(nrow=2))
reposition_legend(d4, 'center', panel=c('panel-2-2','panel-4-2'))
Supplying reposition_legend
with multple panel-names allows the legend to span them.
The panel names are not easy to figure, especially those from facet_wrap
. We refer to gtable_show_names
to get a look at where they are:
gtable_show_names(d4)
Use of gtable_show_names
to reveal the panels’ names.
g_legend
was proposed as early as June 2012 by Baptiste Auguié (http://baptiste.github.io/) on ggplot2’s wiki. It has since propogated throughout Stack Overflow answers.
grid_arrange_shared_legend
was originally proposed by Shaun Jackman original code which was further refined by baptiste
at ggplot2’s wiki. It has been further modified here.
reposition_legend
was coded by Stefan McKinnon Edwards/
Example with reposition_legend
that didn’t quite work:
dsamp <- diamonds[sample(nrow(diamonds), 1000), ]
d <- ggplot(dsamp, aes(carat, price)) +
geom_point(aes(colour = clarity))
reposition_legend(d + theme_gray(base_size=26), 'top left')
We have submitted a solution to gridExtra that should allow the use of the entire gtable in the margin arguments: https://github.com/baptiste/gridextra/issues/39↩