How do you add a general label to facets in ggplot2?
Asked Answered
H

6

103

I often have numeric values for faceting. I wish to provide sufficient information to interpret these faceting values in a supplemental title, similar to the axis titles. The labeller options repeat much unnecessary text and are unusable for longer variable titles.

Any suggestions?

The default:

test<-data.frame(x=1:20, y=21:40, facet.a=rep(c(1,2),10), facet.b=rep(c(1,2), each=20))
qplot(data=test, x=x, y=y, facets=facet.b~facet.a)

enter image description here

What I would love:

enter image description here

The best I can do in ggplot:

qplot(data=test, x=x, y=y)+facet_grid(facet.b~facet.a, labeller=label_both)

enter image description here

As indicated by @Hendy, similar to: add a secondary y axis to ggplot2 plots - make it perfect

Horizontal answered 5/7, 2012 at 22:15 Comment(8)
Holy cow. I was just looking for this, found this via google... and now see it was asked a minute ago. This question seems to be asking the same thing based on his comment, despite the title that might imply otherwise. Great mockups to illustrate. This is exactly my problem -- it'd be nice if I didn't have to explain my numerical facet categories. The graph should speak for itself with a simple label explaining what the faceting variables were.Tool
I asked a similar question in the past.Livery
I inquired with Winston Chang (don't know his SO handle), one of the main ggplot developers, via email. He doesn't think this is currently an option but might consider adding it. He suggested I add an issue on github, so I didTool
Looks like this came up on the mailing list and Winston let me know. He created a branch with a general implementation of this for top labels. See the thread here. @hadley: you are correct. I have no idea how hard it is to do in general. If this never happens... my gratitude for ggplot still prevails :)Tool
At some point I stop trying to wrangle R/ggplot2 and make some desired modifications using an image editor.Scholium
I second what Andy said. Save the plot in SVG format, and pop the result into a vector editor, such as Inkscape. You can edit the plot perfectly. Is this an option in your case?Loculus
any update on best practice here?Brand
I know this is old, but for those still looking for an answer; have a look at the answer I gave here: #40316669Thibodeaux
S
51

As the latest ggplot2 uses gtable internally, it is quite easy to modify a figure:

library(ggplot2)
test <- data.frame(x=1:20, y=21:40, 
                   facet.a=rep(c(1,2),10), 
                   facet.b=rep(c(1,2), each=20))
p <- qplot(data=test, x=x, y=y, facets=facet.b~facet.a)

# get gtable object
z <- ggplotGrob(p)

library(grid)
library(gtable)
# add label for right strip
z <- gtable_add_cols(z, unit(z$widths[[7]], 'cm'), 7)
z <- gtable_add_grob(z, 
                     list(rectGrob(gp = gpar(col = NA, fill = gray(0.5))),
                          textGrob("Variable 1", rot = -90, gp = gpar(col = gray(1)))),
                     4, 8, 6, name = paste(runif(2)))

# add label for top strip
z <- gtable_add_rows(z, unit(z$heights[[3]], 'cm'), 2)
z <- gtable_add_grob(z, 
                     list(rectGrob(gp = gpar(col = NA, fill = gray(0.5))),
                          textGrob("Variable 2", gp = gpar(col = gray(1)))),
                     3, 4, 3, 6, name = paste(runif(2)))

# add margins
z <- gtable_add_cols(z, unit(1/8, "line"), 7)
z <- gtable_add_rows(z, unit(1/8, "line"), 3)

# draw it
grid.newpage()
grid.draw(z)

enter image description here

Of course, you can write a function that automatically add the strip labels. A future version of ggplot2 may have this functionality; not sure though.

Sword answered 30/9, 2012 at 9:6 Comment(8)
-- I am wondering if this is still the cleanest approach, as of version 0.9.3.1. I don't see something that looks like "this fuctionality" in latest documentation docs.ggplot2.org/0.9.3.1Bascio
Version 0.9.3.1 provides ggplotGrob() that is same as ggplot_gtable(ggplot_build()). I think this is still the best way.Sword
This approach overwrites the legend when there is one. I wonder if there is a way to prevent this.Ecclesiolatry
If I am not mistaken, this solution is no more valid as some functions are now deprecated.Allayne
@Allayne try it nowScramble
Oh yes, I figured out but missed the opportunity to make the edit you did. I have used this "kind of code" in a number of presentation already. It is very handy. Thanks it works fine!Allayne
This is not working with ggplot2 3.2.1 and R 3.6.1. No error message but I don't get the labels on the resulting plot.Verbal
It would be really helpful to have more explanation for what the code is doing. For example, I have no idea what z$widths[[7]] is the width of. In my application, its value is 1null, and I have no idea how adding a column with a null width helps. Oddly, that is not what that gtable_add_cols seems to do, as it does add space to the right for some reason.Alisa
T
25

The secondary axis is now an option: https://ggplot2.tidyverse.org/reference/sec_axis.html

# Basic faceted plot
p <- ggplot(mtcars, aes(cyl, mpg)) +
  geom_point() +
  facet_grid(vs ~ am)
    
    # Create a simple secondary axis for the facets (use the appropriate scale_x function)
p + 
  scale_y_continuous(sec.axis = sec_axis(~ . , name = "SECOND Y AXIS", breaks = NULL, labels = NULL)) +
  scale_x_continuous(sec.axis = sec_axis(~ . , name = "SECOND X AXIS", breaks = NULL, labels = NULL))

Plot output

Teece answered 11/9, 2021 at 18:39 Comment(3)
This is awesome since there is no need for another package than ggplot2. Thanks!Pinder
MVP here giving an awesome answer 9 years after the original question was asked. thanks for it!!Mycosis
Seems like this will only work for continuous data thoughOverfill
D
5

There may be a better way to do it, but you can :

fac1 = factor(rep(c('a','b'),10))
fac2 = factor(rep(c('a','b'),10))
data = data.frame(x=1:10, y=1:10, fac1=fac1, fac2=fac2)
p = ggplot(data,aes(x,y)) + ggplot2::geom_point() + 
facet_grid(fac1~fac2)
p + theme(plot.margin = unit(c(1.5,1.5,0.2,0.2), "cm"))
grid::grid.text(unit(0.98,"npc"),0.5,label = 'label ar right', rot = 270) # right
grid::grid.text(unit(0.5,"npc"),unit(.98,'npc'),label = 'label at top', rot = 0)   # top
Den answered 29/12, 2017 at 15:36 Comment(0)
T
5

Apologies for replying to this decade old question, but wanted to put it out there that you can use ggh4x::facet_nested() to put spanning strips in a plot. Downside is that you'd need an extra package, upside is that you don't need to mess about with the gtables.

# install.packages("ggh4x")
library(ggplot2)

test<-data.frame(x=1:20, y=21:40, facet.a=rep(c(1,2),10), facet.b=rep(c(1,2), each=20))

ggplot(test, aes(x, y)) +
  geom_point() +
  ggh4x::facet_nested("Facet B" + facet.b ~ "Facet A" + facet.a)

Created on 2022-09-08 by the reprex package (v2.0.1)

Disclaimer: I wrote ggh4x.

Thibodeaux answered 8/9, 2022 at 19:38 Comment(9)
Hi. I am trying to use ggh4x but I get an error. There is no package called 'ggh4x'.Examen
@KevinSantos have you tried install.packages("ggh4x") first?Laudation
That was my error. But now, in every facet add all options. I mean, in facet A appears all options for A and B, in facet B appears all options for A and B.Examen
I'm not sure how this answer deviates from the desired solution. If your use-case is different than in the question above, feel free to post a new question.Thibodeaux
@teunbrand, thank you very much for writing such a useful package. I am just wondering the behavior in you answer is not documented, and would you like to add some docs to explain a little about it?Ulm
@LiangZhang what behaviour in particular couldn't you find back in the documentation?Thibodeaux
@Thibodeaux I meant that I didn't find documentation of the syntax "Facet B" + facet.b ~ "Facet A" + facet.a in facet_nested().Ulm
Ah right. That isn't syntax specific to facet_nested(), you can do the same with facet_grid() and facet_wrap(). As long as it is extending ggplot2's behaviour, I didn't feel I had to document this specifically.Thibodeaux
I understand it now. The + here is just add another column with given string in place for view.Ulm
O
1

In addition to the method outlined by kohske, you can add a border to the boxes added by changing

col=NA

to

col=gray(0.5), linetype=1

Also, change

fill=gray(0.5)

for

fill=grey(0.8)

and

gp=gpar(col=gray(1))

to

gp=gpar(col=gray(0))

If you want the new bars to match the facet labels

ie

z <- gtable_add_grob(z, 
      list(rectGrob(gp = gpar(col = gray(0.5), linetype=1, fill = gray(0.8))),
      textGrob("Variable 1", rot = -90, gp = gpar(col = gray(0)))),
      4, 8, 6, name = paste(runif(2)))
Orography answered 21/8, 2014 at 2:24 Comment(0)
O
1

My preferred solution is to use gridExtra. You can put in text or you can use calls to grid.text to get some formatting options.

library(ggplot2)
# Basic faceted plot

p <- ggplot(mtcars, aes(cyl, mpg)) +
  geom_point() +
  facet_grid(vs ~ am)

grid.arrange(p,top='Top Label', right='Right Label')

enter image description here

Overfill answered 5/10, 2022 at 0:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.