Manual outlier plotting in grouped boxplot with jittered points

Viewed 147

I am trying to create a grouped boxplot with added jittered points where the outliers are not duplicated. I only found loads of solutions for non-grouped boxplots, but they don't apply to grouped boxplots.

The initial problem with duplicated outliers

When you combine a geom_boxplot with geom_point, outliers are duplicated because they are plotted in both geoms.

Here's an example. I coloured the outliers red and as you can clearly see, each data point is duplicated by geom_point (with a bit of jitter).

I am using part of the diamonds data:

example_data <- 
  diamonds %>% 
  filter(
    carat < .6,
    cut %in% c("Fair", "Very Good", "Ideal"),
    clarity %in% c("I1", "SI2", "IF")
  )
example_data %>% 
  ggplot(aes(y = price, x = cut, colour = clarity)) +
  geom_point(position = position_jitterdodge()) +
  geom_boxplot(fill = NA, outlier.colour = "red", outlier.size = 2) +
  theme_classic() +
  theme(legend.position = "top")

enter image description here

Manual outlier plotting moves the points

The solution is to remove the outliers in the boxplot using outlier.shape = NA, while using aesthetics to code the outliers in geom_point (as I e.g. did here using shape). That works fine when the boxplot is not grouped, but when it is, all of the points are in the wrong place because the outliers create another group in the plot:

example_data %>% 
  group_by(cut, clarity) %>%
  mutate(outlier = price > median(price) + IQR(price) * 1.5) %>%
  
  ggplot(aes(y = price, x = cut, colour = clarity)) +
  geom_point(
    aes(shape = outlier, size = outlier),
    position = position_jitterdodge()
  ) +
  geom_boxplot(outlier.shape = NA, fill = NA) +
  scale_size_manual(values = c(1, 2)) +
  theme_classic() +
  theme(legend.position = "top")

enter image description here

Does anyone know how to fix this? Thanks in advance for any help!

1 Answers

An answer was provided in a very helpful comment by @Cucumiiis on Twitter, which I want to share here.

The solution is to create the boxplots as you normally would, and then use a second data set where the outliers are removed for the points. The code then looks like this:

without_outliers <- example_data %>% 
  group_by(cut, clarity) %>% 
  mutate(outlier = ifelse(price > median(price) + IQR(price) * 1.5, TRUE , FALSE)) %>% 
  filter(!outlier)


example_data %>% 
  ggplot(aes(y = price, x = cut, colour = clarity)) +
  geom_point(
    data = without_outliers,
    position = position_jitterdodge()
  ) +
  geom_boxplot(fill = NA, outlier.colour = "red") +
  theme_classic() +
  theme(legend.position = "top") +
  scale_shape_manual(values = c(NA, 25))

enter image description here

Related