Plot only one side/half of the violin plot

后端 未结 2 1029
说谎
说谎 2021-01-12 16:30

I would like to have only one half of violin plots (similar to the plots created by stat_density_ridges from ggridges). A MWE

library(ggplot2)

dframe = data         


        
相关标签:
2条回答
  • 2021-01-12 16:43

    Package see has also a function geom_violinhalf that seems to do exactly what you want (see right plot below). It behaves mostly like geom_violin(), except does not have all arguments geom_violin() has (missing for example draw_quantiles)

    library(ggplot2)
    library(see)
    
    
    p <- ggplot(mtcars, aes(factor(cyl), mpg))
    p1 <- p + geom_violin()+ ggtitle("geom_violin")
    p2 <- p + see::geom_violinhalf()+ ggtitle("see::geom_violinhalf")
    
    ## show them next to each other
    library(patchwork)
    p1+p2
    

    Created on 2020-04-30 by the reprex package (v0.3.0)

    0 讨论(0)
  • 2021-01-12 16:50

    There's a neat solution by @David Robinson (original code is from his gists and I did only a couple of modifications).

    He creates new layer (GeomFlatViolin) which is based on changing width of the violin plot:

    data <- transform(data, 
                      xmaxv = x,
                      xminv = x + violinwidth * (xmin - x))
    

    This layer also has width argument.


    Example:

    # Using OPs data
    # Get wanted width with: geom_flat_violin(width = 1.5)
    ggplot(dframe, aes(group, val)) +
        geom_flat_violin()
    

    Code:

    library(ggplot2)
    library(dplyr)
    
    
    "%||%" <- function(a, b) {
      if (!is.null(a)) a else b
    }
    
    geom_flat_violin <- function(mapping = NULL, data = NULL, stat = "ydensity",
                            position = "dodge", trim = TRUE, scale = "area",
                            show.legend = NA, inherit.aes = TRUE, ...) {
      layer(
        data = data,
        mapping = mapping,
        stat = stat,
        geom = GeomFlatViolin,
        position = position,
        show.legend = show.legend,
        inherit.aes = inherit.aes,
        params = list(
          trim = trim,
          scale = scale,
          ...
        )
      )
    }
    
    GeomFlatViolin <-
      ggproto("GeomFlatViolin", Geom,
              setup_data = function(data, params) {
                data$width <- data$width %||%
                  params$width %||% (resolution(data$x, FALSE) * 0.9)
    
                # ymin, ymax, xmin, and xmax define the bounding rectangle for each group
                data %>%
                  group_by(group) %>%
                  mutate(ymin = min(y),
                         ymax = max(y),
                         xmin = x - width / 2,
                         xmax = x)
              },
    
              draw_group = function(data, panel_scales, coord) {
                # Find the points for the line to go all the way around
                data <- transform(data, 
                                  xmaxv = x,
                                  xminv = x + violinwidth * (xmin - x))
    
                # Make sure it's sorted properly to draw the outline
                newdata <- rbind(plyr::arrange(transform(data, x = xminv), y),
                                 plyr::arrange(transform(data, x = xmaxv), -y))
    
                # Close the polygon: set first and last point the same
                # Needed for coord_polar and such
                newdata <- rbind(newdata, newdata[1,])
    
                ggplot2:::ggname("geom_flat_violin", GeomPolygon$draw_panel(newdata, panel_scales, coord))
              },
    
              draw_key = draw_key_polygon,
    
              default_aes = aes(weight = 1, colour = "grey20", fill = "white", size = 0.5,
                                alpha = NA, linetype = "solid"),
    
              required_aes = c("x", "y")
    )
    
    0 讨论(0)
提交回复
热议问题