-
Notifications
You must be signed in to change notification settings - Fork 701
/
Copy pathfigure_titles_captions.Rmd
442 lines (383 loc) · 24.9 KB
/
figure_titles_captions.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
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
```{r echo = FALSE, message = FALSE}
# run setup script
source("_common.R")
library(ggrepel)
library(grid)
library(gridExtra)
library(gtable)
library(tibble)
library(lubridate)
```
# Titles, captions, and tables {#figure-titles-captions}
A data visualization is not a piece of art meant to be looked at only for its aesthetically pleasing features. Instead, its purpose is to convey information and make a point. To reliably achieve this goal when preparing visualizations, we have to place the data into context and provide accompanying titles, captions, and other annotations. In this chapter, I will discuss how to properly title and label figures. I will also discuss how to present data in table form.
## Figure titles and captions
One critical component of every figure is the figure title. Every figure needs the title. The job of the title is to accurately convey to the reader what the figure is about, what point it makes. However, the figure title may not necessarily appear where you were expecting to see it. Consider Figure \@ref(fig:corruption-development). It's title is "Corruption and human development: The most developed countries experience the least corruption." This title is not shown above the figure, however. Instead, the title is provided as the first part of the caption block, underneath the figure display. This is the style I am using throughout this book. I consistently show figures without integrated titles and with separate captions. (One exception are the stylized plot examples in Chapter \@ref(directory-of-visualizations), which instead have titles and no captions.)
(ref:corruption-development) Corruption and human development: The most developed countries experience the least corruption. This figure was inspired by a posting in @Economist-corruption. Data sources: Transparency International & UN Human Development Report
```{r corruption-development, fig.width = 5*6/4.2, fig.asp = 0.7, fig.cap = '(ref:corruption-development)'}
country_highlight <- c("Germany", "Norway", "United States", "Greece", "Singapore", "Rwanda", "Russia", "Venezuela", "Sudan", "Iraq", "Ghana", "Niger", "Chad", "Kuwait", "Qatar", "Myanmar", "Nepal", "Chile", "Argentina", "Japan", "China")
corruption %>% filter(year == 2015) %>% na.omit() %>%
mutate(region = case_when(
region == "Middle East and North Africa" ~ "Middle East\nand North Africa",
region == "Europe and Central Asia" ~ "Europe and\nCentral Asia",
region == "Sub Saharan Africa" ~ "Sub-Saharan\nAfrica",
TRUE ~ region),
label = ifelse(country %in% country_highlight, country, "")
) %>%
ggplot(aes(cpi, hdi)) +
geom_smooth(
aes(color = "y ~ log(x)", fill = "y ~ log(x)"),
method = 'lm', formula = y~log(x), se = FALSE, fullrange = TRUE
) +
geom_point(
aes(color = region, fill = region),
size = 2.5, alpha = 0.5, shape = 21
) +
geom_text_repel(
aes(label = label), color = "black", size = 10/.pt,
point.padding = 0.1, box.padding = .6, force = 1.,
min.segment.length = 0, seed = 7654,
family = dviz_font_family
) +
scale_color_OkabeIto(name = NULL, order = c(1:5, 8), darken = 0.3) +
scale_fill_OkabeIto(name = NULL, order = c(1:5, 8)) +
scale_y_continuous(
limits = c(0.3, 1.05), breaks = c(0.2, 0.4, 0.6, 0.8, 1.0),
expand = c(0, 0),
name = "Human Development Index, 2015\n(1.0 = most developed)"
) +
scale_x_continuous(
limits = c(10, 95),
breaks = c(20, 40, 60, 80, 100),
expand = c(0, 0),
name = "Corruption Perceptions Index, 2015 (100 = least corrupt)"
) +
guides(
color = guide_legend(
nrow = 1,
override.aes = list(
linetype = c(rep("blank", 5), "solid"),
shape = c(rep(21, 5), NA)
)
)
) +
theme_dviz_hgrid(12, rel_small = 1) +
theme(
legend.position = "top",
legend.justification = "right",
legend.text = element_text(size = 10),
legend.box.spacing = unit(0, "pt")
) -> plot_corrupt_base
## for some reason grid::forceGrob creates an empty plot, not sure why
#cur_dev <- grDevices::dev.cur()
#cowplot::png_null_device(width = 7, height = 4.9)
#null_dev <- dev.cur()
#grob_corrupt_base <- grid::forceGrob(ggplotGrob(plot_corrupt_base))
#null <- grDevices::dev.off(null_dev)
#if (cur_dev > 1 ) null <- grDevices::dev.set(cur_dev)
#ggdraw(grob_corrupt_base)
ggsave("figures/corruption_plot_base.png", plot_corrupt_base, width = 7, height = 4.9,
dpi = 600)
ggdraw() + draw_image("figures/corruption_plot_base.png")
```
Alternatively, I could incorporate the figure title---as well as other elements of the caption, such as the data source statement---into the main display (Figure \@ref(fig:corruption-development-infographic)). In a direct comparison, you may find Figure \@ref(fig:corruption-development-infographic) more attractive than Figure \@ref(fig:corruption-development), and you may wonder why I am choosing the latter style throughout this book. I do so because the two styles have different application areas, and figures with integrated titles are not appropriate for conventional book layouts. The underlying principle is that a figure can have only one title. Either the title is integrated into the actual figure display or it is provided as the first element of the caption underneath the figure. And, if a publication is laid out such that each figure has a regular caption block underneath the display item, then the title *must* be provided in that block of text. For this reason, in the context of conventional book or article publishing, we do not normally integrate titles into figures. Figures with integrated titles, subtitles, and data source statements are appropriate, however, if they are meant to be used as stand-alone infographics or to be posted on social media or on a web page without accompanying caption text.
(ref:corruption-development-infographic) Infographic version of Figure \@ref(fig:corruption-development). The title, subtitle, and data source statements have been incorporated into the figure. This figure could be posted on the web as is or otherwise used without separate caption block.
```{r corruption-development-infographic, fig.width = 5*6/4.2, fig.asp = 4.9*(0.12+1+.07)/7, fig.cap = '(ref:corruption-development-infographic)'}
plot_corrupt_title <- ggdraw() +
labs(title = "Corruption and human development",
subtitle = "The most developed countries experience the least corruption") +
theme_dviz_map(12, rel_small = 1) +
theme(
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 12),
plot.margin = margin(6, 1.5, 0, 1.5)
)
plot_corrupt_caption <- ggplot() +
labs(caption = "Data sources: Transparency International & UN Human Development Report") +
theme_dviz_map(12) +
theme(plot.margin = margin(0, 1.5, 6, 1.5))
plot_grid(
plot_corrupt_title,
ggdraw() + draw_image("figures/corruption_plot_base.png"),
plot_corrupt_caption,
ncol = 1, rel_heights = c(.12, 1, .07)
)
```
```{block type='rmdtip', echo=TRUE}
If your document layout uses caption blocks underneath each figure, then place the figure titles as the first element of each caption block, not on top of the figures.
```
One of the most common mistakes I see in figure captions is the omission of a proper figure title as the first element of the caption. Take a look back at the caption to Figure \@ref(fig:corruption-development). It begins with "Corruption and human development." It *does not* begin with "This figure shows how corruption is related to human development." The first part of the caption is always the title, not a description of the contents of the figure. A title does not have to be a complete sentence, though short sentences making a clear assertion can serve as titles. For example, for Figure \@ref(fig:corruption-development), a title such as "The most developed countries are the least corrupt" would have worked fine.
## Axis and legend titles
Just like every plot needs a title, axes and legends need titles as well. (Axis titles are often colloquially referred to as *axis labels*.) Axis and legend titles and labels explain what the displayed data values are and how they map to plot aesthetics.
To present an example of a plot where all axes and legends are appropriately labeled and titled, I have taken the blue jay dataset discussed at length in Chapter \@ref(visualizing-associations) and visualized it as a bubble plot (Figure \@ref(fig:blue-jays-scatter-bubbles2)). In this plot, the axis titles clearly indicate that the *x* axis shows body mass in grams and the *y* axis shows head length in millimeters. Similarly, the legend titles show that point coloring indicates the birds' sex and point size indicates the birds' skull size in millimeters. I emphasize that for all numerical variables (body mass, head length, and skull size) the relevant titles not only state the variables shown but also the units in which the variables are measured. This is good practice and should be done whenever possible. Categorical variables (such as sex) do not require units.
(ref:blue-jays-scatter-bubbles2) Head length versus body mass for 123 blue jays. The birds' sex is indicated by color, and the birds' skull size by symbol size. Head-length measurements include the length of the bill while skull-size measurements do not. Data source: Keith Tarvin, Oberlin College
```{r blue-jays-scatter-bubbles2, fig.asp = 3/4, fig.cap='(ref:blue-jays-scatter-bubbles2)'}
blue_jays$sex <- ifelse(blue_jays$KnownSex == "F", "female birds", "male birds")
blue_jays$sex <- factor(blue_jays$sex, levels = c("male birds", "female birds"))
ggplot(blue_jays, aes(Mass, Head, size = Skull, fill = KnownSex)) +
geom_point(pch = 21, color = "white") +
scale_x_continuous(name = "body mass (g)") +
scale_y_continuous(name = "head length (mm)", breaks = c(52, 54, 56, 58, 60)) +
scale_fill_manual(
values = c(F = "#D55E00", M = "#0072B2"),
labels = c("female ", "male"),
name = "sex",
guide = guide_legend(
direction = "horizontal",
title.position = "top",
title.hjust = 0.5,
label.position = "right",
keyheight = grid::unit(19, "pt"),
order = 1,
override.aes = list(size = 4)
)
) +
scale_radius(
name = "skull size (mm)",
range = c(2, 7),
limits = c(28, 34),
breaks = c(28, 30, 32, 34),
labels = c("28 ", "30 ", "32 ", "34"),
guide = guide_legend(
direction = "horizontal",
title.position = "top",
title.hjust = 0.5,
label.position = "right",
order = 2,
override.aes = list(fill = "gray40")
)
) +
theme_dviz_grid() +
theme(
legend.margin = margin(0, 0, 0, 20),
legend.position = "top",
legend.box = "horizontal",
legend.box.spacing = grid::unit(0, "pt"),
legend.justification = c(1, 0),
legend.spacing.x = unit(2, "pt"),
legend.spacing.y = unit(2, "pt"),
legend.background = element_rect(fill = "white", color = NA),
legend.key.width = unit(10, "pt")
)
```
There are cases, however, when axis or legend titles can be omitted, namely when the labels themselves are fully explanatory. For example, a legend showing two differently colored dots labeled "female" and "male" clearly indicates that color encodes sex. The title "sex" is not required to clarify this fact, and indeed throughout this book I have often omitted the legend title for legends indicating sex or gender (see e.g. Figures \@ref(fig:titanic-passengers-by-class-sex), \@ref(fig:blue-jays-scatter-sex), or \@ref(fig:titanic-passenger-breakdown)). Similarly, country names will generally not require a title stating what they are (Figure \@ref(fig:Americas-life-expect)), nor will movie titles (Figure \@ref(fig:boxoffice-vertical)) or years (Figure \@ref(fig:tech-stocks-minimal-labeling)).
(ref:tech-stocks-minimal-labeling) Stock price over time for four major tech companies. The stock price for each company has been normalized to equal 100 in June 2012. This figure is a slightly modified version of Figure \@ref(fig:tech-stocks-good-legend) in Chapter \@ref(redundant-coding). Here, the *x* axis representing time does not have a title. It is clear from the context that the numbers 2013, 2014, etc. refer to years.
```{r tech-stocks-minimal-labeling, fig.cap = '(ref:tech-stocks-minimal-labeling)'}
price_plot_base <- ggplot(tech_stocks, aes(x = date, y = price_indexed, color = ticker)) +
geom_line(na.rm = TRUE) +
scale_color_manual(
values = c("#000000", "#E69F00", "#56B4E9", "#009E73"),
name = "",
breaks = c("FB", "GOOG", "MSFT", "AAPL"),
labels = c("Facebook", "Alphabet", "Microsoft", "Apple")
) +
scale_x_date(
limits = c(ymd("2012-06-01"), ymd("2017-05-31")),
expand = c(0,0)
) +
scale_y_continuous(
limits = c(0, 560),
expand = c(0,0)
) +
theme_dviz_hgrid() +
theme(plot.margin = margin(3, 7, 3, 1.5))
price_plot_base + xlab(NULL) + ylab("stock price, indexed")
```
However, we have to be careful when omitting axis or legend titles, because it is easy to misjudge what is and isn't obvious from the context. I frequently see graphs in the popular press that push omitting axis titles to a point that would make me uncomfortable. For example, some publications might produce a figure such as Figure \@ref(fig:tech-stocks-minimal-labeling-bad), assuming that the meaning of the axes is clear from the plot title and subtitle (here: "stock price over time for four major tech companies" and "the stock price for each company has been normalized to equal 100 in June 2012"). I disagree with the perspective that context clearly defines the axes. Because the caption typically doesn't include words such as "the *x*/*y* axis shows", some amount of guesswork is always required to interpret the figure. In my own experience, figures without properly labeled axes tend to leave me with a nagging feeling of uncertainty---even if I'm 95% certain I understand what is shown, I don't feel 100% certain. As a general principle, I think it is a bad practice to make your readers guess what you mean. Why would you want to create a feeling of uncertainty in your readers?
(ref:tech-stocks-minimal-labeling-bad) Stock price over time for four major tech companies. The stock price for each company has been normalized to equal 100 in June 2012. This variant of Figure \@ref(fig:tech-stocks-minimal-labeling) has been labeled as "bad" because the *y* axis now does not have a title either, and what the values shown along the *y* axis represent is not immediately obvious from the context.
```{r tech-stocks-minimal-labeling-bad, fig.cap = '(ref:tech-stocks-minimal-labeling-bad)'}
stamp_bad(
price_plot_base + xlab(NULL) + ylab(NULL)
)
```
On the flip side, we can overdo the labeling. If the legend lists the names of four well-known companies, the legend title "company" is redundant and doesn't add anything useful (Figure \@ref(fig:tech-stocks-labeling-ugly)). Similarly, even though we generally should report units for all quantitative variables, if the *x* axis shows a few recent years titling it as "time (years AD)" is awkward (Figure \@ref(fig:tech-stocks-labeling-ugly)).
(ref:tech-stocks-labeling-ugly) Stock price over time for four major tech companies. The stock price for each company has been normalized to equal 100 in June 2012. This variant of Figure \@ref(fig:tech-stocks-minimal-labeling) has been labeled as "ugly" because it is labeled excessively. In particular, providing a unit ("years AD") for the values along the *x* axis is awkward.
```{r tech-stocks-labeling-ugly, fig.cap = '(ref:tech-stocks-labeling-ugly)'}
stamp_ugly(
price_plot_base + xlab("time (years AD)") + ylab("stock price, indexed\n(100/share price on Jun 1, 2012)") +
scale_color_manual(
values = c("#000000", "#E69F00", "#56B4E9", "#009E73"),
name = "company",
breaks = c("FB", "GOOG", "MSFT", "AAPL"),
labels = c("Facebook", "Alphabet", "Microsoft", "Apple")
) +
theme(legend.title.align = 0.5)
)
```
Finally, in some cases it is acceptable to omit not only the axis title but the entire axis.
Pie charts typically don't have explicit axes (e.g., Figure \@ref(fig:bundestag-pie)), and neither do treemaps (Figure \@ref(fig:bridges-treemap)). Mosaic plots or bar charts can be shown without one or both axes if the meaning of the plot is otherwise clear (Figures \@ref(fig:bridges-mosaic) and \@ref(fig:titanic-passengers-by-class-sex)). Omitting explicit axes with axis ticks and tick labels signals to the reader that the qualitative features of the graph are more important than the specific data values.
## Tables
Tables are an important tool for visualizing data. Yet because of their apparent simplicity, they may not always receive the attention they need. I have shown a handful of tables throughout this book, for example Tables \@ref(tab:boxoffice-gross), \@ref(tab:titanic-ages), and \@ref(tab:color-codes). Take a moment and locate these tables, look how they are formatted, and compare them to a table you or a colleague has recently made. In all likelihood, there are important differences. In my experience, absent proper training in table formatting, few people will instinctively make the right formatting choices. In self-published documents, poorly formatted tables are even more prevalent than poorly designed figures. Also, most software commonly used to create tables provides defaults that are not recommended. For example, my version of Microsoft Word provides 105 pre-defined table styles, and of these at least 70--80 violate some of the table rules I'm going to discuss here. So if you pick a Microsoft Word table layout at random, you have an 80% chance of picking one that has issues. And if you pick the default, you will end up with a poorly formatted table every time.
Some key rules for table layout are the following:
1. Do not use vertical lines.
2. Do not use horizontal lines between data rows. (Horizontal lines as separator between the title row and the first data row or as frame for the entire table are fine.)
3. Text columns should be left aligned.
4. Number columns should be right aligned and should use the same number of decimal digits throughout.
5. Columns containing single characters are centered.
6. The header fields are aligned with their data, i.e., the heading for a text column will be left aligned and the heading for a number column will be right aligned.
Figure \@ref(fig:table-examples) reproduces Table \@ref(tab:boxoffice-gross) from Chapter \@ref(visualizing-amounts) in four diferent ways, two of which (a, b) violate several of these rules and two of which (c, d) do not.
(ref:table-examples) Examples of poorly and appropriately formatted tables, using the data from Table \@ref(tab:boxoffice-gross) in Chapter \@ref(visualizing-amounts). (a) This table violates numerous conventions of proper table formatting, including using vertical lines, using horizontal lines between data rows, and using centered data columns. (b) This table suffers from all problems of Table (a), and in addition it creates additional visual noise by alternating between very dark and very light rows. Also, the table header is not strongly visually separated from the table body. (c) This is an appropriately formatted table with a minimal design. (d) Colors can be used effectively to group data into rows, but the color differences should be subtle. The table header can be set off by using a stronger color. Data source: Box Office Mojo (http://www.boxofficemojo.com/). Used with permission
```{r table-examples, fig.width = 5*6/4.2, fig.asp = 0.51, fig.cap = '(ref:table-examples)'}
boxoffice <- tibble(
Rank = 1:5,
Title = c("Star Wars: The Last Jedi", "Jumanji: Welcome to the Jungle ", "Pitch Perfect 3", "The Greatest Showman", "Ferdinand"),
Amount = c("$71,565,498", "$36,169,328", "$19,928,525", "$8,805,843", "$7,316,746")
)
boxoffice_ctr <- tibble(
Rank = 1:5,
Title = c("Star Wars: The Last Jedi", " Jumanji: Welcome to the Jungle ", "Pitch Perfect 3", "The Greatest Showman", "Ferdinand"),
Amount = c("$71,565,498", "$36,169,328", "$19,928,525", "$8,805,843", "$7,316,746")
)
table_base_size = 10
zgrob <- function(...) ggplot2::zeroGrob()
tt1 <- ttheme_minimal(
base_size = table_base_size,
base_family = dviz_font_family,
core = list(
fg_params = list(
fontface = rep(c(1L, 3L, 1L), each = 5)
),
bg_params = list(
col = "black",
lwd = 1
)
),
colhead = list(
fg_params = list(
fontface = 1L,
fontfamily = dviz_font_family_bold
),
bg_params = list(
col = "black",
lwd = 1
)
),
rowhead = list(fg_fun = zgrob, bg_fun = zgrob)
)
tt2 <- ttheme_default(
base_size = table_base_size,
base_family = dviz_font_family,
core = list(
fg_params = list(
fontface = rep(c(1L, 3L, 1L), each = 5),
col = c("white", "black")
),
bg_params = list(
col = "black",
lwd = 0.5,
fill = c("grey45", "grey85")
)
),
colhead = list(
fg_params = list(
fontface = 1L,
fontfamily = dviz_font_family_bold
),
bg_params = list(
col = "black",
lwd = 0.5,
fill = "grey85"
)
),
rowhead = list(fg_fun = zgrob, bg_fun = zgrob)
)
tt3 <- ttheme_minimal(
base_size = table_base_size,
base_family = dviz_font_family,
padding = unit(c(4, 3.2), "mm"),
core = list(
fg_params = list(
fontface = rep(c(1L, 3L, 1L), each = 5),
hjust = rep(c(0.5, 0, 1), each = 5),
x = rep(c(0.5, 0.1, 0.9), each = 5)
),
bg_params = list(
col = NA
)
),
colhead = list(
fg_params = list(
hjust = c(0.5, 0, 1),
x = c(0.5, 0.1, 0.9),
fontface = 1L,
fontfamily = dviz_font_family_bold
),
bg_params = list(
col = NA
)
),
rowhead = list(fg_fun = zgrob, bg_fun = zgrob)
)
tt4 <- ttheme_default(
base_size = table_base_size,
base_family = dviz_font_family,
core = list(
fg_params = list(
fontface = rep(c(1L, 3L, 1L), each = 5),
col = "black",
hjust = rep(c(0.5, 0, 1), each = 5),
x = rep(c(0.5, 0.1, 0.9), each = 5)
),
bg_params = list(
col = NA,
fill = c('#D9E0EF', '#C2CCE3') #c("grey95", "grey85")
)
),
colhead = list(
fg_params = list(
col = "white",
hjust = c(0.5, 0, 1),
x = c(0.5, 0.1, 0.9),
fontface = 1L,
fontfamily = dviz_font_family_bold
),
bg_params = list(
col = NA,
fill = "#4069A6"#"grey65"
)
),
rowhead = list(fg_fun = zgrob, bg_fun = zgrob)
)
# horizontal line to be used as separator
hline_top <- segmentsGrob(
x0 = unit(0,"npc"),
y0 = unit(1,"npc"),
x1 = unit(1,"npc"),
y1 = unit(1,"npc"),
gp = gpar(lwd = 0.75, col = "black")
)
hline_bottom <- segmentsGrob(
x0 = unit(0,"npc"),
y0 = unit(0,"npc"),
x1 = unit(1,"npc"),
y1 = unit(0,"npc"),
gp = gpar(lwd = 0.75, col = "black")
)
t1 <- tableGrob(boxoffice_ctr, rows = rep("", nrow(boxoffice)), theme = tt1)
t1$layout$clip <- "off"
t1 <- gtable_add_padding(t1, margin(14, 16, 0, -2))
t2 <- tableGrob(boxoffice_ctr, rows = rep("", nrow(boxoffice)), theme = tt2)
t2$layout$clip <- "off"
t2 <- gtable_add_padding(t2, margin(14, 16, 0, -2))
t3 <- tableGrob(boxoffice, rows = rep("", nrow(boxoffice)), theme = tt3)
t3 <- gtable_add_grob(t3,
grobs = grobTree(hline_top, hline_bottom),
t = 1, b = 1, l = 2, r = 4)
t3 <- gtable_add_grob(t3,
grobs = hline_bottom,
t = 6, b = 6, l = 2, r = 4)
t3$layout$clip <- "off"
t3 <- gtable_add_padding(t3, margin(14, 14, 0, -7))
t4 <- tableGrob(boxoffice, rows = rep("", nrow(boxoffice)), theme = tt4)
t4$layout$clip <- "off"
t4 <- gtable_add_padding(t4, margin(14, 16, 0, -2))
plot_grid(
stamp_ugly(t1), NULL, stamp_ugly(t2),
NULL, NULL, NULL,
t3, NULL, t4,
rel_widths = c(1, 0.06, 1),
rel_heights = c(1, 0.08, 1),
labels = c("a", "", "b", "", "", "", "c", "", "d")
)
```
When authors draw tables with horizontal lines between data rows, the intent is usually to help the eye follow individual lines. However, unless the table is very wide and sparse, this visual aid is not normally needed. We don't draw horizontal lines between rows in a piece of regular text either. The cost of horizontal (or vertical) lines is visual clutter. Compare parts (a) and (c) of Figure \@ref(fig:table-examples). Part (c) is much easier to read than part (a). If we feel that a visual aid separating table rows is necessary, then alternating lighter and darker shading of rows tends to work well without creating much clutter (Figure \@ref(fig:table-examples)d).
Finally, there is a key distinction between figures and tables in where the caption is located relative to the display item. For figures, it is customary to place the caption underneath, whereas for tables it is customary to place it above. This caption placement is guided by the way in which readers process figures and tables. For figures, readers tend to first look at the graphical display and then read the caption for context, hence the caption makes sense below the figure. By contrast, tables tend to be processed like text, from top to bottom, and reading the table contents before reading the caption will frequently not be useful. Hence, captions are placed above the table.