1

At the moment, my graph looks like this:

Figure without x axis tick backgrounds

However, I'd like to colour the background of the x axis tick so it resembles this:

Figure with x axis tick background

I think I'm almost there, but can't figure out how to change the background to the x axis ticks, and not the writing itself.

I changed the text colour using: theme(axis.text.x = element_text(colour = Saltcolour))

Any help would be greatly appreciated!

Richard Telford
  • 9,558
  • 6
  • 38
  • 51

1 Answers1

3

One general approach is to use grobs (Grid objects) and use a rectGrob object to be displayed under the axis text. I'll demonstrate with an example shown here.

library(ggplot2)

set.seed(8675309)
df <- data.frame(
  x=paste0('Test', 1:10),
  y=rnorm(10, 10)
)

ggplot(df, aes(x,y)) +
  geom_col(color='black', fill='gray', alpha=0.8) +
  scale_y_continuous(expand=expansion(mult=c(0,0.05))) +
  coord_cartesian(clip='off') +
  labs(x=NULL) +
  theme_classic()

enter image description here

Display the Boxes

To create the grob, you can use the grid package and rectGrob. Since we want to draw many boxes, we can supply a vector for x (to draw one at x positions 1 through 10), and then supply the fill colors via a vector sent to fill. Note that when using grobs, you can supply the various parameters through the gp argument inside of gpar(). Unlike a ggplot geom, the grobs are not matched to a data frame, so you'll have to manually specify the way colors/sizes are mapped via vectors as I've done here.

library(grid)

muh_grob <- grid::rectGrob(
  x=1:10, y=0, gp=gpar(
    color='black', fill=rainbow(10), alpha=0.2))

To use the grob, you can use annotation_custom(), where you need to specify the min and max values of y and x. You'll have to likely mess around with the numbers to get things to look right. Note the values are in npc, so 0 is left and 1 is all the way on the right in x axis here (discrete values). It's also very important that you include coord_*(clip="off"). It can be any of the coord_ functions, but you need clipping off or you will not be able to see the grob. I've also applied a margin to the top of the x axis text to move it downward a bit and make room for the box around it.

ggplot(df, aes(x,y)) +
  geom_col(color='black', fill='gray', alpha=0.8) +
  scale_y_continuous(expand=expansion(mult=c(0,0.05))) +
  coord_cartesian(clip='off') +
  labs(x=NULL) +
  theme_classic() +
  theme(
    axis.text.x = element_text(margin=margin(t=10)),
  ) +
  annotation_custom(
    grob=muh_grob, xmin = 0, xmax = 1, ymin = -0.5, ymax=0.1
  )

enter image description here

Multiple Facets

OP shared a plot with two facets that contained these boxes... so how to do that? Well, it's not quite as straightforward to do, since annotation_custom() is applied the same way to each facet. Each facet shares the same values of x and y, so if you specify a grob is from xmin=0 and xmax=0.5, this will apply your grob to the left side of each facet.

To get around this, there is a very nice adjustment to the method provided in another answer here, represented below:

library(gridExtra)
annotation_custom2 <- 
  function (grob, xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf, data) 
  {
    layer(data = data, stat = StatIdentity, position = PositionIdentity, 
          geom = ggplot2:::GeomCustomAnn,
          inherit.aes = TRUE, params = list(grob = grob, 
                                            xmin = xmin, xmax = xmax, 
                                            ymin = ymin, ymax = ymax))
  }

I'll then make a left and right grob, and apply it using that function, which allows us to specify a data argument to annotation_custom2() and place the grob on one facet.

muh_left_grob <- rectGrob(
  x=1:5, y=0, gp=gpar(color='black', fill='red', alpha=seq(0.7, 0.1, length.out = 5)))
muh_right_grob <- rectGrob(
  x=1:5, y=0, gp=gpar(color='black', fill='blue', alpha=seq(0.7, 0.1, length.out = 5)))

ggplot(df, aes(x,y)) +
  geom_col(color='black', fill='gray', alpha=0.8) +
  scale_y_continuous(expand=expansion(mult=c(0,0.05))) +
  coord_cartesian(clip='off') +
  labs(x=NULL) +
  theme_classic() +
  theme(axis.text.x = element_text(margin=margin(t=10))) +
  facet_wrap(~my_facet, scales='free_x') +
  annotation_custom2(
    data=subset(df, my_facet=='A Facet'), grob=muh_left_grob,
    xmin=0, xmax=1, ymin=-0.5, ymax=0.1) +
  annotation_custom2(
    data=subset(df, my_facet=='Another Facet'), grob=muh_right_grob,
    xmin=0, xmax=1, ymin=-0.5, ymax=0.1)

enter image description here

chemdork123
  • 12,369
  • 2
  • 16
  • 32
  • Thank you very much for your help - that's great! – William G Rigg Apr 02 '21 at 14:55
  • My issue is that now the graph is compressed along the y axis. I'm not sure why this has happened: https://imgur.com/a/WxK7O6d. Do you have any thoughts? – William G Rigg Apr 02 '21 at 15:00
  • Difficult to say without seeing the plot code. It looks like you're not just creating the plot with `ggplot`, but adding in the "A" and "B" via another package right? I think that package is basically making your plot into a grob and using that to display the facets and text. likely it's something along those lines – chemdork123 Apr 02 '21 at 17:02