Reputation: 614
I want to add two captions for the footer. But it seems that ggplot will only take 1. Is there a workaround to add an annotation or geom_text to the bottom left and right hand corners.
library(ggplot2)
p <- ggplot(mtcars, aes(x=wt, y = mpg)) +
geom_point()
p + labs(caption = "left footer") + theme(plot.caption=element_text(hjust=0))
p + labs(caption = "right footer") + theme(plot.caption=element_text(hjust=1))
p + labs(caption = "right footer") + theme(plot.caption=element_text(hjust=1)) +
labs(caption = "left footer") + theme(plot.caption=element_text(hjust=0))
Upvotes: 8
Views: 8126
Reputation: 11
Not sure if this was the case at the time of the question but, now, you can just put \n
in your caption:
(your_ggplot_object) + labs(caption = 'Line 1 \n Line 2')
Upvotes: 1
Reputation: 23767
This is essentially a "annotate outside the plot area" question and a very common problem when creating plots. Here a bunch of approaches - I am showing annotation as a caption left and right aligned, but most of the given solutions can be flexibly expanded for any desired annotation effect. I have not included the solution with \n
as I find it too imprecise and too much of a hack, but it can be found as a solution in this thread too.
This summarises solutions from:
library(ggplot2)
p <- ggplot()
## James Allison's solution from this thread here
## gives a warning which you can ignore.
## I'd say my preferred solution for short captions where the
## aim is to position them left and right.
## Does not need y limits
p + labs(caption = c("right footer", "left footer")) +
theme(plot.caption = element_text(hjust=c(1, 0)))
#> Warning: Vectorized input to `element_text()` is not officially supported.
#> Results may be unexpected or may change in future versions of ggplot2.
## You can use annotate(geom = "text") or geom_text
## This solution is very powerful and flexible and you will be getting very
## far with it. In fact you can answer a lot of problems on stackoverflow with
## this custom annotation
## if you use geom_text, make a data frame first
## Requires y limits relative to data
## I like to create the y semi-programmatically with a constant sort of expansion factor which needs to be hard coded
## I prefer to put hard coded values only in one place, therefore I define y outside, here
## as a function because I will need a slightly different value below
my_y <- function(fac){min(mtcars$disp)- fac*diff(range(mtcars$disp))}
textframe <- data.frame(x = c(-Inf, Inf), y = my_y(.3),
labels = c("right footer", "left footer"))
p +
## requires a y limit, therefore you need to add a y aesthetic
## (you will have this in most cases)
geom_point(data = mtcars, aes(mpg, disp)) +
geom_text(data = textframe, aes(x, y, label = labels), hjust = c(0,1)) +
## you need to manually specify the y limits
## here 0.05 because this is the default expansion
## you will also need to modify coordinate clipping
coord_cartesian(ylim = c(my_y(.05), NA), clip = 'off') +
## usually you need to add a plot margin, play around
theme(plot.margin = margin(b = 30, 5,5,5, unit = 'pt'))
## same effect with a call to annotate, also requires y limits
p + geom_point(data = mtcars, aes(mpg, disp)) +
annotate(geom = "text", x = c(-Inf, Inf), y = my_y(.3),
label = c("right footer", "left footer"),
## you need to add the hjust accordingly
hjust = c(0,1)) +
coord_cartesian(ylim = c(my_y(.05), NA), clip = 'off') +
theme(plot.margin = margin(b = 30, 5,5,5, unit = 'pt'))
## Baptiste's solution with gridExtra,
## an extremely powerful solution, but it requires some grid knowledge and you won't need it in most cases
## does not need y limits
library(gridExtra)
library(grid)
fthbar <- grobTree(rectGrob(gp=gpar(fill="grey50",col=NA)),
textGrob("right footer", x=0, hjust=0),
textGrob("left footer", x=1, hjust=1, gp=gpar(col="white")), cl="ann")
heightDetails.ann <- function(x) unit(1,"line")
grid.arrange(p, bottom = fthbar)
## other plot combining packages, e.g. patchwork or cowplot.
## I find patchwork super easy to use. You will need three plots
## requires y limits, but does not need to be relative to data
## here using the text frame from above
library(patchwork)
p2 <-
p +
## using geom_blank because I don't wanna drawn anything
geom_blank(data = mtcars, aes(mpg, disp)) +
geom_text(
data = textframe, aes(x , y, label = labels),
hjust = c(0, 1),
vjust = 0
) +
theme_void()
p/p2 + plot_layout(heights = c(1, 0.1))
Created on 2022-06-12 by the reprex package (v2.0.1)
Upvotes: 2
Reputation: 181
I'm very late to the party here but I also had the same issue.
A solution to your question would be to place both the captions at the same time and treat them as vectors:
p + labs(caption = c("right footer", "left footer")) +
theme(plot.caption = element_text(hjust=c(1, 0)))
Upvotes: 18