93

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

Community
  • 1
  • 1
Etienne Low-Décarie
  • 12,513
  • 15
  • 63
  • 87
  • 1
    Holy cow. I was *just* looking for this, found this via google... and now see it was asked a minute ago. [This question](http://stackoverflow.com/questions/9096671/add-a-secondary-y-axis-to-ggplot2-plots-make-it-perfect) 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. – Hendy Jul 05 '12 at 22:19
  • I asked a [similar question in the past.](http://stackoverflow.com/q/7603949/707145) – MYaseen208 Jul 05 '12 at 23:00
  • 3
    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 did](https://github.com/hadley/ggplot2/issues/612) – Hendy Jul 06 '12 at 16:37
  • 1
    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](https://groups.google.com/forum/?fromgroups#!topic/ggplot2-dev/_gpWsOWrSDk). @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 :) – Hendy Jul 09 '12 at 00:49
  • 1
    At some point I stop trying to wrangle R/ggplot2 and make some desired modifications using an image editor. – Andy Aug 27 '12 at 15:24
  • 1
    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? – Christian Sep 26 '12 at 23:30
  • 2
    any update on best practice here? – Arthur Yip Aug 08 '19 at 19:29
  • 1
    I know this is old, but for those still looking for an answer; have a look at the answer I gave here: https://stackoverflow.com/questions/40316169/nested-facets-in-ggplot2-spanning-groups/ – teunbrand Jun 26 '20 at 13:30

4 Answers4

50

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.

rawr
  • 19,873
  • 4
  • 42
  • 74
kohske
  • 63,256
  • 8
  • 159
  • 152
  • -- 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 http://docs.ggplot2.org/0.9.3.1/ – yosukesabai Jun 22 '13 at 15:35
  • 1
    Version 0.9.3.1 provides `ggplotGrob()` that is same as `ggplot_gtable(ggplot_build())`. I think this is still the best way. – kohske Jun 23 '13 at 04:56
  • This approach overwrites the legend when there is one. I wonder if there is a way to prevent this. – papirrin Feb 19 '14 at 10:43
  • 1
    If I am not mistaken, this solution is no more valid as some functions are now deprecated. – Remi.b Mar 01 '16 at 06:34
  • 1
    @Remi.b try it now – rawr Apr 29 '16 at 16:36
  • 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! – Remi.b Apr 29 '16 at 16:40
  • 3
    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. – Calimo Sep 09 '19 at 16:16
  • 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. – randy Mar 26 '21 at 03:58
8

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

Martin Gal
  • 14,910
  • 4
  • 18
  • 37
LizLaw
  • 81
  • 1
  • 1
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
Diogo
  • 782
  • 2
  • 8
  • 15
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)))
Shannon Hodges
  • 133
  • 1
  • 6