Modifying Plot Layouts Through `gtable` Manipulation - brodieG/ggplot2 GitHub Wiki

Before ggplot objects are output to a device, they are represented as gtable objects. These objects provide some structure around grobs to facilitate their arrangement and manipulation in table like structures. gtable objects purportedly inherit from grob, but it seems in reality they are a custom gTree like object that shares some attributes with grid objects but really isn't one of them. gtable objects contain a list with grobs and layout information, among other things.

Generating gtable Objects

As mentioned previously, gtable objets are generated by print.ggplot, though if you want to manipulate them directly you can use ggplotGrob:

library(ggplot2)
p <- ggplot(data=data.frame(a=1:10, b=sample(1:10))) + geom_point(aes(x=a, y=b, color=b))
library(gtable)
p.grob <- ggplotGrob(p)
p.grob                    # we will re-use this later

produces

TableGrob (6 x 6) "layout": 9 grobs
  z     cells       name                                 grob
1 0 (1-6,1-6) background      rect[plot.background.rect.2603]
2 3 (3-3,3-3)     axis-l absoluteGrob[GRID.absoluteGrob.2587]
3 1 (4-4,3-3)     spacer                       zeroGrob[NULL]
4 2 (3-3,4-4)      panel               gTree[GRID.gTree.2575]
5 4 (4-4,4-4)     axis-b absoluteGrob[GRID.absoluteGrob.2581]
6 5 (5-5,4-4)       xlab         text[axis.title.x.text.2589]
7 6 (3-3,2-2)       ylab         text[axis.title.y.text.2591]
8 7 (3-3,5-5)  guide-box                    gtable[guide-box]
9 8 (2-2,4-4)      title           text[plot.title.text.2601]

This gtable object contains all the graphical information required to render the plots (i.e. all the computations for converting the original data into plots has already been carried out, the step missing is the actual plotting to screen).

Navigating gtable Objects

Navigating is relatively straightforward. Printing the gtable directly tells you most of what you need to know. The rest you get by comparing the gtable to the output of gtable_show_layout(p.grob) (do this!!). If you want additional detail on the grobs, you can try using grid.ls on grid objects (note gtables are not grid objects, so it won't work on those; borrowed from Editing Raw Grobs, though that post may pre-date the introduction of gtable):

grid.ls(p.grob$grobs[4](/brodieG/ggplot2/wiki/4), print=nestedListing)  # picked `gTree` from `gtable`

produces:

GRID.gTree.267
  grill.gTree.266
    panel.background.rect.257
    panel.grid.minor.y.polyline.259
    panel.grid.minor.x.polyline.261
    panel.grid.major.y.polyline.263
    panel.grid.major.x.polyline.265
  geom_point.points.252
  GRID.segments.253
  panel.border.zeroGrob.254

Combining Two Plots With a Similar X-axis

In order to bind two plots of similar layout together, you can use rbind.gtable, provided you set the size parameter to "first" (it seems that the plots have some complex measures that can't be easily compared before binding, but if you just take the widths of the first plot's gtable columns as given it seems to work fine).

# Make plots

library(ggplot2)
s <- scale_color_continuous(limits=c(0, 110))
p <- p + s
q <- ggplot(data=data.frame(a=1:10, b=sample(100:109))) + geom_line(aes(x=a, y=b, color=b)) + s
q.grob <- ggplotGrob(q)

# Combine plots by removing bottom rows from top plot

pq.grob <- rbind(p.grob[c(1:3),], q.grob, size="first")  # size="first" side steps an apparent bug (?)

# Remove bottom guide, and recenter top guide

pq.grob[1](/brodieG/ggplot2/wiki/1)[13](/brodieG/ggplot2/wiki/13) <- NULL
pq.grob$layout <- pq.grob$layout[-13, ]
pq.grob$layout[4, "b"] <- 6

# Draw

grid.newpage()
grid.draw(pq.grob)

Other Notes

  • Note axis-b and axis-l mean axis bottom and left respectively.
  • gtable objects are converted to gtree by gtable:::grid.draw.gtable before a final call to grid.draw