3

I'm looking to add point data so that is sits adjacent to a box in a boxplot. When I run the below example, the point data does not separate for each fill factor. Is there a way to add point data that corresponds to the box when plotting 2 factors in a boxplot? Can this also be added so it is to the left of the box rather than on top of it?

Group<-c("A","A","A","A","A","A","A","A","B","B","B","B","B","B","B","B")
Value<-c(2,1,6,4,2,5,3,6,8,1,10,2,2,3,4,6)
Round<-c("Round 1","Round 1","Round 1","Round 1","Round 2","Round 2","Round 2","Round 2",
     "Round 1","Round 1","Round 1","Round 1","Round 2","Round 2","Round 2","Round 2")
data<-as.data.frame(cbind(Group,Value,Round))
data$Round<-as.factor(data$Round)
data$Group<-as.factor(data$Group)
data$Value<-as.numeric(data$Value)
str(data)

ggplot(data,aes(Group,Value,fill=Round))+
 geom_point(aes(colour=Round))+
 geom_boxplot(width=0.5,position = position_dodge(width=0.7))+
 labs(fill= "Round",x="Group",y="Value")

enter image description here

tjebo
  • 21,977
  • 7
  • 58
  • 94

2 Answers2

4

You can use position_dodge for the points like this:

ggplot(data,aes(Group,Value,fill=Round))+
  geom_point(aes(colour=Round), position = position_dodge(width = 0.9))+
  geom_boxplot(width=0.5,position = position_dodge(width=0.7))+
  labs(fill= "Round",x="Group",y="Value")

Output:

enter image description here

You can change the width to change the position of the points.

Another option is using position_jitterdodge():

ggplot(data,aes(Group,Value,fill=Round))+
  geom_point(aes(colour=Round), position = position_jitterdodge())+
  geom_boxplot(width=0.5,position = position_dodge(width=0.7))+
  labs(fill= "Round",x="Group",y="Value")

Output:

enter image description here

Quinten
  • 35,235
  • 5
  • 20
  • 53
  • 1
    To add up to this answer: If you want to see the points that lie in the boxplot area, you could use alpha in your boxplot() environment `geom_boxplot(width=0.5,position = position_dodge(width=0.7), alpha = 0.5)` – U_jex May 26 '22 at 09:04
4

As Quinten says, one would normally use position_dodge here. However, you can't do this if you want the points to sit just to the left of the boxes, since you would need to nudge and dodge at the same time, which isn't currently an option. Instead, you can nudge the groups in two layers:

ggplot(data,aes(Group, Value, fill = Round)) +
 Map(\(r, d) geom_point(aes(colour = Round), subset(data, Round == r),
    position = position_nudge(x=d)), r = levels(data$Round), d = c(-0.33,0.02)) +
 geom_boxplot(width = 0.5, position = position_dodge(width = 0.7))

enter image description here

To have your points and boxes in the same color, use shape = 21 inside geom_point and remove the color = Round aesthetic mapping:

ggplot(data,aes(Group, Value, fill = Round)) +
  Map(function(r, d) geom_point(data = subset(data, Round == r),
      shape = 21, position = position_nudge(x = d), size = 2), 
      r = levels(data$Round), d = c(-0.33, 0.02)) +
  geom_boxplot(width = 0.5, position = position_dodge(width = 0.7)) +
  scale_fill_manual(values=c("#6699FF","#FFFFFF")) +
  theme_gray(base_size = 16)

enter image description here

Or, if you don't want the points outlined (but then the white points are difficult to see)

ggplot(data,aes(Group, Value, fill = Round)) +
  Map(function(r, d) geom_point(aes(color = Round), 
                       data = subset(data, Round == r),
                       position = position_nudge(x = d), size = 2), 
      r = levels(data$Round), d = c(-0.33, 0.02)) +
  geom_boxplot(width = 0.5, position = position_dodge(width = 0.7)) +
  scale_fill_manual(values=c("#6699FF","#FFFFFF")) +
  scale_color_manual(values=c("#6699FF","#FFFFFF")) +
  theme_gray(base_size = 16)

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • Allan, this is wicked code. I thought I knew R, and stand corrected. Would you care enlighten me (and maybe even lesser versed people) what "r" and "d" are?? I can see those are arguments to your anonymous function, but I don't see where you are specifying them. And what is `c(-0.33, 0.02)`? This unnamed argument seems so out of the blue. – tjebo May 26 '22 at 09:27
  • (I realise you haven't named any of the arguments in geom_point, that's probably why I'm confused. I would probably not recommend that (?) – tjebo May 26 '22 at 09:29
  • 1
    @tjebo you're right, I probably wouldn't use this code in production. It's now possible to add a `list` of layers to a ggplot, so you can add an `lapply` or a `Map` to a plot with `+` and it will work. I have used the `\(r, d)` as opposed to the `function(r, d)` for brevity. I should name the arguments in `Map` for clarity. The levels are needed to specify which group of points I'm dealing with in each iteration, and the `c(-0.33, 0.02)` are the amount of dodge each group needs to get them to sit to the left of their respective bars. – Allan Cameron May 26 '22 at 09:35
  • I knew about the list bit (I'm using this a lot to be honest) and the function replacement \ (not using at all, as I don't find this easy to read;) Thanks for clarifying the names - it seems like a miracle to me that the mapping was correct, given that you basically used `...` ? – tjebo May 26 '22 at 09:38
  • @tjebo the `...` are passed to the anonymous function in the order you write them, so if I have defined `\(r, d)`, the first item after the function will be interpreted as `r` and the second as `d`. – Allan Cameron May 26 '22 at 09:44
  • 1
    yeah, makes sense - I think I'm generally more on the cautious side and tend to explicitly name arguments, given lots of annoying bugs not only in package writing :) – tjebo May 26 '22 at 09:46
  • That worked thanks! I'd like to change the colour of my points so they match my boxes (See below). Is there anyway I can get scale_manual_colours to match the points as well as the boxes? ggplot(data,aes(Group, Value, fill = Round)) + Map(\(r, d) geom_point(aes(colour = Round), size=2,subset(data, Round == r), position = position_nudge(x=d)), r = levels(data$Round), d = c(-0.33,0.02)) + geom_boxplot(width = 0.4, position = position_dodge(width = 0.7))+ scale_fill_manual(values=c("#6699FF","#FFFFFF"))+ theme_bw() – Kelly James May 26 '22 at 11:33
  • You can change `color` to `fill` inside `geom_point` and use `shape = 21` _outside_ of `aes` in `geom_point` (otherwise your white points will be difficult to see). An alternative is to add `scale_color_manual(values=c("#6699FF","#FFFFFF"))` to your plot in addition to `scale_fill_manual(values=c("#6699FF","#FFFFFF"))`, though the white points will be hard to see. – Allan Cameron May 26 '22 at 11:37