Search code examples
rggplot2facet-wrap

Why geom_rect colours only first row of facet_wrap?


I am trying to get shaded rectangles on every even-numbered panel of my facet_wrap plot. However, when I use geom_rect, it produces the rectangles only on the second panel. I tried using annotate and geom_tile but with no success. I presume I am missing some simple detail here, probably related to the fact that my X variable is categorical and not numeric, but I am fighting this for a few hours already...

Here is my code:

even_numbers <- seq(2,nrow(df.plt),2)
ggplot(df.plt) + 
  geom_rect(data = df.plt[even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) + 
  facet_wrap(vars(Grp), ncol=1)

And the resulting plot: resulting plot with geom_rect and facet_wrap not working as expected

Edit:

I have created a dummy dataset example which replicates my issue:

set.seed(002) # just to make it reproducible
df.tmp = data.frame(nst = rnorm(100*2), Srs = sample(rep(c("S3","S4"),100)), Cnd = sample(rep(c("DN","DA","DV","DAV"),50)), Grp = sample(rep(c("close","far"),100)))

even_numbers <- seq(2,nrow(df.tmp),2)
ggplot(df.tmp) + 
  geom_rect(data = df.tmp[even_numbers, ],
            xmin = even_numbers - 0.5, xmax = even_numbers + 0.5,
            ymin = -Inf, ymax = Inf, alpha = 0.3, fill = 'grey') +
  geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) + 
  facet_wrap(vars(Grp), ncol=1)

Solution

  • While your idea was right IMHO you could achieve your desired result more easily by putting the xmin and xmax values in a dataframe and by mapping on aesthetics. First note that we only need a vector of even numbers of length(unique(df.tmp$Cnd)), i.e. the number of categories of Cnd. Second, as we are mixing discrete and continuous x variables I added an scale_x_discrete before geom_rect as otherwise we will get an error.

    library(ggplot2)
    
    even_numbers <- seq(2, length(unique(df.tmp$Cnd)), 2)
    
    rects <- data.frame(
      xmin = even_numbers - 0.5,
      xmax = even_numbers + 0.5
    )
    
    ggplot(df.tmp) +
      scale_x_discrete() +
      geom_rect(
        data = rects, aes(xmin = xmin, xmax = xmax),
        ymin = -Inf, ymax = Inf, alpha = 0.3, fill = "grey"
      ) +
      geom_boxplot(aes(x = Cnd, y = nst, fill = Srs), position = position_dodge(0.9), outlier.shape = 1) +
      facet_wrap(vars(Grp), ncol = 1)
    

    EDIT Just in case. The reason why your approach did not work is that the relevant part of the data used for the rects contains only the far group. The issue is that basically only rects corresponding to even numbers in the range 1 to 4 (the number of Cnd categories) are displayed. As can be seen from the following code snippet which replicates the data which under the hood is used for the rects in your approach only the far grp is present (after filtering for even numbers in the range 1 to 4):

    even_numbers <- seq(2,nrow(df.tmp),2)
    
    dplyr::bind_cols(df.tmp[even_numbers, ], data.frame(even_number = even_numbers)) |> 
      dplyr::filter(even_number <= 4)
    #>          nst Srs Cnd Grp even_number
    #> 1  0.1848492  S3  DV far           2
    #> 2 -1.1303757  S3  DA far           4