rggplot2scaleaxis

Set second axis to a plot


I want to create a chart that involves two variables - dtt and PesoAj220_d - through the years 1991 - 2021 (ano_dtt). Both variables are not related.

Everything works when plotting the graphs separately:

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

When I try to add the second axis, it does not work correctly:

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 tried:

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 I need the second axis. I think that the problem is with:

scaleRight <- 0.75

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