Search code examples
rggplot2facet-wrap

Is there a way in R and ggplot to create a facet_wrap plot with a conditional geom?


Using R, I want to create a ggplot facet chart that includes a zero line (using geom_hline) in each facet having data that straddles the zero line, while excluding the zero line in each facet having exclusively positive or exclusively negative data. Here is a reprex.

library(ggplot)
dfw <- data.frame(
  date=c(1,2,3,4,5,6),
  A=c(50,53,62,56,54,61),
  B=c(-3,-1,5,7,4,-2),
  C=c(6,4,5,2,3,-2)
)
dfl <- pivot_longer(dfw,2:4,names_to="nms",values_to="val")
# With no zero line: works fine
ggplot(dfl)+
  geom_line(aes(x=date,y=val))+
  facet_wrap(~nms,scales="free_y")
# With zero line for all facets: works fine
ggplot(dfl)+
  geom_hline(yintercept=0)+
  geom_line(aes(x=date,y=val))+
  facet_wrap(~nms,scales="free_y")
# With zero line, but only for facets with some data points greater than zero
# and other data points less than zero: Does not work
c0 <- ggplot(dfl)+
  geom_line(aes(x=date,y=val))
if (min(y)>0 | max(y)<0) { # Error: object 'y' not found
  c0 <- c0+geom_hline(yintercept=0) 
} 
c0 <- c0+facet_wrap(~nms,scales="free_y")

Solution

  • You can compute y-intercepts for each facet before plotting, setting to 0 if the data includes 0 and NA otherwise:

    library(ggplot2)
    library(dplyr)
    
    dfl <- dfl %>% 
      mutate(
        zline = ifelse(min(val) < 0 & max(val > 0), 0, NA),
        .by = nms    # `.by` argument added in dplyr v1.1.0
      )
    
    ggplot(dfl) +
      geom_hline(aes(yintercept = zline), na.rm = TRUE) +
      geom_line(aes(x = date, y = val)) +
      facet_wrap(~nms, scales = "free_y")