6

Suppose I have the following chart

dat <- data.frame(x = 1:10, y = 1:10)

ggplot(dat, aes(x=x, y=y)) + geom_point()

but I'm not actually interested in the values 2.5, 5, 7.5, 10 on either axis. All I want to mark is something like "Low to High". I know I can mark + xlab("Low to High") but I would prefer "Low" to be at the far left of the axis (under the origin) and "High" to be at the far right (under 10.0) with perhaps an arrow from Low ---> High. I could specify the breaks manually, but that seems too much of a kludge.

Hugh
  • 15,521
  • 12
  • 57
  • 100

2 Answers2

10

Something like this might help,

dat <- data.frame(x = 1:10, y = 1:10)

p <- ggplot(dat, aes(x=x, y=y)) + geom_point() +
  scale_x_continuous('', breaks=NULL)+
  scale_y_continuous('', breaks=NULL)

g <- ggplotGrob(p)
library(gtable)
library(grid)

my_axis <- function(low="low", high="high", axis=c("x", "y"), ...){

  axis <- match.arg(axis)

  if(axis == "x"){
  g1 <- textGrob(low, x=unit(0,"npc"), hjust=0)
  g3 <- textGrob(high, x=unit(1,"npc"), hjust=1)
  g2 <- segmentsGrob(grobWidth(g1) + unit(2,"mm"), unit(0.5,"npc"),
               unit(1,"npc") - grobWidth(g3)- unit(2,"mm"), 
               unit(0.5,"npc"), ...)

  } else if(axis == "y"){
    g1 <- textGrob(low, y=unit(0,"npc"), rot=90, hjust=0)
    g3 <- textGrob(high, y=unit(1,"npc"), rot=90, hjust=1)
    g2 <- segmentsGrob(unit(0.5,"npc"),grobHeight(g1) + unit(2,"mm"), 
                       unit(0.5,"npc"),
                       unit(1,"npc") - grobHeight(g3)- unit(2,"mm"), 
                       ...)

  }

  grobTree(g1,g2,g3)
}

g <- gtable_add_grob(g, my_axis(arrow=arrow(length=unit(2,"mm"))), 
                     t=nrow(g)-2, b=nrow(g)-1, l=4)
g <- gtable_add_grob(g, my_axis(axis="y", arrow=arrow(length=unit(2,"mm"))), 
                     t=3, l=1,r=3)
grid.newpage()
grid.draw(g)

enter image description here

baptiste
  • 75,767
  • 19
  • 198
  • 294
  • +1, but I have to manually specify the horizontal position in `textGrob`. I'd prefer a solution where a change in the values `dat` didn't require other changes. – Hugh Oct 08 '14 at 23:34
  • those positions are actually in "npc" units, aka "normalised parent coordinates", aka what you want, I think. – baptiste Oct 08 '14 at 23:35
  • @baptiste : This appears to be broken. – PatrickT Jan 12 '19 at 19:33
0

As an alternative, and since you did ask for "Low ---> High", here's an option that doesn't involve disassembling the plot

dat <- data.frame(x = 1:10, y = 1:10)
low_to_high <- paste0("low   ", paste0(rep("-", 35), collapse = ""), ">   high")

library(ggplot2)
ggplot(dat, aes(x, y)) + 
  geom_point() +
  labs(x = low_to_high, y = low_to_high) +
  coord_equal() +
  theme_bw() +
  theme(axis.title = element_text(size=20), 
        axis.text = element_blank(),
        axis.ticks = element_blank())

enter image description here

Admittedly, the number of - required will vary and needs to be adjusted per your plot size, and it's not as pretty as the properly rendered arrow. Still, this is quick to make from scratch.

Jonathan Carroll
  • 3,897
  • 14
  • 34