In ggplot2 , I want to plot boxplot+dotplot side by side

In ggplot2 , I want to plot boxplot+dotplot side by side as attached image. But the code can't work, anyone can help? this code from 'R graphic cookbook'. Thanks!

library(gcookbook)
library(tidyverse)
ggplot(heightweight,aes(x=sex,y=heightIn ))+
  geom_boxplot(aes(x=as.numeric(sex)+0.2),group=sex)+
  geom_dotplot(aes(x=as.numeric(sex)-0.2),group=sex,
               binaxis = "y",stackdir = 'center',
               binwidth = 0.5)

enter image description here


Solution 1:

This is a very interesting question. OP is looking to dodge geoms along the x axis, which is not typically difficult to do. The difficulty here lies in that you are dodging the same data using different geoms.

What you can do is use a bit of clever formatting, mapping, and faceting to recreate an example of the type of plot OP shows. For this example solution, I am using the built-in dataset, iris. In the future, OP, please be sure to provide a reproducible example using a built-in dataset, your data, or a sample of your data.

Here's the basic plot showing a dotplot on top of a box plot below - I'll be trying to split the boxplot on the right and dotplot on the left.

ggplot(iris, aes(x=Species, y=Sepal.Width)) +
  geom_boxplot(width=0.3) +
  geom_dotplot(binaxis = 'y', binwidth=0.04, stackdir = "center")

enter image description here

Dodging is the act of splitting an aesthetic across a specific geom according to a value of a particular column in your data frame. Basically, it means you can have two boxplots next to one another, two points, etc - each one colored or represented differently according to a value for another column in your data. We cannot use dodging to move the boxplot alongside the dotplot because dodging only works across the same geom. You can have two boxplots next to one another for the same specified value of x... but not a boxplot and a dotplot.

The solution here is to draw our geoms individually - effectively "manually" doing the dodging. I can't specify a segment within a specific x value (like "x right" vs. "x left"), so the only way to make this work in my mind is to use faceting to create the actual x positions in the dataset, and the positional information for the dodging is going to be specified in the plot using the x axis. This means each value in x (in this example, each Species) will be kind of a mini plot - dotplot on the left and boxplot on the right.

Here's the code and result:

ggplot(iris, aes(x=positional, y=Sepal.Width)) +
  geom_dotplot(aes(x = "1"), binaxis="y", binwidth=0.04, stackdir="center") +
  geom_boxplot(aes(x = "2"), width=0.6) +
  facet_wrap(~Species, strip.position = "bottom") +
  theme(
    # panel.spacing.x = unit(0, "npc"),
    strip.background = element_blank(),
    strip.text = element_text(size=12),
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    axis.title.x = element_blank()
  )

enter image description here

What's going on here? Well, you'll notice that I'm mapping x to a new "column" in the dataset called "positional". This column does not exist in the dataset, so I define it separately for geom_boxplot() and geom_dotplot(). You have to do this in aes(), since it's required for mapping, but if you map in aes() to a constant value, the plot will be created as if every observation is set at that value. This is useful, because this creates our dotplot on the left (where positional == "1" and our boxplot on the right (where positional == "2").

The rest of the code is just theme stuff and creating the facets. Note that I use strip.placement to move the facet labels to the bottom, then remove all the other axis elements so that our facet labels take the place as the new axis label.

Finally, you can either keep the spacing between the facets (I kind of like it), or you can also remove that by using another theme() element. Adding theme(panel.spacing = unit(0, "npc")) gives you:

enter image description here