Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot function to add text just below legend

Tags:

plot

r

ggplot2

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, while keeping legend on the right side).

myplot = ggplot(iris, aes(x=Sepal.Length, y=Sepal.Width, color=Species)) + 
           geom_line()

I want to add text "mean of Sepal.Width = 3.05" (and a box around it) just after the legend. I looked at related questions but they change the position of legend to bottom and do not work as a function rather prints the plot.

like image 741
d.putto Avatar asked Sep 10 '15 15:09

d.putto


People also ask

How do I add text to ggplot2?

You can use the annotate() function to add text to plots in ggplot2. where: x, y: The (x, y) coordinates where the text should be placed. label: The text to display.

How do you suppress a legend in ggplot2?

By specifying legend. position=”none” you're telling ggplot2 to remove all legends from the plot.

How do I change the legend text in Ggplot?

You can use the following syntax to change the legend labels in ggplot2: p + scale_fill_discrete(labels=c('label1', 'label2', 'label3', ...))


1 Answers

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)

enter image description here

like image 106
Sandy Muspratt Avatar answered Oct 11 '22 03:10

Sandy Muspratt