ggplot function to add text just below legend

后端 未结 2 1630
误落风尘
误落风尘 2021-01-17 21:03

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

相关标签:
2条回答
  • 2021-01-17 21:39

    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)
    

    0 讨论(0)
  • 2021-01-17 21:59

    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:

    0 讨论(0)
提交回复
热议问题