91

Is there a way to fill the strips of facets created with facet_wrap based on a variable supplied with the data frame?

Example data:

MYdata <- data.frame(fruit = rep(c("apple", "orange", "plum", "banana", "pear", "grape")), farm = rep(c(0,1,3,6,9,12), each=6), weight = rnorm(36, 10000, 2500), size=rep(c("small", "large")))

Example plot:

p1 = ggplot(data = MYdata, aes(x = farm, y = weight)) + geom_jitter(position = position_jitter(width = 0.3), aes(color = factor(farm)), size = 2.5, alpha = 1) + facet_wrap(~fruit)

I know how to change the background color of the strips (e.g. to orange):

p1 + theme(strip.background = element_rect(fill="orange"))

facet_wrap and orange strip color

Is there a way to pass on the values in the variable size in MYdata to the parameter fill in element_rect?

Basically, instead of 1 color for all strips I would like the strip background color of small fruits (apple, plum, pear) to be green and the background color of large fruits (orange, banana, grape) to be red.

5
  • 1
    There isn't an easy way to do this, it has been asked before on another site though. Commented Oct 18, 2013 at 9:20
  • Thanks, nograpes. I looked but couldn't find where it had been asked before. Commented Oct 21, 2013 at 22:01
  • If you get that code to work, you should post it as an answer. Commented Oct 22, 2013 at 1:46
  • I would love to know how to do that, it is a great idea Commented Feb 5, 2014 at 18:54
  • 6
    +1 for this feature. Would be nice to be able to specify within theme. Commented Oct 9, 2014 at 12:49

5 Answers 5

75

With a little bit of work, you can combine your plot with a dummy gtable that has the right grobs,

enter image description here

d <- data.frame(fruit = rep(c("apple", "orange", "plum", "banana", "pear", "grape")), 
                farm = rep(c(0,1,3,6,9,12), each=6), 
                weight = rnorm(36, 10000, 2500), 
                size=rep(c("small", "large")))

p1 = ggplot(data = d, aes(x = farm, y = weight)) + 
  geom_jitter(position = position_jitter(width = 0.3), 
              aes(color = factor(farm)), size = 2.5, alpha = 1) + 
  facet_wrap(~fruit)

dummy <- ggplot(data = d, aes(x = farm, y = weight))+ facet_wrap(~fruit) + 
  geom_rect(aes(fill=size), xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  theme_minimal()

library(gtable)

g1 <- ggplotGrob(p1)
g2 <- ggplotGrob(dummy)

gtable_select <- function (x, ...) 
{
  matches <- c(...)
  x$layout <- x$layout[matches, , drop = FALSE]
  x$grobs <- x$grobs[matches]
  x
}

panels <- grepl(pattern="panel", g2$layout$name)
strips <- grepl(pattern="strip_t", g2$layout$name)
g2$layout$t[panels] <- g2$layout$t[panels] - 1
g2$layout$b[panels] <- g2$layout$b[panels] - 1

new_strips <- gtable_select(g2, panels | strips)
grid.newpage()
grid.draw(new_strips)

gtable_stack <- function(g1, g2){
  g1$grobs <- c(g1$grobs, g2$grobs)
  g1$layout <- transform(g1$layout, z= z-max(z), name="g2")
  g1$layout <- rbind(g1$layout, g2$layout)
  g1
}
## ideally you'd remove the old strips, for now they're just covered
new_plot <- gtable_stack(g1, new_strips)
grid.newpage()
grid.draw(new_plot)
Sign up to request clarification or add additional context in comments.

9 Comments

This just [R]ocked my world! Been trying to do this for ages!
@duckertito You could have edited the post. I did it now. In the current version ggplot2_2.2.1 you have to grepl for "strip-t" instead of strip_t. Also changed this in my edit. Thanks for the nice solution btw!
Yup, works for me too (with @andrasz change). I wish I could put a legend for the strip color though. Any idea how to do that?
This is exactly what I need, Thanks. But, the facet labels do not show up when I run this code.
I'm also having trouble with the facet labels not showing up. Any solution found yet?
|
47

If you want to have different fills to the strip backgrounds, you can use facets in ggh4x to set a more complicated strip with strip_themed(). No hassle with gtables and your plot remains a ggplot, so you can add the usual layers/scales/theme options etc afterwards.

library(ggh4x)
#> Loading required package: ggplot2

# Only colour strips in x-direction
strip <- strip_themed(background_x = elem_list_rect(fill = rainbow(7)))

# Wrap variant
ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_wrap2(~ class, strip = strip)

It works for the grid layout too, but if you want to colour the vertical strips, you'd need to set the background_y argument in strip_themed() too.

ggplot(mpg, aes(displ, hwy)) +
  geom_point() +
  facet_grid2(year ~ cyl, strip = strip)

Created on 2023-01-04 by the reprex package (v2.0.1)

Disclaimer: I'm the author of ggh4x

9 Comments

Def, best solution! Very nice ggplot2 extension. It's so easy to use! This should be the answer now!
I agree. This is the ggplot2 extension is a great solution. It works for changing all elements of the strip text (e.g. font, face, size, color) and rectangle (fill, etc.) . It is intuitive too. I just wish the help pages integrated into R Studio a little better. (R studio, doesn't find the function names in the help tab even when the library is loaded). Other than that.. Perfect!
I don't see how this remotely answers the question, which is to facet the strip colours based on size of fruit.
I'm sorry that this answer wasn't satisfactory for your purposes. When you find a better method, feel free to post it as an answer.
The question was about making different facet_strip backgrounds and this answer provides a mechanism to do so. To use this answer for the original question, you would just have to sub out the fill = rainbow(7) argument appropriately. In this case you could easily manually do something like fill = c("blue", "red", "red", "red", "blue, "blue") to match the fruit size categories. This could also be automated.
|
5

You can find an updated answer to this question here.

g <- ggplot_gtable(ggplot_build(p))
stripr <- which(grepl('strip-r', g$layout$name))
fills <- c("red","green","blue","yellow")
k <- 1
for (i in stripr) {
  j <- which(grepl('rect', g$grobs[[i]]$grobs[[1]]$childrenOrder))
  g$grobs[[i]]$grobs[[1]]$children[[j]]$gp$fill <- fills[k]
  k <- k+1
}
grid::grid.draw(g)

enter image description here

3 Comments

Anyone have an idea on how to add a legend for the strip colours?
@MaxJ. If you're not using the "color" or "fill" aesthetic, you could probably create a dummy legend that matches the strip colors instead of the data. But that's another hassle on top of the hassle of modifying the gtable. Perhaps ggh4x offers an easier solution?
I was able to create a legend by creating a dummy plot and add it via cowplot but I do not know how to do it in ggplot directly.
0

I would love to know how to do that, it is a great idea. One idea is to generate each chart independently with a different color as you do and then use something like multiplot or viewports to show then side by side - it will require a bit more work.

if you want to extract the legend, which you will need for this approach - here is some code from Hadley that I found a while back

g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  return(legend)}

see how it is extracted it from chart p, and then I took it out of the plot legend <- g_legend(p) lwidth <- sum(legend$width) #if you want to define the viewport based on this p <- p + theme(legend.position="none")

then you eventually draw it

grid.newpage()
vp <- viewport(width = 1, height = 1)
#print(p, vp = vp)

submain <- viewport(width = 0.9, height = 0.9, x = 0.5, y = 1,just=c("center","top"))
print(p, vp = submain)
sublegend <- viewport(width = 0.5, height = 0.2, x = 0.5, y = 0.0,just=c("center","bottom"))
print(arrangeGrob(legend), vp = sublegend)

Good luck

Comments

-1

It's not directly for differently coloring your facets but here you have another (very quick and simpler) solution, based on facet by two variables (size ~ fruit) instead one (~ fruit):

ggplot(data = MYdata, aes(x = farm, y = weight)) + 
  geom_jitter(position = position_jitter(width = 0.3), 
      aes(color = factor(farm)), size = 2.5, alpha = 1) + 
  facet_wrap(size ~ fruit)

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.