-
Notifications
You must be signed in to change notification settings - Fork 4
/
02-themes-colors.Rmd
144 lines (112 loc) · 7.72 KB
/
02-themes-colors.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
---
title: "Colors and themes"
author: "John Franchak"
output: html_document
---
```{r setup, include = FALSE}
library(tidyverse)
library(here)
library(patchwork)
ds <- read_csv(here("data_cleaned","cleaned.csv"))
ds <- ds %>% mutate(across(all_of(c("stim", "age_group", "watched")), as_factor))
ds$age <- ds$age / 365.25
```
### Changing colors
The default colors in ggplot are fine, but often you might need more control over the colors. Let's compare graphing the same data with and without including the "adult" group. Note a couple of useful cosemtic things in this example. First, I'm saving each plot to a variable (`p1`, `p2`) and then using the `patchwork` package (loaded above) to plot them side by side by adding them `p1 + p2`. Second, to make a bit more room for the plots I'm changing the location of the legend with `theme(legend.position = "bottom")`.
```{r}
p1 <- ds %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
theme(legend.position = "bottom")
p2 <- filter(ds, age_group != "adult") %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
theme(legend.position = "bottom")
p1 + p2
```
What's wrong here? We wanted to focus in to look at just infant/child data, but now our colors are mapped inconsistently. In ggplot, using the function `scale_SOMETHING_SOMEWAY` gives you precise control over an aesthetic mapping, including your axes, `scale_x_manual`, colors `scale_color_manual`, and shape `scale_shape_manual`. See <https://ggplot2tor.com/scales/> for a list of things that can be scaled.
The `scale_color_manual` command is also good place to rename the factor (so that it says "Group" instead of "age_group"). But first, if we want to make sure we use the same colors in our two graphs, we should first create a list of colors. A list of colors can simply be a character vector of hex color codes such as:
```{r}
custom_palette <- c("#999999", "#E69F00", "#56B4E9", "#009E73","#F0E442", "#0072B2", "#D55E00")
```
By passing custom_palette to the values argument of scale_color_manual, we can set the colors that will be used. Specifying `custome_palette[-1]` in the second graph skips the color for the age group that we omitted.
```{r}
p1 <- ds %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
scale_color_manual(values = custom_palette, name = "Group") +
theme(legend.position = "bottom")
p2 <- filter(ds, age_group != "adult") %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
scale_color_manual(values = custom_palette[-1], name = "Group") +
theme(legend.position = "bottom")
p1 + p2
```
Does this seem a bit hacky and hard-coded? What if you want to exclude a different group? Or what if you reordered the factor levels for one plot? A better way to set the colors is to create a named vector using the factor labels.
```{r}
age_groups <- unique(ds$age_group)
custom_palette <- c("#999999", "#E69F00", "#56B4E9", "#009E73","#F0E442", "#0072B2", "#D55E00") %>% set_names(age_groups)
```
Now that we've named the colors to correspond to the factor levels, we don't need to adjust custom_palette each time. Yet another use of named vectors/lists.
```{r}
p1 <- filter(ds, age_group %in% c("8-10 y","adult")) %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
scale_color_manual(values = custom_palette, name = "Group") +
theme(legend.position = "bottom")
p2 <- filter(ds, age_group != "adult") %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
scale_color_manual(values = custom_palette, name = "Group") +
theme(legend.position = "bottom")
p1 + p2
```
### Making your plots accessible
The default color palette in ggplot is not color-blind friendly. If you want to use color in your graphs, it's good to check how they may look to people who have different types of colorblindness (as well as how your figure may print in grayscale). The `colorblindr` package can help you with this. It's a beta package that needs to be installed from github.
```{r, message = FALSE, warning = FALSE}
## TO INSTALL:
# remotes::install_github("wilkelab/cowplot")
# install.packages("colorspace", repos = "http://R-Forge.R-project.org")
# remotes::install_github("clauswilke/colorblindr")
library(colorblindr)
#Default colors
p1 <- ds %>% ggplot(aes(x = stim, y = AUC_dist, color = age_group)) +
stat_summary(position = position_dodge(.2)) +
theme(legend.position = "bottom")
p1
cvd_grid(p1)
```
Instead, let's use a color blind friendly palette. There's one bundled in the colorblindr package `palette_OkabeIto`, but you can also find hex codes from <http://www.cookbook-r.com/Graphs/Colors_(ggplot2)/#a-colorblind-friendly-palette> and other websites.
```{r, message = FALSE, warning = FALSE}
p2 <- ds %>% ggplot(aes(x = stim, y = AUC_dist, color = age_group)) +
stat_summary(position = position_dodge(.2)) +
scale_color_manual(values = palette_OkabeIto, name = "Group") +
theme(legend.position = "bottom")
p2
cvd_grid(p2)
```
### Changing theme elements
The `theme` command lets you make changes to every imaginable aspect of your plot. The difference between theme and scale is that theme is for changing aspects of the graph that aren't mapped to an aesthetic, whereas scale refers to the elements that appear in an `aes()` statement. Let's run `?theme` to see the list of possible things to change.
It's a lot! I personally am a fan of plain white backgrounds, larger font sizes, bolder ticks, and (often) removing the axes lines. You can probably figure out what most of this means if you go through piece by piece. `element_blank()` means delete that element from the theme. The rest of it is setting font sizes, line styles, and making the background white:
```{r}
my_theme <- theme(text = element_text(size = 16),
axis.text.x = element_text(size = 16, color = "black"), axis.title.x = element_text(size = 21),
axis.text.y = element_text(size = 16, color = "black"), axis.title.y = element_text(size = 21),
panel.background = element_blank(),panel.border = element_blank(), panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), axis.line = element_line(colour = "white"),
axis.ticks.length = unit(.25, "cm"), axis.ticks = element_line(size = 1, lineend = "round"),
legend.key = element_rect(fill = "white"))
```
Notice that I saved all of that to a variable, `my_theme`. If you have things you want to add to a plot that are repetitive and take up a lot of space, just assign them to a variable. That variable can stand in for the long list in the ggplot call:
```{r}
ds %>% ggplot(aes(x = age, y = AUC_dist, color = age_group)) +
geom_point() +
scale_color_manual(values = custom_palette, name = "Group") +
xlab("Age (years)") + ylab("Distance model AUC") +
my_theme
```
If you want to make those changes permanent for your R session and apply to every graph, you can use `theme_update` and run it at the top of your script. I'm not running it here so that it won't disrupt my examples, but this is more efficient than adding redundant `theme()` calls to each plot.
```{r, eval = FALSE}
theme_update(text = element_text(size = 16),
axis.text.x = element_text(size = 16, color = "black"), axis.title.x = element_text(size = 21),
axis.text.y = element_text(size = 16, color = "black"), axis.title.y = element_text(size = 21),
panel.background = element_blank(),panel.border = element_blank(), panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), axis.line = element_line(colour = "white"),
axis.ticks.length = unit(.25, "cm"), axis.ticks = element_line(size = 1, lineend = "round"),
legend.key = element_rect(fill = "white"))
```