In R I want to make a function which takes an ggplot object and some text and returns and ggplot object by adds text just below the legend (in the right side of the plot, w
A couple of possibilities.
The first uses annotate()
, and involves positioning the text by trial and error. The x position is adjusted using hjust
, the y position is selected to be a little below the legend. Note: no border around the text.
The second assumes a border is required. The combined text and box is constructed using grid
. Then the grob is positioned using annotation_custom()
. ymin
and ymax
are set to be a little below the legend. xmin
and xmax
are set by trial and error to get the text and the legend to align.
Both methods involve plotting outside the plot panel, so clipping to the plot panel needs to be turned off. But if the text size or length changes, the position of the label needs to be adjusted.
The third method is reasonably robust to changes to text length and size. Similar to method 2, the combined text and box grob is constructed using grid
. Then, using gtable
functions, the grob is attached to the legend (to the middle column of the legend).
# 1.
library(ggplot2)
library(grid)
library(gtable)
# The label
label = "Mean of Sepal.Width = 3.05"
# The plot - Note the extra margin space for the label
myplot = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Species)) +
geom_line() +
annotate("text", x = Inf, y = 2.9, label = label, hjust = -0.08, size = 3) +
theme(plot.margin = unit(c(.5,6,.5,.5),"lines"),
legend.background = element_rect(colour = "black"))
# Turn off clipping to the plot panel
g = ggplotGrob(myplot)
g$layout$clip[g$layout$name == "panel"] = "off"
grid.draw(g)
# 2.
# Construct the label grob - a combination of text and box
textgrob = textGrob(label, gp = gpar(cex = .75), )
width = unit(1, "grobwidth",textgrob) + unit(10, "points")
height = unit(1, "grobheight", textgrob)+ unit(10, "points")
rectgrob = rectGrob(gp=gpar(colour = "black", fill = NA), height = height, width = width)
labelGrob = gTree("labelGrob", children = gList(rectgrob, textgrob))
# The plot - Note the extra margin space for the label
myplot = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Species)) +
geom_line() +
annotation_custom(labelGrob,
xmin = 1.137*max(iris$Sepal.Length), xmax = 1.137*max(iris$Sepal.Length),
ymin = 2.9, ymax = 2.9) +
theme(plot.margin = unit(c(0.5, 6, 0.5, 0.5), "lines"),
legend.background = element_rect(colour = "black"))
# Turn off clipping to the plot panel
g = ggplotGrob(myplot)
g$layout$clip[g$layout$name == "panel"] = "off"
grid.draw(g)
#3.
# The label
label = "Mean of\nSepal.Width = 3.05"
# Try a different label
# label = "a"
# The plot
myplot = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Species)) +
geom_line() +
theme(legend.background = element_rect(colour = "black"))
# Get the legend
g = ggplotGrob(myplot)
leg = g$grobs[[which(g$layout$name == "guide-box")]]
# Construct the label grob
xpos = 5
textgrob = textGrob(x = unit(xpos, "points"), label, gp = gpar(cex = .75), just = "left")
width = unit(1, "grobwidth",textgrob) + unit(2*xpos, "points") # twice the x position
height = unit(1, "grobheight", textgrob)+ unit(2*xpos, "points")
rectgrob = rectGrob(x = unit(0, "points"), just = "left",
gp = gpar(colour = "black", fill = NA), height = height, width = width)
labelGrob = gTree("labelGrob", children = gList(rectgrob, textgrob))
# Add the label grob to a new row added to the legend
pos = subset(leg$layout, grepl("guides", name), t:r)
leg = gtable_add_rows(leg, height, pos = pos$t+1)
leg = gtable_add_grob(leg, labelGrob, t = pos$t+2, l = pos$l)
# Adjust the middle width of the legend to be the maximum of the original width
# or the width of the grob
leg$widths[pos$l] = max(width, leg$widths[pos$l])
# Add some space between the two parts of the legend
leg$heights[pos$t+1] = unit(5, "pt")
# Return the modified legend to the origial plot
g$grobs[[which(g$layout$name == "guide-box")]] = leg
# Adjust the width of the column containing the legend to be the maximum
# of the original width or the width of the label
g$widths[g$layout[grepl("guide-box", g$layout$name), "l"]] = max(width, sum(leg$widths))
# Draw the plot
grid.newpage()
grid.draw(g)
Another easy possibility is to use a caption:
myplot = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Species)) +
geom_line() +
labs(caption = "Mean of Sepal.Width = 3.05")
It's not really right below the legend, though: