Search code examples
rggplot2facetfacet-wrap

ggplot2 geom_rect not scale appropriately under facet_grid


The question is: geom_rect prevents the scaling of facet_grid using ggplot2.

I am wondering whether it is caused by conflict in two data frames, but don't know how to solve this issue. Hope you can help me.

A sample code is follows:

library(ggplot2)
data_1 <- data.frame(x = c(seq(from = 1, to = 10, by = 1),
                           seq(from = 21, to = 50, by = 1)),
                     y = rnorm(40, mean = 3, sd = 1),
                     z = c(rep("A", 10), rep("B", 30)))

shade <- 
  data.frame(xmin = c(2, 6, 39),
             xmax = c(3, 8, 43),
             ymin = - Inf,
             ymax = Inf)

If not using geom_rect, I can scale with facet_grid appropriately like this:

ggplot(data = data_1, aes(x = x, y = y)) +
  geom_bar(stat = "identity", fill = "blue") +
  facet_grid(.~z, space = "free_x", scales = "free_x")

the result is like this:

enter image description here

But if I draw some geom_rect, then the scale previously shown disappeared, with code and graph like this:

ggplot(data = data_1, aes(x = x, y = y)) +
  geom_bar(stat = "identity", fill = "blue") +
  geom_rect(data = shade, inherit.aes = FALSE,
            mapping = aes(xmin = xmin, 
                          xmax = xmax, 
                          ymin = ymin, ymax = ymax),
            fill = 'red', alpha = 0.2) +
  facet_grid(.~z, space = "free_x", scales = "free_x")

enter image description here

How to keep the previous scaling on x while drawing these geom_rect?

Any suggestion is greatly appreciated.


Solution

  • The x-axis range changes because data in the geom_rect layer extends beyond that of the original plot. The scale is doing exactly what it's expected to do.

    If you want different rectangles to show up for each facet, it would be cleaner to include the facet variable in shade, & keep only the rows with xmin / xmax within the range of each facet. For example:

    library(dplyr)
    shade2 <- shade %>%
      # add facet-specific x-axis range information from data_1
      tidyr::crossing(data_1 %>% group_by(z) %>%
                        summarise(x1 = min(x),
                                  x2 = max(x)) %>%
                        ungroup) %>%
    
      # filter for rects within each facet's x-axis range
      group_by(z) %>%
      filter(xmin >= x1 & xmax <= x2) %>%
      ungroup()
    
    > shade2
    # A tibble: 3 x 7
       xmin  xmax  ymin  ymax z        x1    x2
      <dbl> <dbl> <dbl> <dbl> <fct> <dbl> <dbl>
    1     2     3  -Inf   Inf A         1    10
    2     6     8  -Inf   Inf A         1    10
    3    39    43  -Inf   Inf B        21    50
    

    Plot:

    ggplot(data = data_1, aes(x = x, y = y)) +
      geom_col(fill = "blue") + # geom_col is equivalent to geom_bar(stat = "identity")
      geom_rect(data = shade2, inherit.aes = FALSE,
                mapping = aes(xmin = xmin, xmax = xmax, 
                              ymin = ymin, ymax = ymax),
                fill = 'red', alpha = 0.2) +
      facet_grid(.~z, space = "free_x", scales = "free_x")
    

    result