0

In attempting to place a border around geom_point()s in ggplot2, this question was an extremely helpful starting point. I ended up opting for the approach outlined by @joran (the second answer) because it allows me to get smaller outlines (this is preferable since these points will end up being quite small in the final product).

However, I am having trouble employing a slight variation of this technique. I have two different shape varieties to outline and this is causing me some problems. Here is a reproducible example:

require('ggplot2')

values <- rnorm(n = 10, mean = 1, sd = 0.5) + c(1:10)

df <- data.frame(id = rep(c('hq', 'lq'), each = 5),
                 values = values,
                 period = rep(c(1:5), 2))

plot <- 
    ggplot(df, aes(x = period,
                   y = values,
                   group = id)) +
    geom_line(color = 'gray40') +
    geom_point(aes(shape = id,
                   color = id),
               size = 3) +
    geom_point(aes(shape = id),
               color = 'gray70',
               size = 3,
               show_guide = FALSE) +
    scale_color_manual(values = c('lightskyblue1', 'lightpink'),
                       labels = c('HQ', 'LQ')) +
    scale_shape_manual(values = c(15, 16, 0, 1),
                       labels = c('HQ', 'LQ')) +
    theme_bw()

This ends up giving a plot that looks like this:

enter image description here

I cannot understand why the points become solid gray (this is the color I'd like the border around the blue and pink points to be). Any help in retaining the blue and pink fill with a gray border matching the shape type would be greatly appreciated!

EDIT: In case it was not clear, I know that I can use pch 21-25 to achieve a border. However, these borders are too large and their thickness is not modifiable (as far as I know). As a result I am trying to layer two geom_points()s, one filled and the other unfilled, to achieve the appearance of a border. I can sort of accomplish this by layering another set of geom_point()s like this:

plot <- 
    ggplot(df, aes(x = period,
                   y = values,
                   group = id)) +
    geom_line(color = 'gray40') +
    geom_point(aes(shape = id,
                   color = id),
               size = 3) +
    geom_point(shape = 1,
               color = 'gray70',
               size = 3,
               show_guide = FALSE) +
    scale_color_manual(values = c('lightskyblue1', 'lightpink'),
                       labels = c('HQ', 'LQ')) +
    scale_shape_manual(values = c(15, 16),
                       labels = c('HQ', 'LQ')) +
    theme_bw()

The problem here is that each "border" does not match the corresponding shape, and the resulting image looks like this:

enter image description here

Community
  • 1
  • 1
thagzone
  • 355
  • 1
  • 3
  • 13
  • Only point character (pch) 21 to 25 support both fill and colour. You need to use these symbols, and specify a fill aesthetic. – Matthew Plourde Mar 18 '15 at 18:23
  • @MatthewPlourde I understand that I can use pch 21 to 25, but the border around those points is too large given that, as I mention, the final product will have very small points. What I am trying to do is layer two geom_point()s to achieve the same effect--one solid and one filled so that I achieve the appearance of a border. – thagzone Mar 18 '15 at 18:31
  • you need to include `aes(shape = id)` also in the first `geom_point` otherwise `R` doesn't know which shape that gray points need to have. Check my edited answer – mucio Mar 18 '15 at 18:41

3 Answers3

2

Actually, you don't have to use so many aes's. Only one aes in ggplot is sufficient. The aes will automatically be applied to the other geom_.. unless it is overrided.

plot <- 
  ggplot(df, aes(x = period, y = values, group = id, shape = id, color=id)) +
  geom_line(color = 'gray40') +
  geom_point(color = 'gray70', size = 5, show_guide = FALSE) +
  geom_point(size = 3) +
  scale_color_manual(values = c('lightskyblue1', 'lightpink'), labels = c('HQ', 'LQ')) +
  scale_shape_manual(values = c(15, 16, 0, 1), labels = c('HQ', 'LQ')) +
  theme_bw()

enter image description here

Randy Lai
  • 3,084
  • 2
  • 22
  • 23
  • Many thanks! You're right about cleaning up the `aes` garbage--I'm getting the hang of `ggplot` as I go so I appreciate your suggestion. – thagzone Mar 18 '15 at 18:54
  • actually, you could also remove `group = id`, since `color` and `shape` will tell the group. – Randy Lai Mar 18 '15 at 18:56
1

Edited

I missread your question. Anyway the solution is in the order of the geom_point:

  1. the first one goes in the background, so I use it to draw bigger gray shapes (size 5)

    geom_point(aes(shape = id),
               color = 'gray70',
               size = 5,
               show_guide = FALSE)
    
  2. the second one draws the colored shapes:

    geom_point(aes(shape = id,
                   color = id),
               size = 3)
    

Whit this:

plot <- 
  ggplot(df, aes(x = period,
                 y = values,
                 group = id)) +
  geom_line(color = 'gray40') +
  geom_point(aes(shape = id),
             color = 'gray70',
             size = 5,
             show_guide = FALSE) + 
  geom_point(aes(shape = id,
                 color = id),
             size = 3) +
  scale_color_manual(values = c('lightskyblue1', 'lightpink'),
                     labels = c('HQ', 'LQ')) +
  scale_shape_manual(values = c(15, 16, 0, 1),
                     labels = c('HQ', 'LQ')) +
  theme_bw()

Whit this code I can achieve this result: enter image description here

mucio
  • 7,014
  • 1
  • 21
  • 33
0

Just saw that @mucio has updated his answer - I think his answer is a little bit more elegant, though this way may give you thin borders that you want.


Since you're using id for the shape legend both times, both the colorful shapes and the grey shapes are both filled. One way around this (admittedly a hack, but that doesn't seem to have stopped you so far), is to create a new variable with new levels and use that for the shape in the grey shapes. (Also note that I had to rearrange the order of the shape specifications).

df$id2 <- ifelse(df$id == 'hq', 'hq_alt', 'lq_alt')
plot <- 
    ggplot(df, aes(x = period,
                   y = values,
                   group = id)) +
    geom_line(color = 'gray40') +

     geom_point(aes(shape = id,
                   color = id),
               size = 3) +
    geom_point(aes(shape = id2),
               color = 'gray70',
               size = 3,
               show_guide = FALSE) +
    scale_color_manual(values = c('lightskyblue1', 'lightpink'),
                       labels = c('HQ', 'LQ')) +
    scale_shape_manual(values = c(15, 0, 16, 1),
                       labels = c('HQ', 'LQ'), guide=F) +
    theme_bw()
GregF
  • 1,292
  • 11
  • 14
  • Haha you are right that I don't mind a hack at the moment--just trying to get it done for the time being. Creative workaround, but you're right, not as elegant as @mucio's solution. – thagzone Mar 18 '15 at 18:55