Home > Software engineering >  Set left and right limit ranges simultaneously for dual y-axis plot using ggplot2
Set left and right limit ranges simultaneously for dual y-axis plot using ggplot2

Time:08-04

Given a dataframe df:

df <- structure(list(date = c("2022-4-30", "2022-5-5", "2022-5-6", 
"2022-5-9", "2022-5-10", "2022-5-11", "2022-5-12", "2022-5-13", 
"2022-5-16", "2022-5-17", "2022-5-18", "2022-5-19", "2022-5-20"
), value1 = c(6, 6.1, 6.5, 6.9, 7.3, 7.7, 8.1, 8, 7.9, 7.8, 7.7, 
7.6, 7.5), value2 = c(-1L, -2L, 0L, 6L, 7L, 5L, 3L, 10L, 18L, 
16L, 12L, 19L, 20L), value3 = c(2.7, 2.4, 1.7, 0.5, -0.5, 0.2, 
-0.3, -0.2, 0.4, 0.9, 1.3, 1.1, 1)), class = "data.frame", row.names = c(NA, 
-13L))

Out:

        date value1 value2 value3
1  2022-4-30    6.0     -1    2.7
2   2022-5-5    6.1     -2    2.4
3   2022-5-6    6.5      0    1.7
4   2022-5-9    6.9      6    0.5
5  2022-5-10    7.3      7   -0.5
6  2022-5-11    7.7      5    0.2
7  2022-5-12    8.1      3   -0.3
8  2022-5-13    8.0     10   -0.2
9  2022-5-16    7.9     18    0.4
10 2022-5-17    7.8     16    0.9
11 2022-5-18    7.7     12    1.3
12 2022-5-19    7.6     19    1.1
13 2022-5-20    7.5     20    1.0

I use code below to plot dual y-axis (left side for value1 and value3, right side for value2):

df_m <- melt(df, id.vars = 'date')
df_m <- df_m %>% 
  mutate(date=as.Date(date))

df_m1 <- df_m %>% 
  filter(variable %in% c("value1", 'value3')) 

df_m2 <- df_m %>% 
  filter(variable %in% c("value2")) %>%
  mutate(value = value * 0.6)


df_m2
coeff = 1/0.6

ggplot()  
  geom_line(data = df_m1[!is.na(df_m1$value), ], aes(x = date, y = value, col = variable), alpha = 1, size = 1)  
  geom_line(data = df_m2[!is.na(df_m2$value), ], aes(x = date, y = value, col = variable), alpha = 1, size = 1)  
  scale_y_continuous(
    name = '',
    # limits=c(-1, 9),
    sec.axis = sec_axis(~.*coeff, name = "")) 

Out:

enter image description here

Now I wondering if it's possible to set limits=c(-1, 9) for left y-axis and limits=c(-2, 20) for right y-axis simultaneously using ggplot2?

I try with code below, but right y-axis's changed to around limits=c(-2, 15), not as figure above. Alternative solutions to plot a similar dual y-axis plot will be welcomed as well.

ggplot()  
  geom_line(data = df_m1[!is.na(df_m1$value), ], aes(x = date, y = value, col = variable), alpha = 1, size = 1)  
  geom_line(data = df_m2[!is.na(df_m2$value), ], aes(x = date, y = value, col = variable), alpha = 1, size = 1)  
  scale_y_continuous(
    name = '',
    limits=c(-1, 9),
    sec.axis = sec_axis(~.*coeff, name = ""))

enter image description here

Reference:

https://r-graph-gallery.com/line-chart-dual-Y-axis-ggplot2.html

CodePudding user response:

This is one of the reasons ggh4x::help_secondary() was written. Essentially it figures out the transformation needed to get the secondary range to match the primary range and the inverse. We apply the transformation to secondary data and it's inverse is used as the trans argument of the secondary axis.

In your example, we can drop the mutate() bit, because that is handled in sec$proj().

(Disclaimer: I wrote ggh4x)

# df <- structure(...) # omitted for brevity

library(ggplot2)
library(dplyr)
library(reshape2)

df_m <- melt(df, id.vars = 'date')
df_m <- df_m %>% 
  mutate(date=as.Date(date))

df_m1 <- df_m %>% 
  filter(variable %in% c("value1", 'value3')) 

df_m2 <- df_m %>% 
  filter(variable %in% c("value2"))

sec <- ggh4x::help_secondary(
  name = "",
  primary = c(-1, 9), secondary = c(-2, 20),
)

ggplot()  
  geom_line(data = df_m1[!is.na(df_m1$value), ], 
            aes(x = date, y = value, col = variable), alpha = 1, size = 1)  
  geom_line(data = df_m2[!is.na(df_m2$value), ], 
            aes(x = date, y = sec$proj(value), col = variable), alpha = 1, size = 1)  
  scale_y_continuous(
    limits = c(-1, 9),
    name = '',
    sec.axis = sec) 

  • Related