Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a ggplot '+'-pipeable function that can refer to the input plot

Tags:

r

ggplot2

ggproto

I'm trying to write a function that can be called using the '+'-based ggplot2 syntax.

myplot + myfunction

Specifically, the function I'm writing symmetrizes the y-axis about zero, so it needs to determine the y-axis range for the input plot.

So let,

ylim_sym <- function(p){
    get_y_range <- function(p){
        ggplot2::ggplot_build(p)$layout$panel_ranges[[1]]$y.range
        }
    max_offset <- max(abs(get_y_range(p)))
    p + ylim(- max_offset, max_offset)
}

With this function, the following works:

qplot(x = 1:10, y = exp(rnorm(10))) %>% ylim_sym()

But this doesn't work because of some precedence issue between +.gg and %>%:

qplot(x = 1:10, y = exp(rnorm(10))) +
    geom_abline(slope = 0) %>%
    ylim_sym()

(I could write the latter (all_my_ggplot_pipeline) %>% ylim_sym() but it's pretty ugly syntax).

Ideally, I'd like to be able to write ylim_sym such that it can be piped like so,

qplot(x = 1:10, y = exp(rnorm(10))) + ylim_sym()

but I can't work out how to access the plot on the LHS of + within ylim_sym

Any ideas?

like image 416
Russ Hyde Avatar asked Dec 13 '17 17:12

Russ Hyde


1 Answers

I was able to solve it by doing the following.

StatSymYLim <- ggproto(
  "StatSymYLim", Stat, 
  compute_group = function(data, scales) {
    out <- data.frame(
      x = median(data$x),
      y = c(-1, 1) * max(abs(data$y))
      )
    out
    },
    required_aes = c("x", "y")
  )

ylim_sym <- function(...){
  geom_blank(..., stat = StatSymYLim)
  }

Then the following works as required:

qplot(x = 1:10, y = exp(rnorm(10))) +
  geom_abline(slope = 0) +
  ylim_sym()

My understanding of ggplot2 internals is pretty shaky to be fair, so this might be a naive solution.

like image 67
Russ Hyde Avatar answered Nov 09 '22 08:11

Russ Hyde