forked from PsyTeachR/ads-v1
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathapp-styling.qmd
409 lines (305 loc) · 14.4 KB
/
app-styling.qmd
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# Styling Plots {#sec-plotstyle}
```{r, include = FALSE}
knitr::opts_chunk$set(
echo = FALSE,
warning = FALSE,
message = FALSE
)
library(ggthemes)
```
## Aesthetics
### Colour/Fill
The `colour` argument changes the point and line colour, while the `fill` argument changes the interior colour of shapes. Type `colours()` into the console to see a list of all the named colours in R. Alternatively, you can use hexadecimal colours like `"#FF8000"` or the `rgb()` function to set red, green, and blue values on a scale from 0 to 1.
Hover over a colour to see its R name.
<style>
.colordemo { list-item-style: none; padding: 0; margin: 0;}
.colordemo li, main ul.colordemo > li:first-child {
display: inline-block;
border: 0.25px solid black;
margin: 0px;
padding: 1.3em 0.2em 0.2em 1.2em;
}
.colordemo li span {
display: none;
background-color: white;
border: 0.25px solid black;
padding: 0.25em 0.5em;
border-radius: 0 1em 1em 1em;
}
.colordemo li:hover span {
display: inline;
position: absolute;
}
</style>
```{r colour-demo, echo = FALSE, results='asis'}
name <- colours(TRUE)
rgb <- col2rgb(name)
hsv <- rgb2hsv(rgb)
hex <- as.hexmode(rgb) %>% as.character() %>%
apply(2, paste, collapse = "")
colors <- tibble(name = name,
hue = hsv[1, ] %>% round(2),
sat = hsv[2, ] %>% round(2),
val = hsv[3, ] %>% round(2),
hex = hex) %>%
arrange(hue, sat, val)
glue::glue(" <li style='background-color: #{colors$hex};'><span>{colors$name}</span></li>") %>%
paste(collapse = "\n") %>%
paste0("\n<ul class='colordemo'>\n", ., "\n</ul>\n") %>%
cat()
```
### Alpha
The `alpha` argument changes transparency (0 = totally transparent, 1 = totally opaque).
```{r alpha-demo, echo = FALSE, fig.height = 3, fig.width = 10, fig.cap="Varying alpha values."}
alphas <- crossing(alpha = seq(0, 1, .1), y = 0:1)
ggplot(alphas, aes(x = alpha, y = y, alpha = I(alpha),
color = as.factor(y))) +
geom_hline(yintercept = 0, size = 5, color = "dodgerblue") +
geom_hline(yintercept = 1, size = 5, color = "black") +
geom_point(size = 20, show.legend = FALSE) +
scale_x_continuous("alpha", breaks = alphas$alpha) +
scale_y_continuous("", breaks = -1:2, limits = c(-1, 2)) +
scale_color_manual(values = c("black", "dodgerblue")) +
theme_minimal(base_size = 20) +
theme(axis.text.y = element_blank(),
panel.grid.minor = element_blank())
```
### Shape
The `shape` argument changes the shape of points.
```{r shape-demo, echo = FALSE, fig.height = 2, fig.width = 10, fig.cap="The 25 shape values"}
shapes <- tibble(shape = 1:25, y = 0)
ggplot(shapes, aes(x = shape, y = y, shape = I(shape))) +
geom_point(size = 8) +
scale_x_continuous("", breaks = shapes$shape) +
scale_y_continuous("", breaks = 0) +
theme_minimal(base_size = 20) +
theme(axis.text.y = element_blank(),
panel.grid.minor = element_blank())
```
### Linetype
You can probably guess what the `linetype` argument does.
```{r linetype-demo, echo = FALSE, fig.height = 4, fig.width = 10, fig.cap="The 6 linetype values at different sizes."}
lines <- crossing(linetype = 1:6, size = c(0.25, 0.5, 1, 2)) %>%
mutate(x = linetype)
ggplot(lines) +
geom_vline(aes(xintercept = linetype,
linetype = I(linetype),
size = I(size))) +
scale_x_continuous("linetype", breaks = 1:6,
expand = expansion(add = 1)) +
facet_wrap(~size, nrow = 1, labeller = label_both) +
theme_minimal(base_size = 20) +
theme(axis.text.y = element_blank(),
panel.grid.minor = element_blank())
```
## Palettes
```{r, include = FALSE}
palette_demo <- function(levels, seed = 8675309) {
set.seed(seed)
dat <- faux::sim_design(
within = list(level = 1:levels),
between = list(condition= c("control", "experimental")),
n = 50, dv = "score",
plot = FALSE, long = TRUE)
ggplot(dat, aes(x = level,
y = score,
color = level,
fill = level)) +
geom_violin(alpha = 0.8, color = "black") +
stat_summary(fun = mean) +
scale_x_discrete(name = "") +
guides(color = "none", fill = "none") +
theme(axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank())
}
palette_plot <- function(g, p) {
g + scale_color_brewer(palette = p) +
scale_fill_brewer(palette = p) +
ggtitle(glue::glue("palette = \"{p}\""))
}
viridis_d_plot <- function(g, option = "viridis") {
g + scale_color_viridis_d(option = option) +
scale_fill_viridis_d(option = option) +
ggtitle(glue::glue("option = \"{option}\""))
}
```
Discrete palettes change depending on the number of categories.
```{r, fig.width = 8, fig.height = 10, fig.cap="Default discrete palette with different numbers of levels."}
plots <- lapply(1:8, palette_demo)
wrap_plots(plots, ncol = 2)
```
### Viridis Palettes
Viridis palettes are very good for colourblind-safe and greyscale-safe plots. The work with any number of categories, but are best for larger numbers of categories or continuous colours.
#### Discrete Viridis Palettes
Set `r glossary("discrete")` viridis colours with `scale_colour_viridis_d()` or `scale_fill_viridis_d()` and set the `option` argument to one of the options below. Set `direction = -1` to reverse the order of colours.
```{r, fig.width = 8, fig.height = 10, fig.cap="Discrete viridis palettes."}
options <- list("magma", "inferno", "plasma", "viridis", "cividis")
g <- palette_demo(5)
plots5 <- lapply(options, viridis_d_plot, g = g)
g <- palette_demo(15)
plots15 <- lapply(options, viridis_d_plot, g = g)
( wrap_plots(plots5, ncol = 1) |
wrap_plots(plots15, ncol = 1)) +
plot_layout(widths = c(1, 3))
```
::: {.callout-note}
If the end colour is too light for your plot or the start colour too dark, you can set the `begin` and `end` arguments to values between 0 and 1, such as `scale_colour_viridis_c(begin = .1, end = .9)`.
:::
#### Continuous Viridis Palettes
Set `r glossary("continuous")` viridis colours with `scale_colour_viridis_c()` or `scale_fill_viridis_c()` and set the `option` argument to one of the options below. Set `direction = -1` to reverse the order of colours.
```{r, echo = FALSE, fig.width = 8, fig.height = 8, fig.cap="Continuous viridis palettes."}
options <- list("magma", "inferno", "plasma", "viridis", "cividis")
set.seed(8675309)
g <- faux::rnorm_multi(
n = 10000,
vars = 2,
r = 0.5) %>%
ggplot(aes(X1, X2)) +
theme(axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank())
# install hexbin if this fails
viridis_c_plot <- function(g, option, geom = geom_hex) {
g + scale_color_viridis_c(option = option) +
geom_hex() +
scale_fill_viridis_c(option = option) +
ggtitle(glue::glue("option = \"{option}\""))
}
hex_plots <- lapply(options, viridis_c_plot, g = g)
wrap_plots(hex_plots, nrow = 3)
```
### Brewer Palettes
Brewer palettes give you a lot of control over plot colour and fill. You set them with `scale_color_brewer()` or `scale_fill_brewer()` and set the `palette` argument to one of the palettes below. Set `direction = -1` to reverse the order of colours.
#### Qualitative Brewer Palettes
These palettes are good for `r glossary("categorical")` data with up to 8 categories (some palettes can handle up to 12). The "Paired" palette is useful if your categories are arranged in pairs.
```{r, echo = FALSE, fig.width = 8, fig.height = 8, fig.cap="Qualitative brewer palettes."}
palettes <- list("Accent", "Dark2", "Paired", "Pastel1", "Pastel2", "Set1", "Set2", "Set3")
g <- palette_demo(12)
plots <- lapply(palettes, palette_plot, g = g)
wrap_plots(plots, ncol = 2)
```
#### Sequential Brewer Palettes
These palettes are good for up to 9 `r glossary("ordinal")` categories with a lot of categories.
```{r, warning=FALSE, echo = FALSE, fig.width = 8, fig.height = 18, fig.cap="Sequential brewer palettes."}
palettes <- c("Reds", "RdPu",
"Oranges", "OrRd",
"YlOrRd", "YlOrBr", "YlGn", "YlGnBu",
"Greens", "GnBu",
"Blues", "BuGn", "BuPu",
"Purples", "PuRd", "PuBu", "PuBuGn",
"Greys")
g <- palette_demo(9)
plots <- lapply(palettes, palette_plot, g = g)
wrap_plots(plots, ncol = 2)
```
#### Diverging Brewer Palettes
These palettes are good for `r glossary("ordinal")` categories with up to 11 levels where the centre level is a neutral or baseline category and the levels above and below it differ in an important way, such as agree versus disagree options.
```{r, fig.width = 8, fig.height = 10, fig.cap="Diverging brewer palettes."}
palettes <- c("BrBG", "PiYG", "PRGn", "PuOr", "RdBu", "RdGy", "RdYlBu", "RdYlGn", "Spectral")
g <- palette_demo(11)
plots <- lapply(palettes, palette_plot, g = g)
wrap_plots(plots, ncol = 2)
```
## Themes {#sec-themes-appendix}
<pkg>ggplot2</pkg> has 8 built-in themes that you can add to a plot like `plot + theme_bw()` or set as the default theme at the top of your script like `theme_set(theme_bw())`.
```{r, fig.width = 8, fig.height = 8, fig.cap="{ggplot2} themes."}
g <- palette_demo(2) + facet_wrap(~condition)
themes <- c("theme_bw", "theme_classic", "theme_dark", "theme_gray", "theme_light", "theme_linedraw", "theme_minimal", "theme_void")
plots <- lapply(themes, function(f) {
g + get(f)() + ggtitle(glue::glue("{f}()"))
})
wrap_plots(plots, ncol = 2)
```
### ggthemes
You can get more themes from add-on packages, like <pkg>ggthemes", "https://yutannihilation.github.io/allYourFigureAreBelongToUs/ggthemes/")`. Most of the themes also have custom `scale_` functions like `scale_colour_economist()`. Their website has extensive examples and instructions for alternate or dark versions of these themes.
```{r, fig.width = 8, fig.height = 18, fig.cap="{ggthemes} themes."}
g <- palette_demo(2) + facet_wrap(~condition)
themes <- c("theme_wsj", "theme_tufte", "theme_stata", "theme_gdocs", "theme_fivethirtyeight", "theme_economist", "theme_igray", "theme_hc", "theme_few", "theme_map", "theme_excel", "theme_calc", "theme_economist_white", "theme_excel_new", "theme_pander", "theme_solarized", "theme_solarized_2") %>% sort() %>% setNames(., .)
plots <- lapply(themes, function(f) {
scale_colour <- tryCatch({
get(gsub("theme_","scale_colour_", gsub("_(2|white)", "", f)))()
}, error = function(e) {
return(NULL)
})
scale_fill <- tryCatch({
get(gsub("theme_","scale_fill_", gsub("_(2|white)", "", f)))()
}, error = function(e) {
return(NULL)
})
g + get(f)() +
ggtitle(glue::glue("{f}()")) +
scale_colour +
scale_fill
})
wrap_plots(plots, ncol = 2)
```
### Fonts
You can customise the fonts used in themes. All computers should be able to recognise the families "sans", "serif", and "mono", and some computers will be able to access other installed fonts by name.
```{r, include = FALSE}
if (.Platform$OS.type == "windows") {
windowsFonts("Comic Sans MS" = windowsFont("Comic Sans MS"))
}
```
```{r theme-font-demo, echo = TRUE, fig.width = 10, fig.height = 2.5, fig.cap="Different fonts."}
sans <- g + theme_bw(base_family = "sans") +
ggtitle("Sans")
serif <- g + theme_bw(base_family = "serif") +
ggtitle("Serif")
mono <- g + theme_bw(base_family = "mono") +
ggtitle("Mono")
font <- g + theme_bw(base_family = "Comic Sans MS") +
ggtitle("Comic Sans MS")
sans + serif + mono + font + plot_layout(nrow = 1)
```
::: {.callout-warning}
If you are working on a Windows machine and get the error "font family not found in Windows font database", you may need to explicitly map the fonts. In your setup code chunk, add the following code, which should fix the error. You may need to do this for any fonts that you specify.
```{r, eval = FALSE}
windowsFonts("Comic Sans MS" = windowsFont("Comic Sans MS"))
```
:::
The <pkg>showtext</pkg> package is a flexible way to add fonts.
If you have a .ttf file from a font site, like [Font Squirrel](https://www.fontsquirrel.com){target="_blank"}, you can load the file directly using `font_add()`. Set `regular` as the path to the file for the regular version of the font, and optionally add other versions. Set the `family` to the name you want to use for the font. You will need to include any local font files if you are sharing your script with others.
```{r, echo = TRUE}
library(showtext)
# font from https://www.fontsquirrel.com/fonts/SF-Cartoonist-Hand
font_add(
regular = "fonts/cartoonist/SF_Cartoonist_Hand.ttf",
bold = "fonts/cartoonist/SF_Cartoonist_Hand_Bold.ttf",
italic = "fonts/cartoonist/SF_Cartoonist_Hand_Italic.ttf",
bolditalic = "fonts/cartoonist/SF_Cartoonist_Hand_Bold_Italic.ttf",
family = "cartoonist"
)
```
To download fonts directly from [Google fonts](https://fonts.google.com/){target="_blank"}, use the function `font_add_google()`, set the `name` to the exact name from the site, and the `family` to the name you want to use for the font.
```{r, echo = TRUE}
# download fonts from Google
font_add_google(name = "Courgette", family = "courgette")
font_add_google(name = "Poiret One", family = "poiret")
```
After you've added fonts from local files or Google, you need to make them available to R using `showtext_auto()`. You will have to do these steps in each script where you want to use the custom fonts.
```{r, echo = TRUE}
showtext_auto() # load the fonts
```
To change the fonts used overall in a plot, use the `theme()` function and set `text` to `element_text(family = "new_font_family")`.
```{r, echo = TRUE, font-demo, fig.width = 8, fig.height = 2.5, fig.cap="Custom Fonts."}
a <- g + theme(text = element_text(family = "courgette")) +
ggtitle("Courgette")
b <- g + theme(text = element_text(family = "cartoonist")) +
ggtitle("Cartoonist Hand")
c <- g + theme(text = element_text(family = "poiret")) +
ggtitle("Poiret One")
a + b + c
```
To set the fonts for individual elements in the plot, you need to find the specific argument for that element. You can use the argument `face` to choose "bold", "italic", or "bolditalic" versions, if they are available.
```{r, echo = TRUE, demo-multi-text, fig.width = 6, fig.height = 2.5, fig.cap="Multiple custom fonts on the same plot."}
g + ggtitle("Cartoonist Hand") +
theme(
title = element_text(family = "cartoonist", face = "bold"),
strip.text = element_text(family = "cartoonist", face = "italic"),
axis.text = element_text(family = "sans")
)
```