Shiny In Production | 13th October 2023
đź‘© Cara Thompson
👩‍💻 Love for patterns in music & language, and a fascination with the human brain %>%
Psychology PhD %>%
Analysis of postgraduate medical examinations %>%
Data Visualisation Consultant
đź’™ Helping others maximise the impact of their expertise
Find out more: cararthompson.com/about
{bakeoff}
by Alison Presmanes Hill
package::function("blah")
hello@cararthompson.com
Some data…
library(tidyverse)
all_the_bakes <- bakeoff::challenges %>%
select(-technical) %>%
pivot_longer(c(signature, showstopper)) %>%
pull(value) %>%
tolower()
key_components <- tibble(
component = c("Chocolate",
"Raspberry",
"GĂ©noise or Sponge",
"Rhubarb"),
count = c(sum(grepl("chocolat", all_the_bakes)),
sum(grepl("raspberr", all_the_bakes)),
sum(grepl("sponge|genoise|génoise", all_the_bakes)),
sum(grepl("rhubarb", all_the_bakes)))
)
key_components
# A tibble: 4 x 2
component count
<chr> <int>
1 Chocolate 206
2 Raspberry 76
3 GĂ©noise or Sponge 12
4 Rhubarb 30
A plot!
Mini tip - theme_minimal()
!
Let’s add a bit of text…
key_components %>%
ggplot(aes(x = component,
y = count,
fill = component)) +
geom_bar(stat = "identity") +
labs(title = "Everybody loves chocolate",
subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!") +
theme_minimal()
Let’s add a bit of text…
key_components %>%
ggplot(aes(x = component,
y = count,
fill = component)) +
geom_bar(stat = "identity") +
labs(title = "Everybody loves chocolate",
subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!") +
theme_minimal(base_size = 18)
Mini tip! Get the bars in order
key_components %>%
arrange(count) %>%
mutate(component = factor(component,
levels = component)) %>%
ggplot(aes(x = component,
y = count,
fill = component,
colour = component)) +
geom_bar(stat = "identity") +
labs(title = "Everybody loves chocolate",
subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!") +
theme_minimal(base_size = 18)
Mini tip! Avoid giving everyone a sore neck
key_components %>%
arrange(count) %>%
mutate(component = factor(component,
levels = component)) %>%
ggplot(aes(x = component,
y = count,
fill = component,
colour = component)) +
geom_bar(stat = "identity") +
labs(title = "Everybody loves chocolate",
subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!") +
theme_minimal(base_size = 18) +
coord_flip()
Mini tip! Use plain English
key_components %>%
arrange(count) %>%
mutate(component = factor(component,
levels = component)) %>%
ggplot(aes(x = component,
y = count,
fill = component,
colour = component)) +
geom_bar(stat = "identity") +
labs(title = "Everybody loves chocolate",
subtitle = "Out of our four key cake components, selected arbitrarily for the purpose of this demonstration, chocolate stood out as the most frequently used by far. No huge surprises there!",
y = "Number of bakes") +
theme_minimal(base_size = 18) +
coord_flip()
Perfectly functional - but…
Find out more: en.wikipedia.org/wiki/Bouba/kiki_effect
Predicted by sound properties - Passi & Arun, 2022
A mouth-watering palette!
Where we were…
Use text colour based on “anchor” colour
Override theme_minimal()
’s axis text colour
Use text colour(s) based on “anchor” colour
Increase difference between title and subtitle
Add some personality!
basic_bar_plot +
scale_fill_manual(values = component_colours) +
theme(text = element_text(family = "Cabin",
colour = "#4C3232"),
legend.position = "none",
axis.title.y = element_blank(),
plot.subtitle = element_text(size = 16),
plot.title = element_text(family = "OPTIAuvantGothic-DemiBold",
size = 24,
face = "bold",
colour = "#200000"),
axis.text = element_text(colour = "#4C3232"))
Getting fonts to work can be frustrating!
Install fonts locally, restart R Studio + 📦
{systemfonts}
({ragg}
+{textshaping}
) + Set graphics device to “AGG” + 🤞
knitr::opts_chunk$set(dev = “ragg_png”)
I ❤️ {ggtext}
basic_bar_plot +
scale_fill_manual(values = component_colours) +
theme(text = element_text(family = "Cabin",
colour = "#4C3232"),
legend.position = "none",
axis.title.y = element_blank(),
plot.subtitle = ggtext::element_textbox_simple(size = 16),
plot.title = element_text(family = "OPTIAuvantGothic-DemiBold",
size = 24,
face = "bold",
colour = "#200000"),
axis.text = element_text(colour = "#4C3232"))
I ❤️ {ggtext}
- but watch that alignment!
basic_bar_plot +
scale_fill_manual(values = component_colours) +
theme(text = element_text(family = "Cabin",
colour = "#4C3232"),
legend.position = "none",
axis.title.y = element_blank(),
plot.subtitle = ggtext::element_textbox_simple(size = 16,
vjust = 1),
plot.title = element_text(family = "OPTIAuvantGothic-DemiBold",
size = 24,
face = "bold",
colour = "#200000"),
axis.text = element_text(colour = "#4C3232"))
Allow some breathing space
basic_bar_plot +
scale_fill_manual(values = component_colours) +
theme(text = element_text(family = "Cabin",
colour = "#4C3232"),
legend.position = "none",
axis.title.y = element_blank(),
plot.subtitle = ggtext::element_textbox_simple(size = 16,
vjust = 1,
margin = margin(0, 0, 12, 0)),
plot.title = element_text(family = "OPTIAuvantGothic-DemiBold",
size = 24,
face = "bold",
colour = "#200000",
margin = margin(12, 0, 12, 0)),
axis.text = element_text(colour = "#4C3232"))
Our starting point
Mind the gap!
I ❤️ {ggtext}
Debugging mode
Debugging mode
Conditional alignments!
Conditional alignments & colour
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = component,
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
size = 7,
fill = "white")
Conditional alignments & colour
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = component,
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
size = 7,
fill = "white") +
scale_colour_identity()
Bring in our fonts and colours
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = component,
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
fill = NA,
box.colour = NA,
family = "Cabin",
size = 7,
fontface = "bold") +
scale_colour_identity()
Do we need that text?
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = component,
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
fill = NA,
box.colour = NA,
family = "Cabin",
size = 7,
fontface = "bold") +
scale_colour_identity() +
theme(axis.text.y = element_blank())
Could we make our labels more informative?
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = paste0(component,
"<br><span style='font-size:32pt'>",
count,
"</span>"),
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
fill = NA,
box.colour = NA,
family = "Cabin",
size = 7,
fontface = "bold") +
scale_colour_identity() +
theme(axis.text.y = element_blank())
Alignment tweak
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = paste0(component,
"<span style='font-size:32pt'><br>",
count,
"</span>"),
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
vjust = 0.45,
fill = NA,
box.colour = NA,
family = "Cabin",
size = 7,
fontface = "bold") +
scale_colour_identity() +
theme(axis.text.y = element_blank())
Do we need the rest of this?
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = paste0(component,
" bakes<span style='font-size:32pt'><br>",
count,
"</span>"),
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
vjust = 0.45,
fill = NA,
box.colour = NA,
family = "Cabin",
size = 7,
fontface = "bold") +
scale_colour_identity() +
theme(axis.text.y = element_blank(),
axis.text.x = element_blank(),
axis.title.x = element_blank(),
panel.grid = element_blank())
How would I actually say that?
bar_plot_text_hierarchy +
scale_y_continuous(expand = expansion(c(0, 0.02))) +
ggtext::geom_textbox(aes(label = paste0("<span style='font-size:32pt'><br>",
count,
"<br></span>",
component, " bakes"),
hjust = case_when(count < 50 ~ 0,
TRUE ~ 1),
halign = case_when(count < 50 ~ 0,
TRUE ~ 1),
colour = case_when(count < 50 ~ "#4C3232",
TRUE ~ "white")),
vjust = 0.45,
fill = NA,
box.colour = NA,
family = "Cabin",
size = 7,
fontface = "bold") +
scale_colour_identity() +
theme(axis.text.y = element_blank(),
plot.title.position = "plot",
axis.text.x = element_blank(),
axis.title.x = element_blank(),
panel.grid = element_blank())
“But that was easy, there was so little data!”
Try facets?
Try facets on less data?
Enter {gghighlight}
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = baker),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1) +
facet_grid(. ~ series) +
theme_minimal()
Enter {gghighlight}
- calculate_per_facet
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = baker),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
facet_grid(. ~ series) +
theme_minimal()
Enter {gghighlight}
- calculate_per_facet
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7) +
facet_grid(. ~ series) +
theme_minimal()
I ❤️ {geomtextpath}
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,
text_only = TRUE,
hjust = 0.3) +
theme_minimal()
And some more {ggtext}
fun!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3) +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = technical)) +
theme_minimal()
And some more {ggtext}
fun!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3) +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = technical,
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0))) +
theme_minimal()
And some more {ggtext}
fun!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3) +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_minimal()
And some more {ggtext}
fun!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_minimal()
Format the facet titles
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series,
labeller = as_labeller(function(x)
paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16))
Format the facet titles
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(16, 0, 0, 0))))
Very subtle decluttering - NA colours!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE,
unhighlighted_params =
list(color = "#D2CCCC")) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(16, 0, 0, 0))))
Adjust text hierarchy
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE,
unhighlighted_params = list(color = "#D2CCCC")) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
size = 7,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
size = 6,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(12, 0, 0, 0))))
Use colour!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE,
unhighlighted_params = list(color = "#D2CCCC")) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
size = 7,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
size = 6,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(12, 0, 0, 0))))
Use colour!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE,
unhighlighted_params = list(color = "#D2CCCC")) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker,
colour = series),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
size = 7,
family = "Cabin") +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0)),
box.colour = NA,
size = 6,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(12, 0, 0, 0))))
Use colour!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE,
unhighlighted_params = list(color = "#D2CCCC")) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker,
colour = series),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
size = 7,
family = "Cabin",
show.legend = FALSE) +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0),
colour = series),
box.colour = NA,
size = 6,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(12, 0, 0, 0))))
Use colour!
bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
ggplot(aes(x = episode,
y = -technical)) +
geom_path(aes(colour = series),
show.legend = FALSE) +
gghighlight::gghighlight(series_winner == 1,
calculate_per_facet = TRUE,
unhighlighted_params = list(color = "#D2CCCC")) +
scale_colour_gradient2(low = "#e04121", mid = "#f7238a",
high = "#ed9e00", midpoint = 7.1) +
facet_grid(. ~ series, labeller = as_labeller(function(x) paste("Series", x))) +
geomtextpath::geom_textpath(aes(label = baker,
colour = series),
vjust = -0.2,text_only = TRUE,
hjust = 0.3,
size = 7,
family = "Cabin",
show.legend = FALSE) +
ggtext::geom_textbox(data = bakeoff::bakes_raw %>%
filter(series %in% c(6:8)) %>%
left_join(bakeoff::bakers %>%
filter(series_winner == 1)) %>%
filter(series_winner == 1,
episode %in% c(1, 10)),
aes(label = paste0("#", technical),
hjust = case_when(episode == 1 ~ 1,
TRUE ~ 0),
halign = case_when(episode == 1 ~ 1,
TRUE ~ 0),
colour = series),
box.colour = NA,
size = 6,
family = "Cabin",
fill = NA) +
scale_x_continuous(expand = expansion(0.2)) +
theme_void() +
theme(strip.text = element_text(family = "Cabin", colour = "#200000",
size = 16,
margin = margin(c(12, 0, 0, 0))),
legend.position = "none")
Starting point
Sources: {bakeoff} + www.krishthebaker.com/bake-off-technicals
Reduce cognitive load: Plain English headers
Reduce cognitive load: Plain English dates - oops!
Reduce cognitive load: Plain English dates
Make numbers easy to compare: formatting
Make numbers easy to compare: monospace
Add text hierarchy: AvoiD Too MaNy CapiTalS!
Add text hierarchy: colour & size
Add text hierarchy and personality!
Make it easy to follow a line: stripe
Make it easy to follow a line: stripe or hover?
Reduce unnecessary eye movements: alignments
Reduce unnecessary eye movements: row heights (no wrap)
Reduce unnecessary eye movements: column widths
Reduce unnecessary eye movements: what’s the story?
Show vs tell: conditional colours?
Show vs tell: so much easier to compare!
Show vs tell: meaningful colours
But we should probably specify their meaning…
Technical challenges, star bakers, and number of viewers for episodes hosted by Mel & Sue and Noel & Co
Time to get interactive! {ggiraph}
ggiraph::girafe()
A (relatively easy) technical challenge
bakeoff::challenges %>%
select(-technical) %>%
pivot_longer(c(signature, showstopper)) %>%
mutate(choc_raspberry = case_when(grepl("[Cc]hocolate", value) & grepl("[Rr]aspberr", value) ~ "Both",
grepl("[Cc]hocolate", value) == T ~ "Chocolate",
grepl("[Rr]aspberry", value) == T ~ "Raspberry")) %>%
filter(!is.na(choc_raspberry)) %>%
ggplot() +
geom_rect(aes(xmin = -Inf, ymin = -Inf, xmax = Inf, ymax = Inf), fill = "#300300") +
geom_jitter(aes(x = 1, y = 1),
alpha = 0.7,
shape = 21,
size = 10,
colour = "#ec3f24",
fill = "#b41504") +
coord_polar() +
scale_colour_identity() +
scale_y_discrete(expand = c(0.05, 0)) +
labs(title = "Chocolate & Raspberry Bakes") +
theme_void() +
theme(plot.title = element_text(family = "Cabin", size = 24,
colour = "#200000", hjust = 0.5,
lineheight = 1.3))
A (relatively easy) technical challenge
choc_raspberry_bakes <- bakeoff::challenges %>%
select(-technical) %>%
pivot_longer(c(signature, showstopper)) %>%
mutate(choc_raspberry = case_when(grepl("[Cc]hocolate", value) & grepl("[Rr]aspberr", value) ~ "Both",
grepl("[Cc]hocolate", value) == T ~ "Chocolate",
grepl("[Rr]aspberry", value) == T ~ "Raspberry")) %>%
filter(!is.na(choc_raspberry)) %>%
ggplot() +
geom_rect(aes(xmin = -Inf, ymin = -Inf, xmax = Inf, ymax = Inf), fill = "#300300") +
ggiraph::geom_jitter_interactive(aes(x = 1, y = 1,
tooltip = value),
alpha = 0.7,
shape = 21,
size = 5,
colour = "#ec3f24",
fill = "#b41504") +
coord_polar() +
scale_colour_identity() +
scale_y_discrete(expand = c(0.05, 0)) +
labs(title = "Chocolate & Raspberry Bakes") +
theme_void() +
theme(plot.title = element_text(family = "Cabin", size = 24,
colour = "#200000", hjust = 0.5,
lineheight = 1.3))
A (relatively easy) technical challenge
But don’t hide the cutlery!
choc_raspberry_bakes <-
choc_raspberry_bakes +
labs(subtitle = "<br>Hover over the raspberries to see **who** created **what** in **which episode**") +
theme(plot.subtitle = ggtext::element_textbox_simple(family = "Cabin",
colour = "#4C3232",
halign = 0.5,
size = 16))
ggiraph::girafe(ggobj = choc_raspberry_bakes)
bakeoff::challenges %>%
select(-technical) %>%
pivot_longer(c(signature, showstopper)) %>%
mutate(tidy_full_ingredients = gsub("(.+?[^ |\\(])([A-Z].)", "\\1\n\\2", value),
tooltip_content = paste0(ifelse(!is.na(tidy_full_ingredients),
tidy_full_ingredients,
""),
"<br><br><span style='font-weight:bold'>", baker, " - S",
str_pad(series, 2, pad = "0"),
" E", str_pad(episode, 2, pad = "0"),
"</span>")) %>%
pull(tooltip_content) %>%
head(5)
[1] "Light Jamaican Black Cakewith Strawberries and Cream<br><br><span style='font-weight:bold'>Annetha - S01 E01</span>"
[2] "Red, White & Blue Chocolate Cake with Cigarellos, Fresh Fruit, and Cream<br><br><span style='font-weight:bold'>Annetha - S01 E01</span>"
[3] "Chocolate Orange Cake<br><br><span style='font-weight:bold'>David - S01 E01</span>"
[4] "Black Forest Floor Gateaux with Moulded Chocolate Leaves, Fallen Fruit and Chocolate Mushrooms Moulded from eggs<br><br><span style='font-weight:bold'>David - S01 E01</span>"
[5] "Caramel Cinnamon and Banana Cake<br><br><span style='font-weight:bold'>Edd - S01 E01</span>"
choc_raspberry_bakes <- bakeoff::challenges %>%
select(-technical) %>%
pivot_longer(c(signature, showstopper)) %>%
mutate(tidy_full_ingredients = gsub("(.+?[^ |\\(])([A-Z].)", "\\1\n\\2", value),
tooltip_content = paste0(ifelse(!is.na(tidy_full_ingredients),
tidy_full_ingredients,
""),
"<br><br><span style='font-weight:bold'>", baker, " - S",
str_pad(series, 2, pad = "0"),
" E", str_pad(episode, 2, pad = "0"),
"</span>")) %>%
mutate(choc_raspberry = case_when(grepl("[Cc]hocolate", value) & grepl("[Rr]aspberr", value) ~ "Both",
grepl("[Cc]hocolate", value) == T ~ "Chocolate",
grepl("[Rr]aspberry", value) == T ~ "Raspberry")) %>%
filter(!is.na(choc_raspberry)) %>%
ggplot() +
geom_rect(aes(xmin = -Inf, ymin = -Inf, xmax = Inf, ymax = Inf), fill = "#300300") +
ggiraph::geom_jitter_interactive(aes(x = 1, y = 1,
tooltip = tooltip_content),
alpha = 0.7,
shape = 21,
size = 5,
colour = "#ec3f24",
fill = "#b41504") +
labs(subtitle = "<br>Hover over the raspberries to see **who** created **what** in **which episode**") +
coord_polar() +
scale_colour_identity() +
scale_y_discrete(expand = c(0.05, 0)) +
labs(title = "Chocolate & Raspberry Bakes") +
theme_void() +
theme(plot.title = element_text(family = "Cabin", size = 24,
colour = "#200000", hjust = 0.5,
lineheight = 1.3),
plot.subtitle = ggtext::element_textbox_simple(family = "Cabin",
colour = "#4C3232",
halign = 0.5,
size = 16))
ggiraph::girafe(ggobj = choc_raspberry_bakes)
Starting point: our colours
Add padding
Fix the max width
Add lineheight spacing
🪄 Increase spacing between letters!
Back to the start
Add plot margin
Increase lineheight
Add margin under subtitle
hello@cararthompson.com | cararthomspon.com/talks