Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manually scale color, shape, and linetype of multiple geoms ggplot2

Tags:

r

ggplot2

A partial solution to my problem is found here. I will show how that solution works and what features are still lacking to meet my needs. For consistency, I use a sample data set that is similar to the one from the other thread:

library(ggplot2)
library(reshape2)
library(tidyverse)

df <- data.frame(x = c(1:5), a = c(1,2,3,3,3), b = c(1,1.1,1.3,1.5,1.5))
df <- mutate(df, log2 = log2(x))
df <- df <- melt(df, c("x", "log2"))

An initial plot of the data (created by the following code) has the shape and color aesthetics in separate legends, which is undesirable:

ggplot(df) +
  geom_point(aes(x, value, colour = variable, shape = variable), size = 3) +
  geom_line(aes(x, log2, color = "log2(x)"), size = 1.5)

The solution proposes using guides() and override.aes to combine these into a single legend, which is certainly an improvement over the original plot:

ggplot(df) +
  geom_point(aes(x, value, colour = variable, shape = variable), size = 3) +
  geom_line(aes(x, log2, color = "log2(x)"), size = 1.5) +
  guides(shape = FALSE,
         colour = guide_legend(override.aes = list(shape = c(16, 17, NA),
                                                   linetype = c("blank", "blank", "solid"))))

My issue is that I would like manual control of the color aesthetic in both geom_point and geom_line. For example, I want to make the triangles light grey, the circles dark grey, and the log2(x) line light blue. My understanding of how guides() works is very limited, so the best I have been able to do is the following:

ggplot(df) +
  geom_point(aes(x, value, shape = variable), color = "gray40", size = 3, data = filter(df, variable == "a")) +
  geom_point(aes(x, value, shape = variable), color = "gray70", size = 3, data = filter(df, variable == "b")) +
  geom_line(aes(x, log2, shape = "log2(x)"), color = "cadetblue2", size = 1.5) +
  guides(shape = guide_legend(override.aes = list(color = c("gray40", "gray70", "cadetblue2"),
                                                  shape = c(16, 17, NA),
                                                  linetype = c("blank", "blank", "solid"))))

This seems like an improper use of the shape aesthetic in geom_line (given the warning generated), however it is almost the result I want. The problem now is that the legend icon for log2(x) is missing. Ideally it should show a horizontal light blue line that matches the plot. Does anyone know how to achieve this and/or could better explain how the guides() function is intended to be used?

I have also tried solutions using a combination of scale_color_manual(), scale_linetype_manual(), and scale_shape_manual() (all with the same name argument that way they should end up in a single legend). The issue with this approach has been that all 3 geom layers have a color aesthetic, but only the first two have a shape aesthetic and only the last one has a linetype aesthetic. So even if all of the scale_xx_manual() additions to ggplot are set up correctly, the legends are not combined into a single one.

I have been stuck on this for a long time, so any help would be greatly appreciated. Thank you

like image 612
Addison Klinke Avatar asked Nov 29 '17 20:11

Addison Klinke


1 Answers

Just create a manual color scale and that will change the colors in both the plot and the guide. In general, you always want to favor using a scale over overriding guide values because it removes the risk of making mistakes in the guide which could cause your visualization to lie.

ggplot(df) +
  geom_point(aes(x, value, colour = variable, shape = variable), size = 3) +
  geom_line(aes(x, log2, color = "log2(x)"), size = 1.5) +
  scale_color_manual(values = c("a" = "gray40", "b" = "gray70", "log2(x)" = "cadetblue2")) +
  guides(shape = FALSE,
         colour = guide_legend(override.aes = list(shape = c(16, 17, NA),
                                                   linetype = c("blank", "blank", "solid"))))

Edit:

Here's a version that also avoids doing the guide override on shape:

ggplot(df) +
  geom_point(aes(x, value, colour = variable, shape = variable, linetype = variable), size = 3) +
  geom_line(aes(x, log2, color = "log2(x)", linetype = "log2(x)", shape = "log2(x)"), size = 1.5) +
  scale_color_manual(values = c("a" = "gray40", "b" = "gray70", "log2(x)" = "cadetblue2")) +
  scale_shape_manual(values = c("a" = 16, "b" = 17, "log2(x)" = NA)) +
  scale_linetype_manual(values = c("a" = "blank", "b" = "blank", "log2(x)" = "solid"))

ggplot will automatically combine legends when the name, labels, and direction are identical (1, 2), so you just have to make sure "a", "b", and "log2(x)" all have mappings to each aesthetic in the same order. In this case, we accomplish this by adding unnecessary mappings to the layers and ignoring the warnings.

like image 162
W. Murphy Avatar answered Oct 16 '22 23:10

W. Murphy