How to change color of facet borders when using facet_grid
Asked Answered
E

3

24

When using facet_grid, ggplot2 divides the major categories that make up the facet variables with a broader-than-usual white line. This serves most purposes well. Sometimes I want to more clearly show the divisions between these major categorizations and would like to shade the facet division with another color. Is there a way to do that? Thanks.

Eminence answered 21/2, 2015 at 22:45 Comment(0)
R
32

Although a year late, I found this to be an easy fix:

ggplot(mpg, aes(cty, hwy, color = factor(year)))+ 
  geom_point()+
  facet_grid(cyl ~ drv) +
  theme(panel.margin=unit(.05, "lines"),
        panel.border = element_rect(color = "black", fill = NA, size = 1), 
        strip.background = element_rect(color = "black", size = 1))

UPDATE 2021-06-01


As of ggplot2 3.3.3, the property panel.margin is deprecated, and we should use panel.spacing instead. Therefore, the code should be:

ggplot(mpg, aes(cty, hwy, color = factor(year)))+ 
  geom_point()+
  facet_grid(cyl ~ drv) +
  theme(panel.spacing = unit(.05, "lines"),
        panel.border = element_rect(color = "black", fill = NA, size = 1), 
        strip.background = element_rect(color = "black", size = 1))

facet wrap example

Rubel answered 17/2, 2016 at 16:49 Comment(5)
This looks like a simple solution, but I don't think it can generalise to all situations. What happens if one wants more space between the panels? Or even the default panel margin? The panel boundary lines' size needs to be increased. But then the thicker boundary lines are drawn inside and outside the panel. Not only does the plot look ugly, one eventually loses information; for instance, with a thick enough line, the top right points in the middle panel in the top row disappear under the boundary line.Baskin
Or, one wants a colour other than black?Baskin
ok, there are several things you can do by adjusting variables in the theme(): * Change color in element_rect(color = "black"...) for both panel.border and strip.background * Increase the spacing by increasing panel.margin=unit(.05, "lines") To deal with the problem of thick lines covering points, you can change the x & y limits of the plotsRubel
Change the colour to red and all boundary lines are red, not just the spaces between the panels.Baskin
Or, change the facet margin to 0.5 lines. Now try to fill the facet divisions with a colour.Baskin
B
2

You might need to use ggplot's layout table and gtable functions.

library(ggplot2)
library(gtable)
library(grid)

p <- ggplot(mtcars, aes(mpg, wt)) + geom_point() + 
     facet_grid(am ~ cyl)
## Get the plot grob
gt <- ggplotGrob(p)

## Check the layout
gtable_show_layout(gt)   # Vertical gaps are in columns 5 and 7
                         # and span rows 4 to 9
                         # Horizontal gap is in row 8
                         # and spans columns 4 to 9


## To automate the selection of the relevant rows and columns:
## Find the panels in the layout
## (t, l, b, r refer to top, left, bottom, right);
## The gaps' indices are one to the right of the panels' r index (except the right most panel);
## and one below the panels' b index (except the bottom most panel);
## Rmin and Rmax give the span of the horizontal gap;
## Bmin and Bmax give the span of the vertical gap;
## cols and rows are the columns and row indices of the gaps.

panels = subset(gt$layout, grepl("panel", gt$layout$name), t:r)

# The span of the horizontal gap
Rmin = min(panels$r)
Rmax = max(panels$r) + 1

#The span of the vertical gap
Bmin = min(panels$t) - 1
Bmax = max(panels$t)

# The columns and rows of the gaps
cols = unique(panels$r)[-length(unique(panels$r))] + 1
rows = unique(panels$t)[-length(unique(panels$t))] + 1

# The grob - orange rectangle
g = rectGrob(gp = gpar(col = NA, fill = "orange"))

## Add orange rectangles into the vertical and horizontal gaps
gt <- gtable_add_grob(gt, 
      rep(list(g), length(cols)),
      t=Bmin, l=cols, b=Bmax)

gt <- gtable_add_grob(gt, 
      rep(list(g), length(rows)),
      t=rows, l=Rmin, r=Rmax)

## Draw it
grid.newpage()
grid.draw(gt)

enter image description here

Baskin answered 23/2, 2015 at 8:51 Comment(8)
This is really excellent. I wonder if this approach will allow modification of a ggplot object that will result in another ggplot object that can have ggplot layers added in a later step. On an unrelated note I have I had trouble added footnotes in ggplot objects and keeping themas ggplot class. I've tried all the approaches listed elsewhere on stackoverflow. Might gtable be an alternative approach?Eminence
I think the answer to your questions would be "yes". gtable allows some elaborate modifications to initial ggplots. For instance, one can add rows and columns to the ggplot layout, then insert grobs into them. @baptiste has put together some notes at https://github.com/baptiste/gtable/wiki/DescriptionBaskin
In general I'll have plots with different numbers of facet rows/columns. Can you think of a data-sensitive way to specify Rmin, and Bmin?Eminence
Perhaps all facet layouts use the same setup so no other parameters are needed? And is there a way to result in another ggplot object instead of having to directly print?Eminence
I've tried your code on a number of examples and they all worked perfectly. Thank you very much. I've added your code as a new function called colorFacet that will be in the next release of the R Hmisc package with you listed as the author.Eminence
Thank you very much. As far as I know, the layout is standard. But Rmin=panelsR[1], and Bmin=panelsB[1]-1. I'm not sure I understand the next question.Baskin
That helps clarify. The other question was about whether there is a way to get a ggplot or grid result without having to just print the resulting object. Some users may want to change the graphics object or add a layer before ultimate printing.Eminence
@FrankHarrell, This post suggests "possibly yes" but not easily.Baskin
S
0

Try this--you can control it with strip.background, formated with an element_rect call.

qplot(hwy, cty, data = mpg) +
  facet_grid(. ~ manufacturer) +   
  theme(strip.text.x = element_text(size = 8,
                                    colour = "red", 
                                    angle = 90),
        strip.background = element_rect(fill = "darkblue",
                                        colour = NA)
  )
Shack answered 21/2, 2015 at 23:37 Comment(1)
That nicely colors the rectangle backgrounds where facet labels appear but does not change the gaps between categories - the white lines that run the length of the plot separating the facet groups.Eminence

© 2022 - 2024 — McMap. All rights reserved.