This is driving me up the wall, and I'm sure I am missing something simple. Any help would be appreciated.
I want the red and blue points to be separated, with each set over the corresponding boxplot as in second image, but with a numeric x axis as in the first image.
df <- data.frame(x = rep(c(1, 2, 10), each = 20),
g = rep(c("A", "B"), times = 30),
y = c(rnorm(60, 0, 1)))
# OK - boxplot by x and g
ggplot(df, aes(y = y, x = x, fill = g, color = g, group = interaction(x, g))) +
geom_boxplot()
# Not OK. The dots are only grouped by x, not g
ggplot(df, aes(y = y, x = x, fill = g, color = g, group = interaction(x, g))) +
geom_point()
# I want the points to correctly overlay the boxplots
ggplot(df, aes(y = y, x = x, fill = g, color = g, group = interaction(x, g))) +
geom_boxplot(alpha = 0.1) +
geom_point()

(I have fixed it by faceting on x, but I want the axis as numeric to reflect the correct scaling)
ggplot(df, aes(y = y, x = g, fill = g, color = g, group = interaction(x, g))) +
geom_boxplot(alpha = 0.1) +
geom_point() +
facet_wrap(~x)

You can use position=position_dodge(...) in geom_point.
ggplot(df, aes(y = y, x = x, fill = g, color = g, group = interaction(x, g))) +
geom_boxplot(alpha = 0.1, width=0.75) +
geom_point(position = position_dodge(width=0.75))
I also defined a width for geom_boxplot to match the position_dodge width in geom_point.

In case you want jitter as well, you can use position_jitterdodge.
ggplot(df, aes(y = y, x = x, fill = g, color = g, group = interaction(x, g))) +
geom_boxplot(alpha = 0.1, width=0.75) +
geom_point(position = position_jitterdodge(jitter.width=0.85))
If using geom_beeswarm, you can use the dodge.width option
library(ggbeeswarm)
ggplot(df, aes(y = y, x = x, fill = g, color = g, group = interaction(x, g))) +
geom_boxplot(alpha = 0.1, width = 0.75) +
geom_beeswarm(dodge.width = 0.75)

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With