7
votes

I have a two facet plots created in ggplot2. I would like to add an arrow outside of the plot area. Multiple questions tried to address this: How to draw lines outside of plot area in ggplot2? Displaying text below the plot generated by ggplot2

but I can't make my example work. Also, I hope there is an easier way to accomplish this?

I tried to increase plot.margins and to use coord_cartesian(), but neither helped.

enter image description here

Instead, I want:

enter image description here

My dummy example:

# read library to assess free data
library(reshape2)
library(ggplot2)


ggplot(tips,
       aes(x=total_bill,
           y=tip/total_bill)) + 
  geom_point(shape=1) +
  facet_grid(. ~ sex) +
  # define the segment outside the plot
  geom_segment(aes(x = 10, 
                   y = -0.25, 
                   xend = 10, 
                   yend = 0),
               col = "red",
               arrow = arrow(length = unit(0.3, "cm"))) +
  theme_bw() +
  # limit the displayed plot extent
  coord_cartesian(ylim = c(0, 0.75)) +
  # increase plot margins - does not help
  theme(plot.margin = unit(c(1,1,1,0), "lines"))
2

2 Answers

9
votes

You can draw outside the panel using the argument clip="off" in coord_cartesian. I also use the expand argument of scale_y to start the y-axis at zero. There is a bit a manual selection over the y start position.

So your example

library(reshape2)
library(ggplot2)

ggplot(tips,
       aes(x=total_bill,
           y=tip/total_bill)) + 
  geom_point(shape=1) +
  facet_grid(. ~ sex) +
  annotate("segment", x=12, y=-0.05, xend=12, yend=0,
               col="red", arrow=arrow(length=unit(0.3, "cm"))) +
  scale_y_continuous(expand=c(0,0))+
  theme_bw() +
  coord_cartesian(ylim = c(0, 0.75), clip="off") +
  theme(plot.margin = unit(c(1,1,1,0), "lines"))

(I changed geom_segment to annotate as you were not mapping aesthetics)

Which produces

enter image description here

3
votes

This is a workaround which is not exactly super satisfying, because you will need to play around with the position of your plots. Principally it uses custom annotation, possible with cowplot

library(ggplot2) 
library(cowplot)
library(reshape2)

First define your plots, with the same coordinate limits.

p <- 
  ggplot(tips, aes(x = total_bill, y = tip/total_bill)) + 
  geom_point(shape = 1) +
  facet_grid(. ~ sex) +
  theme_bw() +
  coord_cartesian(ylim = c(0, 0.75), xlim = c(0, 50)) 

arrow_p <- 
  ggplot(tips) +
  geom_segment(aes(x = 1, y = -1, xend = 1, yend = 0),
               col = "red",
               arrow = arrow(length = unit(0.3, "cm"))) +
  coord_cartesian(ylim = c(0, 0.75), xlim = c(0,50)) +
  theme_void()

arrow_p is created on a void background for use as overlay. You can now position it where you want with:

 ggdraw() +
    draw_plot(p) +
    draw_plot(arrow_p, x = 0.10, y = 0.05) +
    draw_plot(arrow_p, x = 0.57, y = 0.05)

enter image description here

Unfortunately, you have to trial-and-error here. You can also change specify the width/height/scale parameters of draw_plot(). There might be a better way, but this is a fairly straight forward workaround...