Search code examples
rggplot2plotaxis-labels

ggplot sec_axis can I use a vector for the trans formula?


I would like to label the x-axis with two non-linearly but monotonically related values. One is a decile and the other is a mean within the decile. Since there is no algebraic transformation between the two, I though I might use a vector but ggplot gives me an error message that the transformation must be monotonic. How is it checking and is there way to do what I trying?

df <- 
  data.frame(x = rnorm(500)) %>%
  mutate(y = 1 + 2 * x) %>%
  arrange(x) %>% 
  mutate(decile = ceiling(10 * 1:500 / 500)) %>% 
  group_by(decile) %>%
  summarize(x = mean(x), y = mean(y))

 sec_axis_label = df$x

df %>% 
  ggplot(aes(decile, y)) +
  scale_x_continuous(expand = c(0, 0), 
    sec.axis = sec_axis(~ sec_axis_label[.])) +  
   geom_point() 

Solution

  • The sec_axis requires a transformation (formula), not a lookup. To see what's going on, I'll change it as follows:

    sec_axis_label <- function(z) { browser() ; 1 ; }
    df %>% 
       ggplot(aes(decile, y)) +
       scale_x_continuous(expand = c(0, 0), 
         sec.axis = sec_axis(~ sec_axis_label(.))) +  
       geom_point() 
    

    We can explore and see what's going on:

    # Browse[2]> 
    str(z)
    #  num [1:1000] 1 1.01 1.02 1.03 1.04 ...
    # Browse[2]> 
    range(z)
    # [1]  1 10
    # Browse[2]>
    Q
    

    It's giving you enough potential values so that the transformation will return a meaningful range of other-axis-values so that it determine the best pretty-numbers.

    So we need a transformation of sorts. If we assume interpolation is safe (I don't know if it is safe for you), we can do:

    sec_axis_label <- function(a) approx(df$decile, df$x, xout=a)$y
    df %>% 
      ggplot(aes(decile, y)) +
      scale_x_continuous(expand = c(0, 0), 
        sec.axis = sec_axis(~ sec_axis_label(.))) +  
       geom_point() 
    

    simple decile/sec_axis plot