Alex
Alex

Reputation: 2780

Make a new scale in ggplot2

I have the following plot which plots quantile labels and breaks. This is currently done manually.

library(tidyverse)
mtcars %>% 
  as_tibble() %>%
     ggplot(aes(y = mpg, x = hp, color = factor(cyl))) +
     geom_point() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  scale_y_continuous(labels = as.numeric(quantile(mtcars$mpg)),
                     breaks = as.numeric(quantile(mtcars$mpg))) +
  scale_x_continuous(labels = as.numeric(quantile(mtcars$hp)),
                     breaks = as.numeric(quantile(mtcars$hp))) 

Created on 2018-08-28 by the reprex package (v0.2.0).

I would like to make a function which does this with any dataset. This was one attempt

scale_y_quantile <- function(y){
  ggplot2::scale_y_continuous(labels = as.numeric(quantile(y)))
}

I then tried to use it as follows.

mtcars %>% 
  as_tibble() %>%
     ggplot(aes(y = mpg, x = hp, color = factor(cyl))) +
     geom_point() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank()) +
  scale_y_quantile()
but I get the following error

Error in quantile(y) : object 'y' not found

The problem is I don't know how to access the data that is passed to aes().

Upvotes: 2

Views: 763

Answers (1)

Maurits Evers
Maurits Evers

Reputation: 50668

I'm sure this can still be optimised but here is a start:

  1. Define a function quantile_breaks to return the breaks based on quantile(val)

    # Define function for quantile breaks based on val
    quantile_breaks <- function(val, prob) {
        function(x) as.numeric(quantile(val, prob))
    }
    
  2. Define the transformation function with scales::trans_new with breaks defined by quantile_breaks.

    quantile_trans <- function(val, prob) {
        scales::trans_new(
            name = "quantile",
            transform = function(x) x,
            inverse = function(x) x,
            breaks = quantile_breaks(val, prob))
    }
    
  3. Define 2 new scale_*_quantile position scales.

    scale_x_quantile <- function(val, prob = seq(0, 1, 0.25), ...) {
        scale_x_continuous(..., trans = quantile_trans(val, prob))
    }
    
    scale_y_quantile <- function(val, prob = seq(0, 1, 0.25), ...) {
        scale_y_continuous(..., trans = quantile_trans(val, prob))
    }
    

Let's test on mtcars:

mtcars %>%
    ggplot(aes(hp, mpg, colour = factor(cyl))) +
    geom_point() +
    scale_x_quantile(mtcars$hp) +
    scale_y_quantile(mtcars$mpg)

enter image description here

You can also change the quantiles that you want to show, e.g. to show all 20% (rather than the default 25% quartiles) you can do

mtcars %>%
    ggplot(aes(hp, mpg, colour = factor(cyl))) +
    geom_point() +
    scale_x_quantile(mtcars$hp, prob = seq(0, 1, 0.2)) +
    scale_y_quantile(mtcars$mpg, prob = seq(0, 1, 0.2))

enter image description here

Upvotes: 2

Related Questions