ggplot2: plot gruped/nested split violins

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

ggplot2: plot gruped/nested split violins

Big Floppy Dog
Hi,

I posted this on StackOverflow also but did not get a response so I thought
that I would also try luck here. The post is at:

https://stackoverflow.com/questions/49120060/ggplot2-display-blocks-of-nested-split-violins

Basically, I have the following test example:

--cut-and-paste-from-here-on

df <- data.frame(dens = rnorm(5000),
             split = as.factor(sample(1:2, 5000, replace = T)),
             method = as.factor(sample(c("A","B"), 5000, replace = T))
             counts = sample(c(1, 10, 100, 1000, 10000), 5000, replace = T))

-stop-cut-and-paste-here


What i am wanting to do is to do split violin plots for splits 1 and 2
within groups A and B for each count (which would be in the logscale, but
that is not important for this example). We have four groups for each
setting but there is a nested aspect to it.

Here is what I have tried:


-start-cut-and-paste-again---

GeomSplitViolin <- ggproto("GeomSplitViolin", GeomViolin,

  draw_group = function(self, data, ..., draw_quantiles = NULL){
    # By @YAK: https://stackoverflow.com/questions/35717353/split-violin-plot-with-ggplot2
    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 <- create_quantile_segment_frame(data, draw_quantiles,
split = TRUE, grp = grp)
      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, ...))
    }
  })

create_quantile_segment_frame <- function (data, draw_quantiles, split
= FALSE, grp = NULL) {
  dens <- cumsum(data$density)/sum(data$density)
  ecdf <- stats::approxfun(dens, data$y)
  ys <- ecdf(draw_quantiles)
  violin.xminvs <- (stats::approxfun(data$y, data$xminv))(ys)
  violin.xmaxvs <- (stats::approxfun(data$y, data$xmaxv))(ys)
  violin.xs <- (stats::approxfun(data$y, data$x))(ys)
  if (grp %% 2 == 0) {
    data.frame(x = ggplot2:::interleave(violin.xs, violin.xmaxvs),
               y = rep(ys, each = 2), group = rep(ys, each = 2))
  } else {
    data.frame(x = ggplot2:::interleave(violin.xminvs, violin.xs),
               y = rep(ys, each = 2), group = rep(ys, each = 2))
  }}



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, ...))}


ggplot(df, aes(x = factor(counts), y = dens, fill =
interaction(split,method))) +
           geom_split_violin(draw_quantiles = c(0.25, 0.5, 0.75)) +
scale_fill_manual(values=RColorBrewer::brewer.pal(name="Paired",n=4))
+ theme_light() + theme(legend.position="bottom")


--stop-cut-and-paste-again---

Now, I  almost get what i want but for the fact that the two split violins
for a "Count" end up on top of the other. What I want is them to be next to
each other and separated from the values for the other "Counts".

In other words, what I want is really the light blue and the dark blue to
be the two halves of a split violin plot and the light green and the dark
green to be the two halves of another split violin plot and these plots
should be bunched together.

Let me know if something is not clear, sorry for that.

As I mentioned, I also posted on SO, and I will keep both fora updated if I
get a good answer in either (unless someone else also posts there
directly).

TIA for any suggestions!

BFD

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.