Reputation: 125
I have a question that I have been stuck on for a long time now. I want to make a ggplot that combines color and shape aesthetics.
for example let's consider the plot:
ggplot(mtcars, aes(x = mpg, y = hp, color = factor(carb),
shape = factor(gear))) +
geom_point(size = 10) +
custom_theme_wrap
There are 3 different shapes and 6 different colors. When looking at
length(unique(paste(mtcars[[carb]], mtcars[[gear]], sep = "-")))
we get 11 different unique values.
Now in the plot we get a legend that is split into shape and color. However what I would want is a legend in which we get a unique shape AND color for each point. Giving me 11 legend items in total. So for example I would have a red triangle, red circle, red square and blue circle as my figure legend rather than 2 legends for shape and colour separately.
As far as I am aware such functionality does not exist in ggplot as it will not combine two different aesthetics to create a combined aesthetic.
I have managed to generate a unique set of colors and shapes for each data point as follows.
color_column <- "carb"
shape_column <- "gear"
available_shapes <- c(15, 17, 18, 19, 21, 22, 23, 24, 25)
unique_color <- unique(mtcars[[color_column]])
unique_shape <- unique(mtcars[[shape_column]])
# Generate evenly distributed colors
n_colors <- length(unique_color)
color_palette <- (hcl.colors(n_colors))
n_shapes <- length(unique_shape)
shape_palette <- available_shapes[1:n_shapes]
mtcars$combined_name <- paste(mtcars[[color_column]], mtcars[[shape_column]], sep = "-")
length(unique(paste(mtcars[[color_column]], mtcars[[shape_column]], sep = "-")))
name_colors <- setNames(color_palette[1:n_colors], unique_color)
name_shapes <- setNames(shape_palette[1:n_shapes], unique_shape)
mtcars<-
mtcars %>%
mutate(color_id = ifelse(carb %in% names(name_colors),
name_colors[as.character(carb)], "default_color"),
shape_id = ifelse(gear %in% names(name_shapes),
name_shapes[as.character(gear)], "default_shape"))
I can then use the color_id and shape_id to create a plot in base R. However, it again becomes very difficult to create a legend.
Is anyone aware of a way to merge shape and color aesthetics to create a ggplot?
Combine legends for color and shape into a single legend
Upvotes: 0
Views: 379
Reputation: 12585
The accepted answer in the question you link to works only when the columns defining the two aesthetics you wish to define have the same values. That's not the case here. That's why the legends don't combine.
One way of combining two columns in a single aesthetic is to use the interaction
function:
mtcars %>%
ggplot(
aes(
x = mpg,
y = hp,
color = interaction(as.factor(carb), as.factor(gear)),
shape = interaction(as.factor(carb), as.factor(gear))
)
)+
geom_point()
which gives
together with two warnings:
Warning messages:
1: The shape palette can deal with a maximum of 6 discrete values because more than 6 becomes difficult to discriminate; you
have 11. Consider specifying shapes manually if you must have them.
2: Removed 9 rows containing missing values (`geom_point()`).
And the legend itself is very unsatisfactory. So you are going to have to do a bit of work to get what you want.
Start by obtaining the unique combinations of carb
and gear
that exist in mtcars
:
legend <- mtcars %>% distinct(carb, gear)
legend
carb gear
Mazda RX4 4 4
Datsun 710 1 4
Hornet 4 Drive 1 3
Hornet Sportabout 2 3
Duster 360 4 3
Merc 240D 2 4
Merc 450SE 3 3
Porsche 914-2 2 5
Ford Pantera L 4 5
Ferrari Dino 6 5
Maserati Bora 8 5
Now determine the shape, colour and label that you want to represent each:
legend <- mtcars %>% distinct(carb, gear) %>%
mutate(
shape = case_when(
gear == 3 ~ 15,
gear == 4 ~ 17,
gear == 5 ~ 18
),
colour = case_when(
carb == 1 ~ "red",
carb == 2 ~ "green",
carb == 3 ~ "blue",
carb == 4 ~ "orange",
carb == 6 ~ "black",
carb == 8 ~ "pink"
),
label = paste0(carb, " carbs, ", gear, " gears")
)
legend
carb gear shape colour label
Mazda RX4 4 4 17 orange 4 carbs, 4 gears
Datsun 710 1 4 17 red 1 carbs, 4 gears
Hornet 4 Drive 1 3 15 red 1 carbs, 3 gears
Hornet Sportabout 2 3 15 green 2 carbs, 3 gears
Duster 360 4 3 15 orange 4 carbs, 3 gears
Merc 240D 2 4 17 green 2 carbs, 4 gears
Merc 450SE 3 3 15 blue 3 carbs, 3 gears
Porsche 914-2 2 5 18 green 2 carbs, 5 gears
Ford Pantera L 4 5 18 orange 4 carbs, 5 gears
Ferrari Dino 6 5 18 black 6 carbs, 5 gears
Maserati Bora 8 5 18 pink 8 carbs, 5 gears
Now you have all the information you need to construct your plot:
mtcars %>%
ggplot(
aes(
x = mpg,
y = hp,
color = interaction(as.factor(carb), as.factor(gear)),
shape = interaction(as.factor(carb), as.factor(gear))
)
) +
scale_colour_discrete(
name = "Combined",
labels = legend %>% distinct(gear, label) %>% pull(label),
type = legend %>% distinct(gear, colour) %>% pull(colour)
) +
scale_shape_manual(
name = "Combined",
labels = legend %>% distinct(carb, label) %>% pull(label),
values = legend %>% distinct(carb, shape) %>% pull(shape)
) +
geom_point()
giving
and no warnings.
[Personally, I'd map carb
to colour and gear
to shape because there are more distinct values of carb
than gear
and, as the warning message says, lots of shapes are hard to distinguish...]
Upvotes: 2