Home > Net >  R selective ggplot geom_point(position = position_dodge())
R selective ggplot geom_point(position = position_dodge())

Time:02-18

In the plot below, I'd like to dodge the colored shapes, but not the shape for 1 so that the dotted line would line up with 1. The colored shapes need to be dodged so that they don't overlap one another for being at the same time point. Here's the code for generating dummy data and the plot. Is there a way to selectively dodge points that are in the same geom_point?

df <- data.frame(id = factor(sort(rep(seq(1,5),2))),
                 time = rep(c(3,6), 5),
                 cat1 = c(sample(c('good', 'ok', 'bad'), 2),
                          sample(c('good', 'ok', 'bad'), 2),
                          sample(c('good', 'ok', 'bad'), 2),
                          sample(c('good', 'ok', 'bad'), 2),
                          sample(c('good', 'ok', 'bad'), 2)),
                 cat2 = c(sample(c('a', 'b', 'c', 'd'), 2), 
                          sample(c('a', 'b', 'c', 'd'), 2),
                          sample(c('a', 'b', 'c', 'd'), 2),
                          sample(c('a', 'b', 'c', 'd'), 2),
                          sample(c('a', 'b', 'c', 'd'), 2))) %>%
  pivot_longer(cols = c('cat1', 'cat2'), names_to='type', names_prefix = 'value', values_to = 'value') %>%
  plyr::rbind.fill(data.frame(id = factor(seq(1,5)), 
        time = 9,
        time2 = 9,
        type = 'off',
        value = c(1, NA, NA, 1, 1))) %>%
  dplyr::arrange(id, time)

ggplot(df, aes(x = id, y = time))  
  geom_point(aes(id, time, colour = value, shape = value), size = 2, position = position_dodge(width = 0.7))  
  geom_segment(data = df[df$type == 'off',], aes(x = id, xend = id, y = 6,
                                          yend = time2), colour = 'black', linetype = 'dotted')  
  coord_flip()  
  scale_shape_manual(values = c(13, 17, 17, 17, 17, 16, 16, 16, 15, 15, 15, 15))  
  scale_colour_manual(values = c('black', 'purple', 'green', '#ffff66', 'red',
                                 'green', '#ffff66', 'red', 
                                 'green', '#ffff66', 'pink'))  
  guides(fill = guide_legend(order = 2), shape = guide_legend(override.aes = list(size = 3)))

enter image description here

CodePudding user response:

One way to prevent one (or more) points from dodging would be to have two geom_point() calls: one which is dodged (and excludes the point(s) in question, and one that is not dodged and includes the point

ggplot(df, aes(x = id, y = time))  
  geom_point(
    data = subset(df, value=="1"),
    aes(colour = value, shape = value), size = 2)  
  geom_point(
    data = subset(df, value!="1"),
    aes(colour = value, shape = value),
    size = 2, position = position_dodge(width = 0.7))  
  
  geom_segment(data = df[df$type == 'off',], aes(x = id, xend = id, y = 6,
                                                 yend = time2), colour = 'black', linetype = 'dotted')  
  coord_flip()  
  scale_shape_manual(values = c(13, 17, 17, 17, 17, 16, 16, 16, 15, 15, 15, 15))  
  scale_colour_manual(values = c('black', 'purple', 'green', '#ffff66', 'red',
                                 'green', '#ffff66', 'red', 
                                 'green', '#ffff66', 'pink'))  
  guides(fill = guide_legend(order = 2), shape = guide_legend(override.aes = list(size = 3)))

enter image description here

As long as the data is mapped the same way, they will appear in the legend the same way as you have it originally. Also note that there's no need to include the x and y again in aes() for geom_point(). You can specify there, but if you do not, the function will look to the globally-defined mappings (i.e. those within ggplot(aes(...)) for the assignment.

  • Related