The answer to this question lies in the grid and gtable . Everything in the plot is laid out in a certain order, and you can find where everything is, if you dig a little.
library('gtable') library('grid') library('magrittr')
The ultimate goal of this operation is to overlay the top facet label, but the trick is that both of these faces exist on the same line in the grid space. They are a table inside a table (look at the lines called "strip", also pay attention to zeroGrob , they will be useful later):
z ## TableGrob (13 x 14) "layout": 34 grobs ## z cells name grob ## 1 0 ( 1-13, 1-14) background rect[plot.background..rect.522] ## 2 1 ( 7- 7, 4- 4) panel-1-1 gTree[panel-1.gTree.292] ... ## 20 3 ( 7- 7,12-12) axis-r-1 zeroGrob[NULL] ## 21 3 ( 9- 9,12-12) axis-r-2 zeroGrob[NULL] ## 22 2 ( 6- 6, 4- 4) strip-t-1 gtable[strip] ## 23 2 ( 6- 6, 6- 6) strip-t-2 gtable[strip] ## 24 2 ( 6- 6, 8- 8) strip-t-3 gtable[strip] ## 25 2 ( 6- 6,10-10) strip-t-4 gtable[strip] ## 26 2 ( 7- 7,11-11) strip-r-1 gtable[strip] ## 27 2 ( 9- 9,11-11) strip-r-2 gtable[strip] ... ## 32 8 ( 3- 3, 4-10) subtitle zeroGrob[plot.subtitle..zeroGrob.519] ## 33 9 ( 2- 2, 4-10) title zeroGrob[plot.title..zeroGrob.518] ## 34 10 (12-12, 4-10) caption zeroGrob[plot.caption..zeroGrob.520]
If you zoom in to the first bar, you can see the nested structure:
z$grob[[22]]
For each grob, we have an object that lists the order in which it is depicted ( z ), position in the grid ( cells ), label ( name ) and geometry ( grob ).
Since we can create gtables inside gtables, we will use this to build on our original plot. First, we need to find positions in the plot that need to be replaced.
# Find the location of the strips in the main plot locations <- grep("strip-t", z$layout$name)
Once we have the positions, we need to create a replacement table. We can do this with a list matrix (yes, that's weird, just roll with it). This matrix should have three columns and two rows in our case due to two faces and a gap between them. Since we are just going to replace the data in the matrix later, we are going to create one with zeroGrob s:
mat <- matrix(vector("list", length = 6), nrow = 2) mat[] <- list(zeroGrob()) # The separator for the facets has zero width res <- gtable_matrix("toprow", mat, unit(c(1, 0, 1), "null"), unit(c(1, 1), "null"))
The mask is created in two stages, covering the first group of facets, and then the second. In the first part, we use the previously recorded location to grab the corresponding bar from the original graph and add it on top of our replacement matrix res , covering the entire length. Then we add this matrix on top of our graph.
# Adding the first layer zz <- res %>% gtable_add_grob(z$grobs[[locations[1]]]$grobs[[1]], 1, 1, 1, 3) %>% gtable_add_grob(z, ., t = top, l = l[1], b = top, r = r[1], name = c("add-strip"))
