-
Notifications
You must be signed in to change notification settings - Fork 0
/
mod_data-viz.qmd
470 lines (341 loc) · 23.3 KB
/
mod_data-viz.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
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
---
title: "Data Visualization & Exploration"
code-annotations: hover
---
## Overview
Data visualization is a fundamental part of working with data. Visualization can be only used in the final stages of a project to make figures for publication but it can also be hugely valuable for quality control and hypothesis development processes. This module focuses on the fundamentals of graph creation in an effort to empower you to apply those methods in the various contexts where you might find visualization to be helpful.
## Learning Objectives
After completing this module you will be able to:
- <u>Define</u> fundamental `ggplot2` vocabulary
- <u>Identify</u> appropriate graph types for given data type/distribution
- <u>Discuss</u> differences between presentation- and publication-quality graphs
- <u>Explain</u> how your graphs can be made more accessible
## Preparation
Each Synthesis Fellow should:
- Select one dataset your group has identified
- Make sure it is a table (e.g., CSV, MS Excel, TXT, etc.)
- Multiple group members may select the same dataset if they want
- Download this file
## Needed Packages
If you'd like to follow along with the code chunks included throughout this module, you'll need to install the following packages:
```{r install-pkgs}
#| eval: false
# Note that these lines only need to be run once per computer
## So you can skip this step if you've installed these before
install.packages("tidyverse")
install.packages("lterdatasampler")
install.packages("supportR")
install.packages("cowplot")
```
We'll go ahead and load some of these libraries as well to be able to better demonstrate these concepts.
```{r libs}
#| message: false
# Load needed libraries
library(tidyverse)
```
## Graphing with `ggplot2`
### `ggplot2` Fundamentals
You may already be familiar with the `ggplot2` package in R but if you are not, it is a popular graphing library based on [The Grammar of Graphics](https://bookshop.org/p/books/the-grammar-of-graphics-leland-wilkinson/1518348?ean=9780387245447). Every ggplot is composed of four elements:
1. A 'core' `ggplot` function call
2. Aesthetics
3. Geometries
4. Theme
Note that the theme component may be implicit in some graphs because there is a suite of default theme elements that applies unless otherwise specified.
This module will use example data to demonstrate these tools but as we work through these topics you should <u>feel free to substitute a dataset of your choosing</u>! If you don't have one in mind, you can use the example dataset shown in the code chunks throughout this module. This dataset comes from the [`lterdatasampler` R package](https://lter.github.io/lterdatasampler/) and the data are about fiddler crabs (_Minuca pugnax_) at the [Plum Island Ecosystems (PIE) LTER](https://pie-lter.ecosystems.mbl.edu/welcome-plum-island-ecosystems-lter) site.
```{r data-prep}
#| message: false
#| warning: false
# Load the lterdatasampler package
library(lterdatasampler)
# Load the fiddler crab dataset
data(pie_crab)
# Check its structure
str(pie_crab)
```
With a dataset in hand, let's make a scatterplot of crab size on the Y-axis with latitude on the X. We'll forgo doing anything to the theme elements at this point to focus on the other three elements.
```{r gg-1}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot(data = pie_crab, mapping = aes(x = latitude, y = size, fill = site)) + # <1>
geom_point(pch = 21, size = 2, alpha = 0.5) # <2>
```
1. We're defining both the data and the X/Y aesthetics in this top-level bit of the plot. Also, note that each line ends with a plus sign
2. Because we defined the data and aesthetics in the `ggplot()` function call above, this geometry can assume those mappings without re-specificying
We can improve on this graph by tweaking theme elements to make it use fewer of the default settings.
```{r gg-2}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot(data = pie_crab, mapping = aes(x = latitude, y = size, fill = site)) +
geom_point(pch = 21, size = 2, alpha = 0.5) +
theme(legend.title = element_blank(), # <1>
panel.background = element_blank(),
axis.line = element_line(color = "black"))
```
1. All theme elements require these `element_...` helper functions. `element_blank` removes theme elements but otherwise you'll need to use the helper function that corresponds to the type of theme element (e.g., `element_text` for theme elements affecting graph text)
### Multiple Geometries
We can further modify `ggplot2` graphs by adding _multiple_ geometries if you find it valuable to do so. Note however that <u>geometry order matters</u>! Geometries added later will be "in front of" those added earlier. Also, adding too much data to a plot will begin to make it difficult for others to understand the central take-away of the graph so you may want to be careful about the level of information density in each graph. Let's add boxplots behind the points to characterize the distribution of points more quantitatively.
```{r gg-3}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot(data = pie_crab, mapping = aes(x = latitude, y = size, fill = site)) +
geom_boxplot(pch = 21) + # <1>
geom_point(pch = 21, size = 2, alpha = 0.5) +
theme(legend.title = element_blank(),
panel.background = element_blank(),
axis.line = element_line(color = "black"))
```
1. By putting the boxplot geometry first we ensure that it doesn't cover up the points that overlap with the 'box' part of each boxplot
:::{.callout-note icon="false"}
#### Activity: Graph Creation (P1)
In a script, attempt the following with one of either yours or your group's datasets:
- Make a graph using `ggplot2`
- Include at least one geometry
- Include at least one aesthetic (beyond X/Y axes)
- Modify at least one theme element from the default
:::
### Multiple Data Objects
`ggplot2` also supports adding more than one data object to the same graph! While this module doesn't cover map creation, maps are a common example of a graph with more than one data object. Another common use would be to include both the full dataset and some summarized facet of it in the same plot.
Let's calculate some summary statistics of crab size to include that in our plot.
```{r gg-4-prep}
#| message: false
# Load the supportR library
library(supportR)
# Summarize crab size within latitude groups
crab_summary <- supportR::summary_table(data = pie_crab, groups = c("site", "latitude"),
response = "size", drop_na = TRUE)
# Check the structure
str(crab_summary)
```
With this data object in-hand, we can make a graph that includes both this and the original, unsummarized crab data. To better focus on the 'multiple data objects' bit of this example we'll pare down on the actual graph code.
```{r gg-4}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot() + # <1>
geom_point(pie_crab, mapping = aes(x = latitude, y = size, fill = site),
pch = 21, size = 2, alpha = 0.2) +
geom_errorbar(crab_summary, mapping = aes(x = latitude, # <2>
ymax = mean + std_error,
ymin = mean - std_error),
width = 0.2) +
geom_point(crab_summary, mapping = aes(x = latitude, y = mean, fill = site),
pch = 23, size = 3) +
theme(legend.title = element_blank(),
panel.background = element_blank(),
axis.line = element_line(color = "black"))
```
1. If you want multiple data objects in the same `ggplot2` graph you need to leave this top level `ggplot()` call _empty!_ Otherwise you'll get weird errors with aesthetics later in the graph
2. This geometry adds the error bars and it's important that we add it before the summarized data points themselves if we want the error bars to be 'behind' their respective points
:::{.callout-note icon="false"}
#### Activity: Graph Creation (P2)
In a script, attempt the following:
- Add a second data object to the graph you made in the preceding activity
- _Hint:_ If your first graph is unsummarized, add a summarized version (or vice versa)
:::
## Streamlining Graph Aesthetics
Synthesis projects often generate an entire network of inter-related papers. Ensuring that all graphs across papers from a given team have a similar "feel" is a nice way of implying a certain standard of robustness for all of your group's projects. However, copy/pasting the theme elements of your graphs can (A) be cumbersome to do even once and (B) needs to be re-done every time you make a change anywhere. Fortunately, there is a better way!
`ggplot2` supports adding theme elements to an object that can then be reused as needed elsewhere. This is the same theory behind wrapping repeated operations into custom functions.
```{r std-theme}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
# Define core theme elements
theme_synthesis <- theme(legend.position = "none",
panel.background = element_blank(),
axis.line = element_line(color = "black"),
axis.text = element_text(size = 13)) # <1>
# Create a graph
ggplot(pie_crab, aes(y = water_temp, x = air_temp, color = size, size = size)) +
geom_point() +
theme_synthesis +
theme(legend.position = "right") # <2>
```
1. This theme element controls the text on the tick marks. `axis.title` controls the text in the _labels_ of the axes
2. As a bonus, subsequent uses of `theme()` will replace defaults defined in your earlier theme object. So, you can design a set of theme elements that are _usually_ appropriate and then easily change just some of them as needed
:::{.callout-note icon="false"}
#### Activity: Graph Creation (P3)
In a script, attempt the following:
- Remove all theme edits from the graph you made in the preceding activity and assign them to a separate object
- Then add that object to your graph
- Make a second (different) graph and add your consolidated theme object to that graph as well
:::
## Multi-Panel Graphs
It is sometimes the case that you want to make a single graph file that has multiple panels. For many of us, we might default to creating the separate graphs that we want, exporting them, and then using software like Microsoft PowerPoint to stitch those panels into the single image we had in mind from the start. However, as all of us who have used this method know, this is hugely cumbersome when your advisor/committee/reviewers ask for edits and you now have to redo all of the manual work behind your multi-panel graph.
Fortunately, there are two nice entirely scripted alternatives that you might consider: **Faceted graphs** and **Plot grids**. See below for more information on both.
:::{.panel-tabset}
### Facets
In a faceted graph, <u>every panel of the graph has the same aesthetics</u>. These are often used when you want to show the relationship between two (or more) variables but separated by some other variable. In synthesis work, you might show the relationship between your core response and explanatory variables but facet by the original study. This would leave you with one panel per study where each would show the relationship only at that particular study.
Let's check out an example.
```{r facet-1}
#| fig-align: center
#| fig-width: 6
#| fig-height: 6
ggplot(pie_crab, aes(x = date, y = size, color = site))+
geom_point(size = 2) +
facet_wrap(. ~ site) + # <1>
theme_bw() +
theme(legend.position = "none") # <2>
```
1. This is a `ggplot2` function that assumes you want panels laid out in a regular grid. There are other `facet_...` alternatives that let you specify row versus column arrangement. You could also facet by multiple variables by putting something to the left of the tilde
2. We can remove the legend because the site names are in the facet titles in the gray boxes
### Plot Grids
In a plot grid, <u>each panel is completely independent of all others</u>. These are often used in publications where you want to highlight several _different_ relationships that have some thematic connection. In synthesis work, your hypotheses may be more complicated than in primary research and such a plot grid would then be necessary to put all visual evidence for a hypothesis in the same location. On a practical note, plot grids are also a common way of circumventing figure number limits enforced by journals.
Let's check out an example that relies on the `cowplot` library.
```{r grid-1}
#| message: false
#| fig-align: center
#| fig-width: 9
#| fig-height: 5
# Load a needed library
library(cowplot)
# Create the first graph
crab_p1 <- ggplot(pie_crab, aes(x = site, y = size, fill = site)) + # <1>
geom_violin() +
coord_flip() + # <2>
theme_bw() +
theme(legend.position = "none")
# Create the second
crab_p2 <- ggplot(pie_crab, aes(x = air_temp, y = water_temp)) +
geom_errorbar(aes(ymax = water_temp + water_temp_sd, ymin = water_temp - water_temp_sd),
width = 0.1) +
geom_errorbarh(aes(xmax = air_temp + air_temp_sd, xmin = air_temp - air_temp_sd), # <3>
width = 0.1) +
geom_point(aes(fill = site), pch = 23, size = 3) +
theme_bw()
# Assemble into a plot grid
cowplot::plot_grid(crab_p1, crab_p2, labels = "AUTO", nrow = 1) # <4>
```
1. Note that we're assigning these graphs to objects!
2. This is a handy function for flipping X and Y axes without re-mapping the aesthetics
3. This geometry is responsible for _horizontal_ error bars (note the "h" at the end of the function name)
4. The `labels = "AUTO"` argument means that each panel of the plot grid gets the next sequential capital letter. You could also substitute that for a vector with labels of your choosing
:::
:::{.callout-note icon="false"}
#### Activity: Graph Creation (P4)
In a script, attempt the following:
- Assemble the two graphs you made in the preceding two activities into the appropriate type of multi-panel graph
:::
## Accessibility Considerations
After you've made the graphs you need, it is good practice to revisit them with to ensure that they are as accessible as possible. You can of course also do this during the graph construction process but it is sometimes less onerous to tackle as a penultimate step in the figure creation process. There are many facets to accessibility and we've tried to cover just a few of them below.
### Color Choice
One of the more well-known facets of accessibility in data visualization is choosing colors that are "colorblind safe". Such palettes still create distinctive colors for those with various forms of color blindness (e.g., deuteranomoly, protanomaly, etc.). The classic red-green heatmap for instance is very colorblind unsafe in that people with some forms of colorblindness cannot distinguish between those colors (hence the rise of the yellow-blue heatmap in recent years). Unforunately, the `ggplot2` default rainbow palette--while nice for exploratory purposes--_is not_ colorlbind sfae.
Some websites (such as [colorbewer2.org](https://colorbrewer2.org/#type=sequential&scheme=YlGnBu&n=9)) include a simple checkbox for colorblindness safety which automatically limits the listed options to those that are colorblind safe. Alternately, you could use a browser plug-in (such as [Let's get color blind](https://chromewebstore.google.com/detail/lets-get-color-blind/bkdgdianpkfahpkmphgehigalpighjck) on Google Chrome) to simulate colorblindness on a particular page.
One extreme approach you could take is to dodge this issue entirely and format your graphs such that color either isn't used at all or only conveys information that is also conveyed in another graph aesthetic. We don't necessarily recommend this as color--when the palette is chosen correctly--can be a really nice way of making information-dense graphs more informative and easily-navigable by viewers.
### Multiple Modalities
Related to the color conversation is the value of mapping multiple aesthetics to the same variable. By presenting information in multiple ways--even if that seems redundant--you enable a wider audience to gain an intuitive sense of what you're trying to display.
```{r multi-modal}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot(data = pie_crab, mapping = aes(x = latitude, y = size,
fill = site, shape = site)) + # <1>
geom_jitter(size = 2, width = 0.1, alpha = 0.6) +
scale_shape_manual(values = c(21:25, 21:25, 21:23)) + # <2>
theme_bw() +
theme(legend.title = element_blank())
```
1. In this graph we're mapping both the fill and shape aesthetics to site
2. This is a little cumbersome but there are only five 'fill-able' shapes in R so we need to reuse some of them to have a unique one for each site. Using fill-able shapes is nice because you get a crisp black border around each point. See `?pch` for all available shapes
In the above graph, even though the rainbow palette is not ideal for reasons mentioned earlier, it is now much easier to tell the difference between sites with similar colors. For instance, "NB", "NIB", and "PIE" are all shades of light blue/teal. Now that they have unique shapes it is dramatically easier to look at the graph and identify which points correspond to which site.
:::{.callout-warning icon="false"}
#### Discussion: Graph Accessibility
With a group discuss (some of) the following questions:
- What are other facets of accessibility that you think are important to consider when making data visualizations?
- What changes do you make to your graphs to increase accessibility?
- What changes _could_ you make going forward?
:::
### Presentation vs. Publication
One final element of accessibility to consider is the difference between a '_presentation_-quality' graph and a '_publication_-quality' one. While it may be tempting to create a single version of a given graph and use it in both contexts that is likely to be less effective in helping you to get your point across than making small tweaks to two separate versions of what is otherwise the same graph.
:::{.panel-tabset}
### Presentation-Focused
**Do:**
- Increase size of text/points **greatly**
- If possible, sit in the back row of the room where you'll present and look at your graphs from there
- _Consider_ adding graph elements that highlight certain graph regions
- Present summarized data (increases focus on big-picture trends and avoids discussion of minutiae)
- Map multiple aesthetics to the same variables
**Don't:**
- Use technical language / jargon
- Include _unnecessary_ background elements
- Use multi-panel graphs (either faceted or plot grid)
- If you have multiple graph panels, put each on its own slide!
```{r talk-graph}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot(crab_summary, aes(x = latitude, y = mean,
shape = reorder(site, latitude), # <1>
fill = reorder(site, latitude))) +
geom_vline(xintercept = 36.5, color = "black", linetype = 1) +
geom_vline(xintercept = 41.5, color = "black", linetype = 2) + # <2>
geom_errorbar(mapping = aes(ymax = mean + std_error, ymin = mean - std_error),
width = 0.2) +
geom_point(size = 4) +
scale_shape_manual(values = c(21:25, 21:25, 21:23)) +
labs(x = "Latitude", y = "Mean Crab Size (mm)") + # <3>
theme(legend.title = element_blank(),
axis.line = element_line(color = "black"),
panel.background = element_blank(),
axis.title = element_text(size = 17),
axis.text = element_text(size = 15))
```
1. We can use the `reorder` function to make the order of sites in the legend (from top to bottom) match the order of sites in the graph (from left to right)
2. Adding vertical lines at particular parts in the graph can make comparisons within the same graph easier
3. `labs` lets us customize the title and label text of a graph
### Publication-Focused
**Do:**
- Increase size of text/points **slightly**
- You want to be legible but you can more safely assume that many readers will be able to increase the zoom of their browser window if needed
- Present un-summarized data (with or without summarized points included)
- Many reviewers will want to get a sense for the "real" data so you should include unsummarized values wherever possible
- Use multi-panel graphs
- If multiple graphs "tell a story" together, then they should be included in the same file!
- Map multiple aesthetics to the same variables
- If publishing in a journal available in print, check to make sure your graph still makes sense in grayscale
- There are nice browser plug-ins (like [Grayscale the Web](https://chromewebstore.google.com/detail/grayscale-the-web-save-si/mblmpdpfppogibmoobibfannckeeleag) for Google Chrome) for this too
**Don't:**
- Include _unnecessary_ background elements
- Add graph elements that highlight certain graph regions
- You can--and should--lean more heavily on the text of your publication to discuss particular areas of a graph
```{r pub-graph}
#| fig-align: center
#| fig-width: 9
#| fig-height: 4
ggplot() +
geom_point(pie_crab, mapping = aes(x = latitude, y = size,
color = reorder(site, latitude)),
pch = 19, size = 1, alpha = 0.3) +
geom_errorbar(crab_summary, mapping = aes(x = latitude, y = mean,
ymax = mean + std_error,
ymin = mean - std_error),
width = 0.2) +
geom_point(crab_summary, mapping = aes(x = latitude, y = mean,
shape = reorder(site, latitude),
fill = reorder(site, latitude)),
size = 4) +
scale_shape_manual(values = c(21:25, 21:25, 21:23)) +
labs(x = "Latitude", y = "Mean Crab Carapace Width (mm)") + # <1>
theme(legend.title = element_blank(),
axis.line = element_line(color = "black"),
panel.background = element_blank(),
axis.title = element_text(size = 15),
axis.text = element_text(size = 13))
```
1. Here we are using a reasonable amount of technical language
:::
## Multivariate Visualization
If you are working with multivariate data (i.e., data where multiple columns are all response variables collectively) you may need to use visualization methods unique to that data structure. For more information, check out the [bonus multivariate visualization module](https://lter.github.io/ssecr/mod_multivar-viz.html).
## Maps
You may find it valuable to create a map as an additional way of visualizing data. Many synthesis groups do this--particularly when there is a strong spatial component to the research questions and/or hypotheses. Check out the [bonus spatial data module](https://lter.github.io/ssecr/mod_spatial.html) for more information on map-making if this is of interest!
## Additional Resources
### Papers & Documents
- National Center for Ecological Analysis and Synthesis (NCEAS). [Colorblind Safe Color Schemes](https://www.nceas.ucsb.edu/sites/default/files/2022-06/Colorblind%20Safe%20Color%20Schemes.pdf). **2022**.
### Workshops & Courses
- The Carpentries. [Data Analysis and Visualization in R for Ecologists: Data Visualization with `ggplot2`](https://datacarpentry.org/R-ecology-lesson/visualizing-ggplot.html). **2024**.
- The Carpentries. [Data Analysis and Visualization in Python for Ecologists: Making Plots with `plotnine`](https://datacarpentry.org/python-ecology-lesson/07-visualization-ggplot-python.html). **2024**.
- LTER Scientific Computing Team. [Coding in the Tidyverse: 'Visualize' Module](https://lter.github.io/workshop-tidyverse/visualize.html). **2023**.
### Websites
-