ggplot2 - annotate outside of plot

Solution 1:

This is now straightforward with ggplot2 3.0.0, since now clipping can be disabled in plots by using the clip = 'off' argument in coordinate functions such as coord_cartesian(clip = 'off') or coord_fixed(clip = 'off'). Here's an example below.

    # Generate data
    df <- data.frame(y=c("cat1","cat2","cat3"),
                     x=c(12,10,14),
                     n=c(5,15,20))

    # Create the plot
    ggplot(df,aes(x=x,y=y,label=n)) +
      geom_point()+
      geom_text(x = 14.25, # Set the position of the text to always be at '14.25'
                hjust = 0,
                size = 8) +
      coord_cartesian(xlim = c(10, 14), # This focuses the x-axis on the range of interest
                      clip = 'off') +   # This keeps the labels from disappearing
      theme(plot.margin = unit(c(1,3,1,1), "lines")) # This widens the right margin

enter image description here

Solution 2:

You don't need to be drawing a second plot. You can use annotation_custom to position grobs anywhere inside or outside the plotting area. The positioning of the grobs is in terms of the data coordinates. Assuming that "5", "10", "15" align with "cat1", "cat2", "cat3", the vertical positioning of the textGrobs is taken care of - the y-coordinates of your three textGrobs are given by the y-coordinates of the three data points. By default, ggplot2 clips grobs to the plotting area but the clipping can be overridden. The relevant margin needs to be widened to make room for the grob. The following (using ggplot2 0.9.2) gives a plot similar to your second plot:

library (ggplot2)
library(grid)

df=data.frame(y=c("cat1","cat2","cat3"),x=c(12,10,14),n=c(5,15,20))

p <- ggplot(df, aes(x,y)) + geom_point() +            # Base plot
     theme(plot.margin = unit(c(1,3,1,1), "lines"))   # Make room for the grob

for (i in 1:length(df$n))  {
p <- p + annotation_custom(
      grob = textGrob(label = df$n[i], hjust = 0, gp = gpar(cex = 1.5)),
      ymin = df$y[i],      # Vertical position of the textGrob
      ymax = df$y[i],
      xmin = 14.3,         # Note: The grobs are positioned outside the plot area
      xmax = 14.3)
 }    

# Code to override clipping
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)

enter image description here

Solution 3:

Simplier solution based on grid

require(grid)

df = data.frame(y = c("cat1", "cat2", "cat3"), x = c(12, 10, 14), n = c(5, 15, 20))

p <- ggplot(df, aes(x, y)) + geom_point() + # Base plot
theme(plot.margin = unit(c(1, 3, 1, 1), "lines"))

p

grid.text("20", x = unit(0.91, "npc"), y = unit(0.80, "npc"))
grid.text("15", x = unit(0.91, "npc"), y = unit(0.56, "npc"))
grid.text("5", x = unit(0.91, "npc"), y = unit(0.31, "npc"))