rggplot2

Preserving box plot whiskers when mixing box and violin plots in {ggplot2}


Preamble

Let's say I have a function that allows users to create a boxviolin plot where box and violin plots are superposed.

library(ggplot2)

df <- dplyr::filter(mpg, class %in% c("compact", "midsize"))

p1 <- ggplot(df, aes(class, hwy)) + geom_violin() + geom_boxplot() 

p1

I also want to allow users to remove the violin plot if they want to and the way I support this is by asking users to set the width of the violin plot to 0.


p2 <- ggplot(df, aes(class, hwy)) + geom_violin(width = 0) + geom_boxplot() 

p2

Created on 2023-12-10 with reprex v2.0.2

Problem

If you notice carefully, the box plot whiskers between the two plots have changed. I think this is because the violin plot increases the range of data by interpolating data, which makes the outlier points no longer outliers.

How can I avoid this? That is, how can I allow users to make the violin plot disappear without affecting the boxplot whiskers?

N.B. I am aware that I can just offer a parameter like plot.type and then add the violin layer based on its argument. But this is not the API I prefer for other reasons.


Solution

  • It's not the box plot whiskers which change. When you set width=0 the violin collapses to a flat line. To avoid that or to remove it completely set linewidth=0 too:

    library(ggplot2)
    
    df <- dplyr::filter(mpg, class %in% c("compact", "midsize"))
    
    ggplot(df, aes(class, hwy)) +
      geom_violin(width = 0, linewidth = 0) +
      geom_boxplot()