7

I have created a function for creating a barchart using ggplot.

In my figure I want to overlay the plot with white horizontal bars at the position of the tick marks like in the plot below

p <- ggplot(iris, aes(x = Species, y = Sepal.Width)) + 
geom_bar(stat = 'identity')
# By inspection I found the y-tick postions to be c(50,100,150)
p + geom_hline(aes(yintercept = seq(50,150,50)), colour = 'white')

irisplot

However, I would like to be able to change the data, so I can't use static positions for the lines like in the example. For example I might change Sepal.With to Sepal.Height in the example above.

Can you tell me how to:

  1. get the tick positions from my ggplot; or
  2. get the function that ggplot uses for tick positions so that I can use this to position my lines.

so I can do something like

tickpositions <- ggplot_tickpostion_fun(iris$Sepal.Width)
p + scale_y_continuous(breaks = tickpositions) +
geom_hline(aes(yintercept = tickpositions), colour = 'white')
jbaums
  • 27,115
  • 5
  • 79
  • 119
Gabra
  • 745
  • 8
  • 12

4 Answers4

11

A possible solution for (1) is to use ggplot_build to grab the content of the plot object. ggplot_build results in "[...] a panel object, which contain all information about [...] breaks".

ggplot_build(p)$layout$panel_ranges[[1]]$y.major_source
# [1]   0  50 100 150

See edit for pre-ggplot2 2.2.0 alternative.

Henrik
  • 65,555
  • 14
  • 143
  • 159
2

Check out ggplot2::ggplot_build - it can show you lots of details about the plot object. You have to give it a plot object as input. I usually like to str() the result of ggplot_build to see what all the different values it has are.

For example, I see that there is a panel --> ranges --> y.major_source vector that seems to be what you're looking for. So to complete your example:

p <- ggplot() +
    geom_bar(data = iris, aes(x = Species, y = Sepal.Width), stat = 'identity')
pb <- ggplot_build(p)
str(p)
y.ticks <- pb$panel$ranges[[1]]$y.major_source
p + geom_hline(aes(yintercept = y.ticks), colour = 'white')


Note that I moved the data argument from the main ggplot function to inside geom_bar, so that geom_line would not try to use the same dataset and throw errors when the number in iris is not a multiple of the number of lines we're drawing. Another option would be to pass a data = data.frame() argument to geom_line; I cannot comment on which one is a more correct solution, or if there's a nicer solution altogether. But the gist of my code still holds :)

DeanAttali
  • 25,268
  • 10
  • 92
  • 118
2

For ggplot 3.1.0 this worked for me:

ggplot_build(p)$layout$panel_params[[1]]$y.major_source
#[1]   0  50 100 150
Florian
  • 1,248
  • 7
  • 21
  • 4
    In ggplot2 v3.3.0, this worked for me (but this also returns NAs in the vector for me): `ggplot_build(p)$layout$panel_params[[1]]$y$breaks` – SHKT May 04 '20 at 17:50
  • With ggplot2 v3.3.5 works too, but it returns one NA at the end (something that I don't understand) – emr2 Mar 22 '22 at 09:48
-3

for sure you can. Read the help file for the seq() function.

seq(from = min(), to = max(), len = 5)

and do something like this.

p <- ggplot(iris, aes(x = Species, y = Sepal.Width)) + 
geom_bar(stat = 'identity')
p + geom_hline(aes(yintercept = seq(from = min(), to = max(), len = 5)), colour = 'white')
alias_paj
  • 148
  • 1
  • 9
  • 1
    Unfortunately, even if this code would run, the sequence wouldn't necessarily correspond to the position of ggplot's grid lines. – jbaums Dec 09 '14 at 08:51