20

I have a ggplot2 graph which appears as follows:

ggplot2 graph without axis lines visible

Notice that the grid or axis lines do not show through the ribbons. One way of dealing with this is to alter the alpha property of the ribbons; however, this can make the lighter colours too light.

An alternative idea would be to draw the grid/axis lines on top of the ribbons rather than beneath them. How can I achieve this render ordering?

Naturally, the question could be asked of any plot generated by ggplot2. But a copy-and-paste command that illustrates the issue is as follows:

 ggplot(data.frame(x=sample(1:100),y=sample(1:100)),aes(x=x,y=y))+geom_point(size=20)
Richard
  • 56,349
  • 34
  • 180
  • 251
  • 1
    It's more likely that we will be able to help you if you make a minimal reproducible example to go along with your question. Something we can work from and use to show you how it might be possible to solve your problem. You can have a look at [this SO post](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) on how to make a great reproducible example in R. – Eric Fail Nov 30 '15 at 00:11
  • @EricFail: Since this would apply generally to _any_ picture made with ggplot, a MWE didn't seem necessary; however, I've added a WE which generates a suitable test case. – Richard Nov 30 '15 at 00:19
  • http://stackoverflow.com/questions/19054092/r-put-ggplot-grid-lines-in-foreground . Unless there has been a new development it appears the extra h/vline geoms are the way to go – user20650 Nov 30 '15 at 01:12
  • looks like it, anyone know how to get those automatically rather than manually entering them? – jeremycg Nov 30 '15 at 01:20
  • If p is your plot (without the h.vline), you can get the default x and y grid positions by looking at `ggplot_build(p)`. Although you may prefer to define the breaks (hence gridlines)manually using scale_x_continuous, and use these same values in your hline call? – user20650 Nov 30 '15 at 01:27
  • @jeremycg To automatically update `xintercept` and `yintercept` we can set these equal to axis-ticks vector as explained [here](http://stackoverflow.com/questions/31223818/accessing-vector-of-axis-ticks-for-an-existing-plot-in-ggplot2). – narendra-choudhary Nov 30 '15 at 03:36
  • Is this post resolved, maybe accept one of the answers? – zx8754 Feb 19 '18 at 11:56

3 Answers3

29

ggplot introduced an option in theme() that lets you do just that with pull number 993, on June 18, 2015.

Just add to your plot:

+ theme(
  panel.background = element_rect(fill = NA),
  panel.ontop = TRUE
)

There is an example in the ggplot docs.

zx8754
  • 52,746
  • 12
  • 114
  • 209
AF7
  • 3,160
  • 28
  • 63
  • 3
    Just a note that some themes (e.g., `theme_bw()` have a background color specified. So you may need to add another line to the call so that your background doesn't obscure your layers: `theme(panel.ontop = TRUE, panel.background = element_rect(color = NA, fill = NA))` – Nova Dec 07 '18 at 15:47
8

You could use grid package functionality to extract the grid lines from the plot, and then redraw them, which would avoid some of the manual specification when adding horizontal or vertical lines.

library(ggplot2)
library(grid)

# Draw your plot
ggplot(data.frame(x=sample(1:100),y=sample(1:100)), aes(x=x,y=y))+
   geom_point(size=20)

# This extracts the panel including major and minor gridlines
lines <- grid.get("grill.gTree", grep=TRUE)

# Redraw plot without the gridlines
# This is done, as otherwise when the lines are added again they look thicker
last_plot() + 
  theme(panel.grid.minor = element_blank(),
        panel.grid.major = element_blank())

# Navigate to relevant viewport
# To see these use grid.ls(viewports=TRUE)
seekViewport("panel.3-4-3-4")

# Redraw lines
grid.draw(lines$children[-1])

Which produces

enter image description here

Alternatively, if you wanted to automate the adding of the vertical and horizontal lines within ggplot (as in Narendra's answer), but without specifying the breaks manually, you can access their positions using ggplot_build(p), where p is your plot.


It may be worth showing this for a graph with facets. Same procedure, except you select multiple lines and panels, and then just loop through them to draw.

# New plot with facets
ggplot(mtcars, aes(mpg, wt)) + geom_point(size=10) + facet_grid(am~cyl)

gr <- grid.ls(print=FALSE)
# Get the gTree for each of the panels, as before    
lines <- lapply(gr$name[grep("grill.gTree", gr$name)], grid.get)

last_plot() + 
  theme(panel.grid.minor = element_blank(),
        panel.grid.major = element_blank())

# Get the names from each of the panels
panels <- gr$name[grep("panel.\\d", gr$name)]

# Loop through the panels redrawing the gridlines
for(i in 1:length(panels)) {
             seekViewport(panels[i])
             grid.draw(lines[[i]]$children[-1])
             }

This will also work for the plots without facts.

user20650
  • 24,654
  • 5
  • 56
  • 91
7

Here's a workaround using geom_hline and geom_vline.

f <- ggplot(mpg, aes(cty, hwy))
f + geom_smooth(color="red")

It generates this plot.

enter image description here

To add horizontal and vertical lines manually:

f + geom_smooth(color="red") 
  + geom_vline(xintercept = c(10,15,20,25,30,35), color="white", size=1.25) 
  + geom_hline(yintercept = c(20,30,40), color="white", size=1.25)

To automatically add xintercept and yintercept:

f <- ggplot(mpg, aes(cty, hwy)) + geom_smooth(color="red")
x_intercept <- ggplot_build(f)$panel$ranges[[1]]$x.major_source
## x_intercept
## [1] 10 15 20 25 30 35
y_intercept <- ggplot_build(f)$panel$ranges[[1]]$y.major_source
## y_intercept
## [1] 20 30 40
f + geom_vline(xintercept=x_intercept, color="white", size=1.25)
  + geom_hline(yintercept=y_intercept, color="white", size=1.25)

Now any changes in axis-ticks introduced by scale-* functions will reflect in the final plot.
Here we have horizontal and vertical lines (similar to grid) on top of the plot. You can vary size to make lines more thick. enter image description here

But it's just a workaround. Given the flexibility of ggplot2 package, I think something similar can be achieved using theme. But I do not know how.

Edit1 : We can try following but it won't put grids on top. This way we can change size, color, linetype but that's all.

f + geom_smooth(color="red") 
  + theme(panel.grid.major=element_line(color="white", size=2))

Edit2 : Added automatically insertion of xintercept and yintercept using ggplot_build(f) as explained here.

Community
  • 1
  • 1
narendra-choudhary
  • 4,582
  • 4
  • 38
  • 58
  • Thanks, this helped a lot! But it seems that these days you have to use `ggplot_build(f)$layout$panel_ranges[[1]]$x.major_source` – nadieh Jul 19 '17 at 07:50
  • @nadieh you do not need this, see my answer – AF7 Feb 19 '18 at 11:23