Search code examples
rggplot2annotations

How to annotate a plot outside the plot frame in ggplot?


I created a small data frame

x <- 1:10
y1 <- x
y2 <- x^2
y3 <- x + 1

df <- data.frame(x, y1, y2, y3)

We want to plot y against x and remove the default space on both axis, so I did it like this

ggplot() +
  geom_line(data = df, aes(x, y1), linetype = "solid", size = 1, show.legend = TRUE) +
  geom_line(data = df, aes(x, y2), linetype = "dashed", size = 1, show.legend = TRUE) +
  geom_line(data = df, aes(x, y3), linetype = "dotted", size = 1, show.legend = TRUE) +
  theme(panel.border = element_rect(colour = "black", fill=NA),
        panel.background = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank()) +
  scale_x_continuous(expand = c(0, 0), labels = label_number(accuracy = 1)) +
  scale_y_continuous(expand = c(0, 0)) +
  xlab("x-axis") +
  ylab("y-axis")

and the output is

enter image description here

How can I label each function at the end of each curve and outside the plot frame? So for example, I would like to show y2 at around x=10.5 and y=100.

I tried

annotate("text", x = max(df$x) + 0.5, y = max(df$y2), label = expression(y2), parse = TRUE)

but this doesn't show the text I think because of expand = c(0, 0).

Is there a way to keep expand = c(0, 0) and annotate the plot outside the frame?


Solution

  • Based on @YacineHajji’s comment suggesting coord_cartesian(clip = "off"). You also need to:

    • manually set the limits based on the ranges of x and y; otherwise, the limits will expand to include the annotations,
    • manually nudge overlapping annotations, and
    • adjust the margins to make room for the annotations.
    library(ggplot2)
    library(scales)
    
    ggplot() +
      geom_line(data = df, aes(x, y1), linetype = "solid", size = 1, show.legend = TRUE) +
      geom_line(data = df, aes(x, y2), linetype = "dashed", size = 1, show.legend = TRUE) +
      geom_line(data = df, aes(x, y3), linetype = "dotted", size = 1, show.legend = TRUE) +
      annotate("text", x = max(df$x) + 0.5, y = max(df$y1) - 2.5, label = expression(y1), parse = TRUE) +
      annotate("text", x = max(df$x) + 0.5, y = max(df$y2), label = expression(y2), parse = TRUE) +
      annotate("text", x = max(df$x) + 0.5, y = max(df$y3) + 2.5, label = expression(y3), parse = TRUE) +
      theme(panel.border = element_rect(colour = "black", fill=NA),
            panel.background = element_blank(),
            panel.grid.major = element_blank(),
            panel.grid.minor = element_blank(),
            plot.margin = margin(20, 40, 10, 10)) +
      scale_x_continuous(labels = label_number(accuracy = 1)) +
      coord_cartesian(xlim = range(df$x), 
                      ylim = c(min(c(df$y1, df$y2, df$y3)), max(c(df$y1, df$y2, df$y3))),
                      expand=FALSE, 
                      clip="off") +
      xlab("x-axis") +
      ylab("y-axis")