Search code examples
rggplot2facet-gridggtext

Is it possible to change the alignment of only 1 facet title


Say I have a facetted ggplot like this:

data(iris)
ggplot(iris, aes(x = Petal.Width, y = Sepal.Length)) +
  facet_grid(. ~ Species) +
  geom_point()

enter image description here

Question:

Is it possible to align only the 1st facet label ("setosa") to the left, but keep the others central?

I have not had any luck with straight ggplot, so I tried converting to a gtable (code below), and I can see that the element I probably need to modify is tableGrb #13 - strip-t-1, but I'm not sure how to do so. I tried changing the gt$layout$l and gt$layout$r values to 4 (was 5) but that moves the heading off the facet entirely.

pl <- ggplot(iris, aes(x = Petal.Width, y = Sepal.Length)) +
  facet_grid(. ~ Species) +
  geom_point()
gt <- ggplotGrob(pl)

gt$layout[gt$layout$name == 'strip-t-1', c('l', 'r')] <- c(4, 4) # my attempt
grid.newpage()
grid.draw(gt)

Does anyone know if this is possible?


Solution

  • You have the right idea but not the right spot. Basically you need to do something like this:

    library('ggplot2')
    library('grid')
    
    data(iris)
    pl <- ggplot(iris, aes(x = Petal.Width, y = Sepal.Length)) +
      facet_grid(. ~ Species) +
      geom_point()
    gt <- ggplotGrob(pl)
    
    gt$grobs[[13]]$grobs[[1]]$children$strip.text.x.top..titleGrob.186$children$GRID.text.184$x <- unit(0.1, 'npc')
    grid.newpage()
    grid.draw(gt)
    

    enter image description here

    But this won't work for you... it won't even work for me a second time since the titleGrob.xxx and GRID.text.xxx change each time you use gt <- ggplotGrob(pl)

    So try a different method and knowing the location you need to edit as you have pointed out (it's in the first strip):

    gp <- ggplotGrob(pl)
    grid.ls(grid.force(gp))
    
    # layout
    #   background.1-13-13-1
    #   panel-1-1.8-5-8-5
    # ...
    #   strip-t-1.7-5-7-5                        <- here
    #     strip.1-1-1-1
    #       strip.background.x..rect.533
    #       strip.text.x.top..titleGrob.525
    #         GRID.text.523
    #   strip-t-2.7-7-7-7
    #     strip.1-1-1-1
    #       strip.background.x..rect.533
    #       strip.text.x.top..titleGrob.528
    #         GRID.text.526
    #   strip-t-3.7-9-7-9
    #     strip.1-1-1-1
    #       strip.background.x..rect.533
    #       strip.text.x.top..titleGrob.531
    #         GRID.text.529
    #   axis-t-1.6-5-6-5
    # ...
    #   title.3-9-3-5
    #   caption.11-9-11-5
    #   tag.2-2-2-2
    

    You can use gPath to get the path without knowing the GRID.text.xxx in advance. Note that in your example, we can just edit the first GRID.text and it will work if global = FALSE, ie, if global = TRUE, all of them would change.

    g1 <- editGrob(
      grid.force(gp),
      gPath('GRID.text'), grep = TRUE, global = FALSE,
      x = unit(0.25, 'npc')
    )
    grid.newpage()
    grid.draw(g1)
    

    enter image description here

    However, you might need a very specific path, so follow the strip-t-1 down to your GRID.text (note that global = TRUE, and it only affects one strip)

    g2 <- editGrob(
      grid.force(gp),
      gPath('strip-t-1', 'strip', 'strip', 'GRID.text'), grep = TRUE, global = TRUE,
      x = unit(0.75, 'npc')
    )
    grid.newpage()
    grid.draw(g2)
    

    enter image description here