rggplot2scaleaxis

Set second axis to a plot, using ggplot


I want to perform a chart that involves two variables (weight - kg) and (rain - mm) through the years (1991 - 2021). BOTH VARIABLES ARE NOT RELATED!

Everything goes ok when plotting the graphs separated one from each other:

library(ggplot2)

    ggplot(data2, aes(x=ano_dtt)) +
      geom_col(aes(y=PesoAj220_d), fill="lightblue2") +
      ylim(0,300)
    

enter image description here

    ggplot(data2, aes(x=ano_dtt)) +
      geom_point(aes(y=ppt)) +
      geom_path(aes(y=ppt)) +
      ylim(0,1700)

enter image description here

But, when I try to add the second axis everything goes wrong!!!

    scaleRight <- 0.75
    
    ggplot(data2, aes(x=ano_dtt)) +
      geom_col(aes(y=PesoAj220_d), fill="lightblue2") +
      ylim(0,300) +
      geom_point(aes(y=ppt)) +
      geom_path(aes(y=ppt), colour="red", size=0.9) +
      ylim(0,1700)+
      scale_y_continuous(sec.axis = sec_axis(~.*scaleRight, name="precipitacion (mm)")) +
      labs(title = "", x="año", y="peso")

enter image description here

I also try this:

    scaleRight <- 0.75
    
    ggplot(data2, aes(x=ano_dtt)) +
      geom_col(aes(y=PesoAj220_d), fill="lightblue2") +
      ylim(0,300) +
      geom_point(aes(y=ppt)) +
      geom_path(aes(y=ppt), size=0.9) +
      ylim(c(0,1700)) 

enter image description here

But there is something wrong with the only axis present in the plot.

I think that the problem is with the

    scaleRight <- 0.75

Can someone help to solve this problem? I'm pretty sure that is very very easy!


Solution

  • The issue you face is that you have to scale your secondary y-axis values to fall within the range of the primary x-axis. As you guessed, multiplying the secondary y-axis values by a set value (scaleRight) is not how to achieve your goal.

    I struggle with scaling within a sec_axis() function, so I prefer to scale the secondary y-axis values prior to plotting. The approach outlined below creates the same number of axis breaks on both y-axes, comment if you prefer a different number of breaks each side and I'll update the code.

    library(ggplot2)
    
    # Example data
    set.seed(1)
    data2 <- data.frame(ano_dtt = 1991:2021,                   
                        PesoAj220_d = sample(250:300, 31),
                        ppt = sample(750:1550, 31))
    
    # Set y-axes limits, must exceed or equal min/max data values
    # Set min and max limits for primary (left) y-axis
    l.y.min <- 0
    l.y.max <- 300
    
    # Set min and max limits for secondary (right) y-axis
    r.y.min <- 0
    r.y.max <- 1700
    
    # Function to scale secondary y-axis values
    axis_scaled <- function(x, y, z, max.var) {
      y <- ifelse(is.na(x), NA, ((x - y) / (z - y)) * max.var)
      y <- y[2:(length(y)-1)]
    }
    
    # Add scaled secondary y-axis values to data2
    data2$scaled_ppt <- axis_scaled(c(r.y.min, data2$ppt, r.y.max),
                                    r.y.min, r.y.max, l.y.max)
    
    # Create breaks for primary y-axis and labels for secondary y-axis
    l.y.breaks <- seq(l.y.min, l.y.max, length.out = 5)
    r.y.labels <- seq(r.y.min, r.y.max, length.out = length(l.y.breaks))
    
    # Plot
    ggplot(data2, aes(x = ano_dtt)) +
      geom_col(aes(y = PesoAj220_d), fill = "lightblue2") +
      geom_point(aes(y = scaled_ppt)) +
      geom_path(aes(y = scaled_ppt), colour = "red", linewidth = 0.9) +
      scale_x_continuous(breaks = 1991:2021) +
      scale_y_continuous(breaks = l.y.breaks,
                         sec.axis = sec_axis(~ .,
                                             breaks = l.y.breaks,
                                             labels = r.y.labels,
                                             name = "precipitacion (mm)")) +
      labs(title = "", x = "año", y = "peso") +
      theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
    

    result