3

Ok, I'm stumped on a home-brew ggplot.

What I would like to do is have a three row, one column faceted plot with a different y-axis label for each facet. The units of the y-axis are all the same. This would be the most convenient, but googling tells me it may not be possible.

Alternatively, I found this solution using grid.arrange, which seems like it will work. However, I want to keep a legend only for one plot and remove it from the other two, but maintain the spacing as if it were still there so that everything lines up nice. Someone had the same problem a few years ago, but the suggested solution is depreciated and I can't sort out how to make it work in modern ggplot.

Any help is appreciated! Using facets would be easiest!

Edited to add copy of plot after using user20560's gridArrange solution below. Very nearly there, just would like to get back the box around the top and bottom facet panels!

enter image description here

Iceberg Slim
  • 451
  • 6
  • 14

4 Answers4

6

I have assumed (possibly wrongly) that you are wanting to add separate y-axis titles rather than axis labels. [If it is the labels you want different you can use the scales argument in facet_grid]

There will be a ggplot way to do this but here are a couple of ways you could tweak the grobs yourself.

So using mtcars dataset as example

library(ggplot2)
library(grid)
library(gridExtra)

One way

p <- ggplot(mtcars, aes(mpg, wt, col=factor(vs))) +   geom_point() + 
                                 facet_grid(gear ~ .)

# change the y axis labels manually
g <- ggplotGrob(p)
yax <- which(g$layout$name=="ylab")

# define y-axis labels
g[["grobs"]][[yax]]$label <- c("aa","bb", "cc")

# position of labels (ive just manually specified)
g[["grobs"]][[yax]]$y <- grid::unit(seq(0.15, 0.85, length=3),"npc")

grid::grid.draw(g)


enter image description here
Or using grid.arrange

# Create a plot for each level of grouping variable and y-axis label
p1 <- ggplot(mtcars[mtcars$gear==3, ], aes(mpg, wt, col=factor(vs))) + 
                                 geom_point() + labs(y="aa") + theme_bw()
p2 <- ggplot(mtcars[mtcars$gear==4, ], aes(mpg, wt, col=factor(vs))) +  
                                 geom_point() + labs(y="bb") + theme_bw()
p3 <- ggplot(mtcars[mtcars$gear==5, ], aes(mpg, wt, col=factor(vs))) +  
                                 geom_point() + labs(y="cc") + theme_bw()

# remove legends from two of the plots
g1 <- ggplotGrob(p1)
g1[["grobs"]][[which(g1$layout$name=="guide-box")]][["grobs"]] <- NULL

g3 <- ggplotGrob(p3)
g3[["grobs"]][[which(g3$layout$name=="guide-box")]][["grobs"]] <- NULL

gridExtra::grid.arrange(g1,p2,g3)

If it is the axis titles you want to add I should ask why you want a different titles - can the facet strip text not do?

user20650
  • 24,654
  • 5
  • 56
  • 91
  • It is indeed axis titles I'm after. I hadn't thought of the (obvious) answer of just using the facet strip text, but I think it is cleaner and more standard to have the titles by the axis labels since they are actually describing the units (the units all three plots are mm but they are mm of different things). Your above solution using grid arrange works well except that it also removes the box around each facet panel. I'm not very grob savvy, so I'm not sure what to do about it. I'll try and add a copy of the plot in here somewhere.... – Iceberg Slim May 20 '14 at 14:01
  • @IcebergSlim; You can alter the thickness of the `panel.border`. For example, using the plot p3 above: `p3 + theme_bw() + theme(panel.border=element_rect(fill=NA, colour = "black", size=2))`, although perhaps just `theme_bw()` is enough. You may also find it useful to change the theme `plot.margin` to control the space between the arranged plots. – user20650 May 20 '14 at 17:38
  • Hmm....panel border doesn't do much for me. The borders display correctly until I get in there and manipulate the grob. I'm not sure why changing the guide box would affect the plot border. Maybe I'm moving into bug territory here. Thanks for the suggestion! – Iceberg Slim May 20 '14 at 19:39
  • @IcebergSlim; sorry mistake in the answer code - i was missing a `[["grobs"]]` - corrected - seems ok now – user20650 May 20 '14 at 22:34
  • Yup, that did it, really hammering home the fact that I don't know anything about grobs. Thanks for the followups. – Iceberg Slim May 21 '14 at 03:10
  • I am trying to use the first method, but I don't have a `ylab` in `g$layout$name` , insted i have `ylab-r` and `ylab-l`. I guess it means `ylab` on right and left side respectively. I tried to change one of them as well as both of them to the labels I want. But nothing works. Can you help me? – rm167 Dec 23 '17 at 17:01
  • 1
    @user3420448 ; seems this doesnt work any more due to the several significant rewrites of ggplot2 which have changed the internal plot object structure. These sort of answers have proven to be quite unstable. Some of the other answers may be able to help / provide an alt approach. – user20650 Dec 23 '17 at 19:57
4

Following the comments by Axeman and aosmith (thank you), here's a way to do this using the facet labels using ggplot2 version 2.2.0

library(ggplot2) # From sessionInfo(): ggplot2_2.2.0

ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() + 
  facet_grid(gear ~ ., switch = 'y') +
  theme( axis.title.y = element_blank(), # remove the default y-axis title, "wt"
         strip.background = element_rect(fill = 'transparent'), # replace the strip backgrounds with transparent
         strip.placement = 'outside', # put the facet strips on the outside
         strip.text.y = element_text(angle=180)) # rotate the y-axis text (optional)
# (see ?ggplot2::theme for a list of theme elements (args to theme()))
Richard DiSalvo
  • 850
  • 12
  • 16
  • 1
    Sounds like you want `switch.placement = "outside"` for ggplot2_2.2.0. See answer [here](http://stackoverflow.com/a/36337286/2461552) for an example. – aosmith Nov 21 '16 at 21:22
1

I know this is an old post, but after finding it, I could not get @user20560's response to work.

I've edited @user20560's grid.extra approach as follows:

library(ggplot2)
library(gridExtra)
library(grid)

# Create a plot for each level of grouping variable and y-axis label
p1 <- ggplot(mtcars[mtcars$gear==3, ], aes(mpg, wt, col=factor(vs))) + 
  geom_point() + labs(y="aa") + theme_bw()
p2 <- ggplot(mtcars[mtcars$gear==4, ], aes(mpg, wt, col=factor(vs))) +  
  geom_point() + labs(y="bb") + theme_bw()
p3 <- ggplot(mtcars[mtcars$gear==5, ], aes(mpg, wt, col=factor(vs))) +  
  geom_point() + labs(y="cc") + theme_bw()

# get the legend as a grob
legend <- ggplotGrob(p1)
legend <- legend$grobs[[which(legend$layout$name=="guide-box")]]
lheight <- sum(legend$height)
lwidth <- sum(legend$width)

# remove the legend from all the plots
p1 <- p1 + theme(legend.position = 'none')
p2 <- p2 + theme(legend.position = 'none')
p3 <- p3 + theme(legend.position = 'none')

# force the layout to the right side
layoutMat <- matrix(c(1,2,3,4,4,4),ncol = 2)

grid.arrange(p1,p2,p3,legend, layout_matrix = layoutMat, ncol = 2, 
             widths = grid::unit.c(unit(1,'npc') - lwidth, lwidth))

This example is somewhat specific to this particular layout. There is a more general approach on the ggplot2 wiki.

r_alanb
  • 873
  • 8
  • 21
  • These have different x-axes. I believe the OP wanted three plots that shared the x-axis, but had different y-axes. – banbh Sep 30 '16 at 00:29
  • Based on OP's last edit the picture showed different x-axes. If you want to share x-axes, then you can use `facet_grid` as shown in @Richard DiSalvo's response below. – r_alanb Nov 22 '16 at 21:51
1

I too had trouble getting the first approach in the answer of user20560 (above) to work. This is probably because the internals of ggplot2 have evolved, and there is no guarantee that these internals should stay the same. In any case, here is a version that currently works:

library(ggplot2) # From sessionInfo(): ggplot2_2.1.0
library(grid)

p <- ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() + facet_grid(gear ~ .)
g <- ggplotGrob(p)
yax <- which(g$layout$name == "ylab")
g[["grobs"]][[yax]]$children[[1]]$label <- c('fo','bar','foobar')
g[["grobs"]][[yax]]$children[[1]]$y <- grid::unit(seq(0.15, 0.85, length=3), "npc")
grid.draw(g)

Note that this is the approach that keeps the facets and does not repeat the x-axes.

Community
  • 1
  • 1
banbh
  • 1,331
  • 1
  • 13
  • 31
  • 2
    A good way to do this with the current `ggplot2` version is to use `switch = 'y'` in `facet_grid`, and then use theme settings to remove the y axis labels and strip background. No `grid` manipulation necessary. – Axeman Sep 30 '16 at 13:45