2

I am trying to do something similar as in panel B of this figure: https://ars.els-cdn.com/content/image/1-s2.0-S0002929721000938-gr3_lrg.jpg, where I use shape as a legend, and closed or open shape as a second legend.

I use the following example data

example_data <- data.frame(x=c(1,3,2,4,5,3,1,2,3,2),
                           y=c(3,5,7,9,1,3,4,7,8,9),
                           color=c('col1','col2','col3','col1','col2','col3','col1','col2','col1','col3'),
                           shape=c('triangle','circle','triangle','circle','triangle','circle','triangle','circle','triangle','circle'),
                           openclosed=c('open','open','open','open','open','closed','closed','closed','closed','closed'))

I manage to make a plot with the different shapes and open/closed dependent on the columns with

example_data$point_shape <- -1
example_data[example_data$shape=='triangle' & example_data$openclosed=='closed',]$point_shape <- 15
example_data[example_data$shape=='triangle' & example_data$openclosed=='open',]$point_shape <- 0
example_data[example_data$shape=='circle' & example_data$openclosed=='closed',]$point_shape <- 16
example_data[example_data$shape=='circle' & example_data$openclosed=='open',]$point_shape <- 1
example_data$point_shape <- as.character(example_data$point_shape)


ggplot(example_data, aes(x, y, colour=color, shape=point_shape))+
  geom_point()+
  scale_shape_manual(name='', breaks=unique(example_data$point_shape),
                     values=as.numeric(unique(example_data$point_shape)),
                     labels=c('Open square','Open circle',
                              'Closed circle','Closed square'))

example

I then tried https://stackoverflow.com/a/44725969/651779 to get two shape legends with

ggplot(example_data, aes(x, y, colour=color, shape=point_shape))+
  geom_point(show.legend=F)+
  geom_point(data = example_data[example_data$shape %in% c('triangle','circle'),], 
             size=0,alpha=0)+
  geom_point(data = example_data[example_data$openclosed %in% c('open','closed'),], 
             size=0, alpha=0)+
  scale_shape_manual(name='', breaks=unique(example_data$point_shape),
                     values=as.numeric(unique(example_data$point_shape)),
                     labels=c('Open square','Open circle',
                              'Closed circle','Closed square'))+
    guides(alpha = guide_legend(title = "Shape", order = 1, 
                                override.aes = list(shape = c(15,16), 
                                                    size = 5, color = "black",alpha=1)),
           size = guide_legend(title = "Open or closed", order = 2, 
                               override.aes = list(shape = c(16,1), 
                                                   size = 5, color = "black", alpha=1)))

example 2

But this didnd't add the two separate legends.

How can I change the legend to have 1 legend with a square/circle, and another legend with open/closed shape?

tjebo
  • 21,977
  • 7
  • 58
  • 94
Niek de Klein
  • 8,524
  • 20
  • 72
  • 143

2 Answers2

3

This could be achieved by

  1. mapping your variables shape on the shape aes and openclosed on the fill aes
  2. Setting the shapes to 24 and 21 which allow for both color and fill
  3. Adjusting the shape used for the fill guide
library(ggplot2)

example_data <- data.frame(x=c(1,3,2,4,5,3,1,2,3,2),
                           y=c(3,5,7,9,1,3,4,7,8,9),
                           color=c('col1','col2','col3','col1','col2','col3','col1','col2','col1','col3'),
                           shape=c('triangle','circle','triangle','circle','triangle','circle','triangle','circle','triangle','circle'),
                           openclosed=c('open','open','open','open','open','closed','closed','closed','closed','closed'))

ggplot(example_data, aes(x, y, shape=shape))+
  geom_point(aes(fill = openclosed), color = "black") +
  scale_shape_manual(values = c(24, 21)) +
  scale_fill_manual(values = c(open = "white", closed = "black")) +
  guides(fill = guide_legend(override.aes = list(shape = 21)))

stefan
  • 90,330
  • 6
  • 25
  • 51
3

I personally like the ggnewscale package for this purpose. Not sure how exactly you wanted to arrange your legends - I am using different shapes rather than changing the fill.

library(tidyverse)
library(ggnewscale)
example_data <- data.frame(x=c(1,3,2,4,5,3,1,2,3,2),
                           y=c(3,5,7,9,1,3,4,7,8,9),
                           color=c('col1','col2','col3','col1','col2','col3','col1','col2','col1','col3'),
                           shape=c('triangle','circle','triangle','circle','triangle','circle','triangle','circle','triangle','circle'),
                           openclosed=c('open','open','open','open','open','closed','closed','closed','closed','closed'))


ggplot(mapping = aes(x, y))+
  geom_point(data = filter(example_data, shape == "circle"),
             aes(shape = openclosed)) +
  # it's important to give a different name to this scale than to the second scale
  # This can also be NULL
  scale_shape_manual("Circle", values = c(open = 1, closed = 16)) + 
  new_scale("shape") +
  geom_point(data = filter(example_data, shape == "triangle"),
             aes(shape = openclosed)) +
  scale_shape_manual("Triangle", values = c(open = 2, closed = 17)) 

Created on 2021-04-11 by the reprex package (v1.0.0)

tjebo
  • 21,977
  • 7
  • 58
  • 94
  • Creative way to use the ggnewscale package! +1 – teunbrand Apr 11 '21 at 21:11
  • @teunbrand thanks for the kind words, but I am not sure if deserved! Maybe it's just me thinking rather simplistically: As soon as I read "multiple legends for one aes", ggnewscale is the first thing that pops to my mind... – tjebo Apr 12 '21 at 07:17
  • 1
    I always forget that you can use ggnewscale for aesthetics other than colour/fill :') – teunbrand Apr 12 '21 at 08:48