2

I am trying to use a function from this post to produce a split violin plot.

Here's the code:

GeomSplitViolin <- ggproto("GeomSplitViolin", GeomViolin, draw_group = function(self, data, ..., draw_quantiles = NULL){
  data <- transform(data, xminv = x - violinwidth * (x - xmin), xmaxv = x + violinwidth * (xmax - x))
  grp <- data[1,'group']
  newdata <- plyr::arrange(transform(data, x = if(grp%%2==1) xminv else xmaxv), if(grp%%2==1) y else -y)
  newdata <- rbind(newdata[1, ], newdata, newdata[nrow(newdata), ], newdata[1, ])
  newdata[c(1,nrow(newdata)-1,nrow(newdata)), 'x'] <- round(newdata[1, 'x']) 
  if (length(draw_quantiles) > 0 & !scales::zero_range(range(data$y))) {
    stopifnot(all(draw_quantiles >= 0), all(draw_quantiles <= 
                                              1))
    quantiles <- ggplot2:::create_quantile_segment_frame(data, draw_quantiles)
    aesthetics <- data[rep(1, nrow(quantiles)), setdiff(names(data), c("x", "y")), drop = FALSE]
    aesthetics$alpha <- rep(1, nrow(quantiles))
    both <- cbind(quantiles, aesthetics)
    quantile_grob <- GeomPath$draw_panel(both, ...)
    ggplot2:::ggname("geom_split_violin", grid::grobTree(GeomPolygon$draw_panel(newdata, ...), quantile_grob))
  }
  else {
    ggplot2:::ggname("geom_split_violin", GeomPolygon$draw_panel(newdata, ...))
  }
})

geom_split_violin <- function (mapping = NULL, data = NULL, stat = "ydensity", position = "identity", ..., draw_quantiles = NULL, trim = TRUE, scale = "area", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) {
  layer(data = data, mapping = mapping, stat = stat, geom = GeomSplitViolin, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(trim = trim, scale = scale, draw_quantiles = draw_quantiles, na.rm = na.rm, ...))
}

Here's an example dataset:

set.seed(20160229)
my_data = data.frame(
  y=c(rnorm(1000), rnorm(1000, 0.5), rnorm(1000, 1), rnorm(1000, 1.5)),
  x=c(rep('a', 2000), rep('b', 2000)),
  m=c(rep('i', 1000), rep('j', 2000), rep('i', 1000))
)

Use aes() in the plot function, we could get the normal plot

ggplot(my_data, aes(x, y, fill=m)) + geom_split_violin()

enter image description here

But if use aes_string(), I got a weird plot where two split violin on top of each other.

ggplot(my_data, aes_string(x='x', y='y', fill='m')) + geom_split_violin()

enter image description here

I do not have a good understanding of how aes vs. aes_string might lead to this difference. Hope someone could give me a hint. Thanks a lot!

Xiao
  • 36
  • 5
  • Looks like the problem is the `grp%%2==1` check. You seem to be assuming that the groups are going to be in some order? The `aes()` and `aes_string()` versions do seem to return the groups in a different order of some reason, but plotting shouldn't depend on that ordering. How do you want to decide which one is supposed to go on the left and which is supposed to go on the right? – MrFlick Oct 04 '18 at 19:59
  • I don't have specific preference which one go to the left and right. But in this case, seems like both went to the left. Do you know how was that controlled? – Xiao Oct 04 '18 at 20:06
  • 4
    There is an open issue on the github repo about this. See [here](https://github.com/tidyverse/ggplot2/issues/2896) – aosmith Oct 04 '18 at 20:07
  • Wow, that's exactly same question. Thanks! – Xiao Oct 04 '18 at 20:08

1 Answers1

1

Instead of using aes_string(), you can use tidy evaluation approach in ggplot2 3.0 or higher by:

  • converting the input strings to variables using sym()

  • unquote them inside aes() using !! so they get evaluated

library(tidyverse)

ggplot(my_data, aes(x = !!sym('x'), y = !!sym('y'), fill = !!sym('m'))) + geom_split_violin()

Created on 2018-10-04 by the reprex package (v0.2.1.9000)

Tung
  • 26,371
  • 7
  • 91
  • 115
  • 1
    Thanks, this works! Still trying to understand under the hood why they produce different behaviors. – Xiao Oct 04 '18 at 20:53
  • I recommend not using `aes_string` as it was deprecated when tidy evaluation was made possible in ggplot2. I don't think they will spend that much time improving it anymore – Tung Oct 04 '18 at 21:09