Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot2 and gridExtra: completely remove strip in facet_grid - not just invisible

I have two graphs that I'm placing one above the other, in the following way:

library(ggplot2)
library(gridExtra)
p1 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p2 <- p2 + facet_grid(cyl ~ .)
grid.arrange(p1, p2, ncol=1)

For this I need the x axes of the top and bottom graphs to line up, however because of the strip to the left, the faceted graph is narrower than the top graph. I can make the strip invisible using:

theme(strip.text.y = element_blank())
theme(strip.background = element_blank())

However this does not get rid of the space that the strip takes up. So I either need a way to get rid of the strip entirely, or have a way to split my faceted graph into separate graphs, yet somehow sharing the same y-axis label across them. In my graph I have two faceted panels that are not very tall, and there isn't enough space for them to each have a decent-sized y-axis.

Any suggestions?

like image 379
Claire Armstrong Avatar asked Jun 17 '13 09:06

Claire Armstrong


3 Answers

My solution would be to find the width of the strip and then set the margins of both plots to be zero, but shrink the one without the strip to be slightly smaller (the width of the strip) so they appear to be the same size. By trial and error it seems the strip is about 0.5 lines wide (but I guess you could figure this out programatically). Therefore just make sure the right plot margin in the plot without the strip text is 0.5 lines greater than the one with the invisible strip:

#  Add a line of width 0.5 on the left but set all other margins to zero
p1 <- p1 + theme( plot.margin = unit( c(0,0.5,0,0) , units = "lines" ) )
#  Set all margins to zero, the strip will take up a phantom amount of invisible space
p2 <- p2 + theme(strip.text.y = element_blank() , 
  strip.background = element_blank(),
  plot.margin = unit( c(0,0,0,0) , units = "lines" ) )

grid.arrange(p1, p2, ncol=1)

Obviously you can adjust the margins as you wish (e.g. add 1 to the first position in each numeric vector in plot.margin to get a border of one line along the top of each plot), as long as you keep 0.5 lines more margin in the right border of the second plot they will look the same.

enter image description here

like image 125
Simon O'Hanlon Avatar answered Oct 30 '22 04:10

Simon O'Hanlon


Another solution using functions from the gtable package. It aligns the plots but keeps the strip text. It uses a gtable function to insert a column to the right of p1 equal to the width of the strip text of p2.

library(ggplot2)
library(gridExtra)
library(gtable)

p1 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p2 <- p2 + facet_grid(cyl ~ .)

g1 = ggplotGrob(p1)
# gtable_show_layout(g1)  # View the layout
# g1$widths               # Get the widths of g1

g2 = ggplotGrob(p2)
# gtable_show_layout(g2)  # View the layout
# g2$widths               # Check the widths of g2

#  Add new column to the right of g1 equal in width the strip width of g2.
#  In g2, strip width is the 6th element the vector g2$widths
g1 <- gtable_add_cols(g1, g2$widths[6])  
grid.arrange(g1, g2, ncol=1)

enter image description here

## But note that if the y-axis titles and/or labels take up different widths, 
#  the two plots are not aligned
p1 <- ggplot(mtcars, aes(mpg, wt)) + geom_point() +
   theme(axis.title.y = element_text(vjust = .5, angle = 0, size = 30))
p2 <- ggplot(mtcars, aes(mpg, wt)) + geom_point()
p2 <- p2 + facet_grid(cyl ~ .)

g1 = ggplotGrob(p1)
g2 = ggplotGrob(p2)

g1 <- gtable_add_cols(g1, g2$widths[6])  # New column added to the right
grid.arrange(g1, g2, ncol=1)             # Plots are not aligned

enter image description here

# Need to set widths to the maximums in the two plots, 
# i.e., set g2 widths to be the same as g1 widths
g2$widths <- g1$widths
grid.arrange(g1, g2, ncol=1)            # Plots are aligned

enter image description here

EDIT: Or as suggested by baptiste, use gtable's rbind() function:

g1 = ggplotGrob(p1)
g2 = ggplotGrob(p2)

g1 <- gtable_add_cols(g1, g2$widths[6], 5)  # New column added to the right 

library(grid)
grid.draw(rbind(g1, g2, size = "first"))
like image 2
Sandy Muspratt Avatar answered Oct 30 '22 02:10

Sandy Muspratt


Here another solution using viewport and grid.layout. I creates 2 viewports with 2 differents layouts.Then I place ggplot2 plots using argument vp.

library(grid)
pushViewport(plotViewport(c(1,1,1,1),layout = grid.layout(2, 1)))
print(p2, vp = viewport(layout.pos.row = 2, layout.pos.col = 1))
pushViewport(viewport(layout.pos.row=1,
  layout = grid.layout(1, 2,widths = unit(c(1,1),c("null",'lines')))))
print(p1, vp = viewport(layout.pos.row = 1, layout.pos.col = 1))
upViewport(2)             

enter image description here

like image 1
agstudy Avatar answered Oct 30 '22 03:10

agstudy