r/ggplot - Use position_jitterdodge without a fill aesthetic

10.7k views Asked by At

Using geom_point with position_jitterdodge works only when you set the fill aesthetic. I can't see why this should be!

This command

library(ggplot2)
ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, color = clarity)) +
  geom_point(shape = 21, position = position_jitterdodge())

Produces an error:

Error: position_jitterdodge requires the following missing aesthetics: fill

This works, though:

ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, fill = clarity)) +
  geom_point(shape = 21, position = position_jitterdodge())

enter image description here

Simply supplying a NA value to fill isn't a viable workaround:

ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, color = clarity, fill=NA)) +
  geom_point(shape = 21, position = position_jitterdodge())

> Error in seq.default(h[1], h[2], length = n) : 
  'to' cannot be NA, NaN or infinite

Although it works if you specify an arbitrary constant (forgive the hideous results):

ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, color = clarity, fill='constant')) +
  geom_point(shape = 21, position = position_jitterdodge())

enter image description here

Any ideas on how to use jitter/dodge without specifying fill? (i.e. colored points only)

Edit: further to @joran's comment, I'd like to overlay points over boxplots. Since one isn't necessarily going to use fill to differentiate boxplots, it would be great if geom_point(position=position_jitterdodge()) accommodated plots without fill. Maybe not currently possible, though...

#This doesn't work:
ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, color = clarity)) +
  geom_boxplot() +
  geom_point(shape = 21, position = position_jitterdodge())

#This does, although obviously no one wants a plot like this
ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, color = clarity, fill='constant')) +
  geom_boxplot() +
  geom_point(shape = 21, position = position_jitterdodge())

#This is way it's intended to work, but marries you to 'fill'
ggplot(diamonds[ sample(nrow(diamonds), 1000), ], 
       aes(x = cut, y = carat, fill = clarity)) +
  geom_boxplot() +
  geom_point(shape = 21, position = position_jitterdodge())
1

There are 1 answers

1
arvi1000 On BEST ANSWER

Ok, here's my workaround. Specify fill along with the aesthetic you really want (color in my case), then blank out fill with scale_fill_manual

I produced a different fake dataset that's more similar to my actual use case, since the diamonds data as specified above isn't really a good candidate for box + points

my_dat <- data.frame(class=factor(rep(1:2, 600)),
                     y=rnorm(1200)),
                     x=rep(letters[1:3], each=400))

ggplot(my_dat, aes(x=x, y=y, fill=class, color=class)) +
  geom_boxplot(outlier.shape = NA) +
  geom_point(shape = 21, alpha = 0.5, position=position_jitterdodge()) +
  scale_fill_manual(values = rep(NA, 2)) 

enter image description here