Search code examples
rggplot2facet-grid

How can I annotate facets that do not have data using ggplot2 facet_grid?


I have multivariate longitudinal samples that I am plotting in a large facet_grid, specifically using facet_grid so that when I don't have a certain time point for a certain sample it still keeps everything in a nicely ordered grid. I am trying to add some sort of indicator to empty facets to show that they don't have data (rather than just having no dots show up), because sometimes the empty facet is a result of not having collected that sample yet. What I have been able to get is a label that shows up in every facet that DOES have data, but I can't seem to figure out how to get a label that shows up in every facet that does NOT have data. It would be even better if I could add a red X through select facets, or some other indication of "No Data Available", rather than a text label, if you have suggestions about that as well.

ggplot(mpg, aes(displ, cty)) + geom_point() + 
facet_grid(vars(drv), vars(cyl)) + geom_text(x = 5, y = 20, label = "Blah")

Example plot


Solution

  • You could add rows for the missing combinations with a full_join. These new rows will have no data, so nothing will be plotted, but you can add an additional column with the message that geom_text will use:

    library(tidyverse)
    theme_set(theme_bw())
    
    mpg %>% 
      full_join(crossing(drv=unique(mpg$drv), cyl=unique(mpg$cyl))) %>% 
      mutate(empty=ifelse(is.na(model), "No data available", NA_character_),
             x=mean(range(displ, na.rm=TRUE)), 
             y=mean(range(cty, na.rm=TRUE))) %>% 
      ggplot(aes(displ, cty)) + 
        geom_point() + 
        facet_grid(vars(drv), vars(cyl)) + 
        geom_text(aes(x, y, label=empty), colour="red", size=3)
    

    enter image description here

    For an "X", change the empty text to "X" and plot it with a large size. For example:

    mpg %>% 
      full_join(crossing(drv=unique(mpg$drv), cyl=unique(mpg$cyl))) %>% 
      mutate(empty=ifelse(is.na(model), "X", NA_character_),
             x=mean(range(displ, na.rm=TRUE)), 
             y=mean(range(cty, na.rm=TRUE))) %>% 
      ggplot(aes(displ, cty)) + 
        geom_point() + 
        geom_text(aes(x, y, label=empty), colour="red", size=20) +
        facet_grid(vars(drv), vars(cyl))
    

    enter image description here

    Or we could use geom_segment:

    mpg %>% 
      full_join(crossing(drv=unique(mpg$drv), cyl=unique(mpg$cyl))) %>% 
      ggplot(aes(displ, cty)) + 
        geom_point() + 
        geom_segment(data=. %>% filter(is.na(model)),
                     x=min(mpg$displ), xend=max(mpg$displ),
                     y=min(mpg$cty), yend=max(mpg$cty),
                     colour="red") +
        geom_segment(data=. %>% filter(is.na(model)),
                     x=min(mpg$displ), xend=max(mpg$displ),
                     y=max(mpg$cty), yend=min(mpg$cty),
                     colour="red") +
        facet_grid(vars(drv), vars(cyl))
    

    enter image description here

    The above can be done with a single call to geom_segment, but it requires (AFAICT) a more complex data prep:

    mpg %>% 
      full_join(crossing(drv=unique(mpg$drv), cyl=unique(mpg$cyl))) %>% 
      ggplot(aes(displ, cty)) + 
        geom_point() + 
        geom_segment(data=. %>% 
                       filter(is.na(model)) %>% 
                       select(-displ, -cty) %>% 
                       crossing(
                         displ=range(mpg$displ),
                         cty=range(mpg$cty)
                       ),
                     aes(xend=rev(displ), yend=rev(cty)), colour="red") +
        facet_grid(vars(drv), vars(cyl))