Search code examples
rggplot2bar-chartaxis-labelsgeom-bar

How to draw secondary axis of a bar chart in R?


I have the following data.frame and R-code and i am trying to produce a bar graph where precipation on the secondary axis should be reversed.

library(tidyverse)

DF <- data.frame(Month = 1:12, Flow = c(1,2,5,30,45,50,40,25,15,10,4,1.2),
                 Prec = c(24,17,23,31,55,93,80,69,13,32,25,25))

ggplot(DF,aes(x = Month))+
  geom_bar(aes(y = Flow), stat = "identity", color="blue", fill= "blue", width = 0.5)+
    geom_bar(aes(y = Prec*0.5), stat = "identity", color="red", fill= "red", width = 0.5)+
  scale_y_continuous("Streamflow", sec.axis = sec_axis(~ . /0.5, name = "Precipitation (mm)"), trans = "reverse")

enter image description here

Target: I would like to see a plot like below enter image description here


Solution

  • The secondary axis isn't really the issue here. The problem is having bars sitting on different baselines. The geom layers geom_bar and geom_col need to start at the same baseline even when there are different groups, so I don't think there is a way to do this using these geoms. However, it's not terribly difficult to reshape your data to draw the plot using geom_rect.

    DF %>%
      pivot_longer(Flow:Prec) %>%
      group_by(Month) %>%
      summarize(xmin = Month - 0.25, 
                xmax = Month + 0.25,
                ymax = ifelse(name == "Prec", 150, value),
                ymin = ifelse(name == "Prec", 150 - value, 0),
                name = name) %>%
      ggplot(aes(Month, ymin, fill = name)) +
      geom_rect(aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax)) +
      scale_y_continuous(name = "Flow", expand = c(0, 0),
                         sec.axis = sec_axis(~ 150 - .x, name = "Precipitation")) +
      theme_classic(base_size = 20) +
      scale_fill_manual(values = c("red", "blue"),
                        labels = c("Steam flow", "Precipitation"), name = NULL) +
      scale_x_continuous(breaks = 1:12, labels = month.abb) +
      theme(panel.border = element_rect(fill = NA),
            legend.position = c(0.85, 0.4))
    

    enter image description here