7


I'm about to plot odds ratios with R/ggplot2 and I want to add two arrows underneath or next to the X-axis label. One pointing to the left, one pointing to the right, showing de-/increasing influence. I've tried lots of things, like geom_path, geom_line, without great success. This is my code:

forest <- function(d, xlab="Odds Ratio", ylab="Influencing variables"){
require(ggplot2)
p <- ggplot(d, aes(x=ordered(x, levels=rev(x)), y=y, ymin=ylo, ymax=yhi)) +
geom_pointrange() +
geom_segment(aes(x = 0, xend = 0, y= 1, yend= 2)) +
coord_flip() +
geom_hline(aes(yintercept=1), lty=2) +
ylab(xlab) +
xlab(ylab) +
scale_y_log10() 
return(p)
}
##Test Data
data <- data.frame( x   = c("A","B","C","D","E","F","G","H","I"),
                y   = c(1.782,0.136,0.978,0.645,0.518,1.474,0.855,0.673,0.369))
data <- transform(data, ylo = (0.719,0.046,0.945,0.295,0.188,0.577,0.407,0.310,0.145), 
        yhi = c(4.420,0.398,1.012,1.411,1.424,3.768,1.798,1.460,0.940))
forest(data)

Adding a line like geom_line(aes(x=1), arrow=arrow(length=unit(0.15,"cm")), colour="black", size=1) brings some arrows but they collide with my original data.
Thanks in advance for your solution, help or hint!
Marc

Machavity
  • 30,841
  • 27
  • 92
  • 100
Marc
  • 71
  • 1
  • 1
  • 2

3 Answers3

18

This can easily be done using mathematical annotation in R expression(x <-> y). A table of available options can be found here. expression also allows latex like math symbols.

p1 <- qplot(x = 1:10, y = 1:10, geom = "blank") 
p1 <- p1 + xlab(expression(decreasing %<->% increasing))
p1 <- p1 + ylab(expression(down %->% up))
p1 <- p1 + ggtitle(expression("Darcy's law" %->% q == -k*frac(Delta*h,Delta*l)))
p1 <- p1 + theme_bw(base_size=14)

Including arrows in plot labels

Scott Worland
  • 1,352
  • 1
  • 12
  • 15
  • Link is broken, [here](https://stat.ethz.ch/R-manual/R-devel/library/grDevices/html/plotmath.html) is a working version (or google "Mathematical Annotation in R") – MokeEire Nov 10 '21 at 22:46
  • You can also just input `demo(plotmath)` into the R console to see all the available options. – coip Jul 04 '22 at 16:59
15

This is a pretty old post, but thought I would share another option for anyone who stumbles upon this.

I have a situation which really requires that I have arrows outside of the plotting region (and don't care to involve grid). As shown below, using expressions and symbols might be a good option for some:

p1 <- qplot(x = 0:12, y = 0:12, geom = "blank") 
p1 <- p1 + ylab(expression(symbol('\256')))
p1 <- p1 + xlab(expression(symbol('\256')))
p1 <- p1 + theme(axis.title.y = element_text(size = 30))
p1 <- p1 + theme(axis.title.x = element_text(size = 30))

p1  

enter image description here

The theme modifications aren't absolutely necessary but you will probably want to increase the size of the arrows at least a bit.

By default, the symbol() function takes Adobe Symbol codes which are outline in this document.

If you want to add some text to the labels (just adding to the expression would probably make it too big), the following should get you started:

grid.text(label="X", x = 0.645, y = 0.02)
grid.text(label="Y", x = 0.1, y = 0.5, rot=90)
grid.gedit("GRID.text", gp=gpar(fontsize=15))
joemienko
  • 2,220
  • 18
  • 27
3

I'm doing a bit of guessing about what you want, exactly, as I'm not sure what you were attempting with geom_segment, whether that was part of your data, or an attempt to create an arrow. But you can build on geom_segment to get something like what you describe:

#Your test data, with a small typo repaired
dd <- data.frame(x = c("A","B","C","D","E","F","G","H","I"),
                 y = c(1.782,0.136,0.978,0.645,0.518,1.474,0.855,0.673,0.369))
dd <- transform(dd, ylo = c(0.719,0.046,0.945,0.295,0.188,0.577,0.407,0.310,0.145), 
        yhi = c(4.420,0.398,1.012,1.411,1.424,3.768,1.798,1.460,0.940))

#Create a separate data frame for the arrow labels
arrowLab <- data.frame(lab = c("Increasing","Decreasing"),
                       x = c(0.15,0.15),y = c(10^0.25,10^(-0.25)))

ggplot(data = dd, aes(x=ordered(x, levels=rev(x)), y=y)) +
        geom_pointrange(aes(ymin=ylo, ymax=yhi)) +
        geom_segment(aes(x = 0, xend = 0, y= 1, yend= 2),
                         arrow=arrow(length=unit(0.2,"cm"))) +
        geom_segment(aes(x = 0, xend = 0, y= 1, yend= 10^(-0.25)),
                         arrow=arrow(length=unit(0.2,"cm"))) +
        geom_text(data = arrowLab,aes(x=x,y=y,label = lab),size = 3) +
        coord_flip() +
        geom_hline(aes(yintercept=1), lty=2) +
        scale_y_log10() 

enter image description here

Note that I hard-coded much of the positioning, so you'll likely want to tinker with those values to get what you want, or you could try to choose them programmatically based on your data.

joran
  • 169,992
  • 32
  • 429
  • 468
  • Thanks for your help! That gets me a big step closer to what I need. My intention was to plot these two arrows under the "y". But maybe this is enough. – Marc Aug 24 '11 at 08:15
  • @Marc - Drawing stuff outside the plotting region is technically possible, but complicated, requiring that you mess around with the `grid` package and viewports. I often tell people that for unusual custom annotations like this its often faster/easier to just modify the image after the fact in an image editor like Illustrator, or something. – joran Aug 24 '11 at 14:13