Workshops for Ukraine | #rstats #ggplot | 20th April 2023
š© Cara Thompson
š©āš» A love for patterns |>
Psychology PhD |>
Analysis of postgraduate medical examinations |>
Data Visualisation Consultant
š Helping others maximise the impact of their expertise
Find out more: cararthompson.com/about
To equip you with some design tips and coding tricks to enhance the storytelling capabilities of your plots.
R
code{ggtext}
, {geomtextpath}
and {gghighlight}
Find out more: en.wikipedia.org/wiki/Bouba/kiki_effect
The penguins had a baking competition to see which species could make the best banana loaf. Each species was given bananas of a different level of ripeness.
The penguins had a baking competition to see which species could make the best banana loaf. Each species was given bananas of a different level of ripeness.
The Adelie penguins decided to experiment with different quantities of banana in their mix. Each island chose a different quantity.
The Adelie penguins decided to experiment with different quantities of unripe banana in their mix. Each island chose a different quantity.
They decided to go on a retreat to plan their bakes in different locations
Each species was allowed to invite a different mentorā¦
ā¦ and to choose a type of snack between practice bakes
The penguins also baked their cakes for different amounts of time. Here are the mean durations per species.
The penguins also baked their cakes for different amounts of time. Here are the mean durations per species.
{gghighlight}
, Tee pipe and curved arrowsAll about making it easier to remember what is what
Picking colours is hard! Let others help you!
Find out more: blog.datawrapper.de/colors-for-data-vis-style-guides/
Quick tip: Viewing your colours
Quick tip: Naming and viewing your colours
A few things to bear in mind
colorblindr::cvd_grid()
A few things to bear in mind
colorblindr::cvd_grid()
A few things to bear in mind
A few things to bear in mind
A few things to bear in mind
A few things to bear in mind
imagecolorpicker.com
monochromeR::view_palette()
colorblindr::cvd_grid()
See you in 5 minutes! š šØ ā
05:00
Using the ToothGrowth dataset
package::function()
šµļø?ToothGrowth
)With a few tips along the way
With a few tips along the way
With a few tips along the way
Mini tip: get rid of abbreviations
ToothGrowth %>%
mutate(supplement =
case_when(supp == "OJ" ~ "Orange Juice",
supp == "VC" ~ "Vitamin C",
TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
ggplot(aes(x = dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
position = "dodge",
colour = "#FFFFFF",
size = 2)
Mini tip: theme_minimal()
ToothGrowth %>%
mutate(supplement =
case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
ggplot(aes(x = dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
position = "dodge",
colour = "#FFFFFF",
size = 2) +
theme_minimal()
Turning Dose into a categorical variable (fear not!)
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
position = "dodge",
colour = "#FFFFFF",
size = 2) +
theme_minimal()
Turning Dose into a categorical variable (fear not!) + facetting
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Adding some text (finally!)
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Legend + facet strip + colour + titleā¦ Wait, which one is which?
Generating a colour palette, starting with orange juice! #fab909
[1] "#DB5A05" "#E93603" "#F71201"
[1] "#3C6B30" "#0C1509"
[1] "#0C1509" "#323A30" "#595F57" "#80857F" "#A7AAA6" "#CED0CD"
Creating a named vector
Back to the plot!
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Add in our colours
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = c("#fab909",
"#E93603")) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Add in our colours - wait, what?
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose),
supplement =
factor(supplement,
levels = c("Vitamin C",
"Orange Juice"))) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = c("#fab909",
"#E93603")) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Add in our colours - named vector to the rescue!
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose),
supplement =
factor(supplement,
levels = c("Vitamin C",
"Orange Juice"))) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Add in our colours - named vector to the rescue!
Key advantages
ggtext::element_markdown()
later in this workshopGet rid of unused colours
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Get rid of unused colours
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette,
limits = force) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Use transparency to indicate dose
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(aes(alpha = dose),
stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette, limits = force) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Use transparency to indicate dose - within limits
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(aes(alpha = dose),
stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette, limits = force) +
scale_alpha(range = c(0.33, 1)) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
What is the dose unit again? ?ToothGrowth
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(aes(alpha = dose),
stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette, limits = force) +
scale_alpha(range = c(0.33, 1)) +
scale_x_discrete(breaks = c("0.5", "1", "2"),
labels = function(x)
paste0(x, " mg/day")) +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal()
Legend has always been redundant!
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(aes(alpha = dose),
stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette, limits = force) +
scale_alpha(range = c(0.33, 1)) +
facet_wrap(supplement ~ ., ncol = 1) +
scale_x_discrete(breaks = c("0.5", "1", "2"), labels = function(x) paste0(x, " mg/day")) +
theme_minimal() +
theme(legend.position = "none")
And I find this so much less confusing!
ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose,
y = mean_length,
fill = supplement)) +
geom_bar(aes(alpha = dose),
stat = "identity",
colour = "#FFFFFF",
size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette, limits = force) +
scale_alpha(range = c(0.4, 1)) +
scale_x_discrete(breaks = c("0.5", "1", "2"), labels = function(x) paste0(x, " mg/day")) +
coord_flip() +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal() +
theme(legend.position = "none")
So much clearer, and we havenāt even done any annotating!
{gghighlight}
, Tee pipe and curved arrowsFind out more: https://www.interaction-design.org/
Time to start playing with theme()
!
basic_plot <- ToothGrowth %>%
mutate(supplement = case_when(supp == "OJ" ~ "Orange Juice", supp == "VC" ~ "Vitamin C", TRUE ~ as.character(supp))) %>%
group_by(supplement, dose) %>%
summarise(mean_length = mean(len)) %>%
mutate(categorical_dose = factor(dose)) %>%
ggplot(aes(x = categorical_dose, y = mean_length, fill = supplement)) +
geom_bar(aes(alpha = dose), stat = "identity", colour = "#FFFFFF", size = 2) +
labs(x = "Dose",
y = "Mean length (mm)",
title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C",
subtitle = "With the highest dose, the mean recorded length was almost identical.") +
scale_fill_manual(values = vit_c_palette, limits = force) +
scale_alpha(range = c(0.4, 1)) +
scale_x_discrete(breaks = c("0.5", "1", "2"), labels = function(x) paste0(x, " mg/day")) +
coord_flip() +
facet_wrap(supplement ~ ., ncol = 1) +
theme_minimal(base_size = 15)
basic_plot
Time to start playing with theme()
!
Time to start playing with theme()
!
Time to start playing with theme()
!
Time to start playing with theme()
!
Move away from the default fonts
Move away from the default fonts
basic_plot +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
plot.title = element_text(colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta"),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold"),
axis.text = element_text(colour = vit_c_palette["light_text"]))
Choosing fonts can be tricky!
Find out more: pimpmytype.com/font-matrix/
Getting custom fonts to work can be frustrating!
Install fonts locally, restart R Studio + š¦
{systemfonts}
({ragg}
+{textshaping}
) + Set graphics device to āAGGā + š¤
See what fonts are available on your device
systemfonts::system_fonts() |> View()
A Dataviz Design System: A simple set of rules to follow, to effortlessly make your visualisations look on brand, every time you go to create a new plot.
Give everything some space to breathe
basic_plot +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
plot.title = element_text(colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta"),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold"),
axis.text = element_text(colour = vit_c_palette["light_text"]))
Give everything some space to breathe
basic_plot +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
plot.title = element_text(colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = element_text(size = rel(1.1), lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = vit_c_palette["light_text"]))
Remove unnecessary text
basic_plot +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
axis.title.y = element_blank(),
plot.title = element_text(colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = element_text(size = rel(1.1), lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = vit_c_palette["light_text"]))
Watch out for that title!
basic_plot +
labs(title = "In smaller doses, Orange Juice was associated with greater mean tooth growth,
compared to equivalent doses of Vitamin C") +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
axis.title.y = element_blank(),
plot.title = element_text(colour = vit_c_palette["dark_text"],
size = 36,
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = element_text(size = rel(1.1), lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = vit_c_palette["light_text"]))
Watch out for that title!
basic_plot +
labs(title = "In smaller doses, Orange Juice was associated with greater mean tooth growth, compared to equivalent doses of Vitamin C") +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
axis.title.y = element_blank(),
plot.title = element_text(colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = element_text(size = rel(1.1), lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = vit_c_palette["light_text"]))
I ā¤ļø š¦ {ggtext}
basic_plot +
labs(title = "In smaller doses, Orange Juice was associated with greater mean tooth growth, compared to equivalent doses of Vitamin C") +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
axis.title.y = element_blank(),
plot.title = ggtext::element_textbox_simple(
colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = ggtext::element_textbox_simple(
size = rel(1.1),
lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = vit_c_palette["light_text"]))
Hello, HTML + CSS!
We can make text <span style='color:green'>green</span> and also <span style='color:green; font-size:60pt'>really big</span>! š¤Æ
We can make text green and also really big! š¤Æ
I ā¤ļø š¦ {ggtext}
basic_plot +
labs(title =
paste0("In smaller doses, **<span style='color:",
vit_c_palette["Orange Juice"], "'>Orange Juice</span>**
was associated with greater mean tooth growth,
compared to equivalent doses of **<span style='color:",
vit_c_palette["Vitamin C"], "'>Vitamin C</span>**")
) +
theme(legend.position = "none",
text = element_text(colour = vit_c_palette["light_text"],
family = "Cabin"),
axis.title.y = element_blank(),
plot.title = ggtext::element_textbox_simple(colour = vit_c_palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = ggtext::element_textbox_simple(family = "Cabin", size = rel(1.1), lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = vit_c_palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = vit_c_palette["light_text"]))
See for yourselves!
systemfonts::systemfonts() |> View()
theme()
monochromeR::generate_palette()
See you in 10 minutes! š šØ ā
10:00
At this point, weāre on our way to implementing our Dataviz Design System in R. Letās package up a few things for use in future plots!
{usethis}
green blue dark_text light_text
"#28A569" "#2C3D4F" "#1A242F" "#808A95"
Find out more: Shannon Pileggi - Your first R package in 1 hour
theme_guineapigs <- function(base_text_size = 15,
palette = vit_c_palette) {
theme_minimal(base_size = base_text_size) +
theme(legend.position = "none",
text = element_text(colour = palette["light_text"],
family = "Cabin"),
axis.title.y = element_blank(),
plot.title = ggtext::element_textbox_simple(colour = palette["dark_text"],
size = rel(1.5),
face = "bold",
family = "Enriqueta",
lineheight = 1.3,
margin = margin(0.5, 0, 1, 0, "lines")),
plot.subtitle = ggtext::element_textbox_simple(family = "Cabin", size = rel(1.1), lineheight = 1.3,
margin = margin(0, 0, 1, 0, "lines")),
strip.text = element_text(family = "Enriqueta",
colour = palette["light_text"],
size = rel(1.1), face = "bold",
margin = margin(2, 0, 0.5, 0, "lines")),
axis.text = element_text(colour = palette["light_text"]))
}
{gghighlight}
& curved arrowsWeāve made it easy to see whatās what. Now, letās make it even easier to compare values.
Weāve made it easy to see whatās what. Now, letās make it even easier to compare values.
Weāve made it easy to see whatās what. Now, letās make it even easier to compare values.
Time to add some text boxes!
Time to add some text boxes!
Find out more: Alignment Cheatsheet
Time to add some text boxes!
Time to add some text boxes!
Now for the fun stuffā¦
themed_plot +
scale_y_continuous(expand = c(0, 0.5)) +
theme(strip.text = element_text(hjust = 0.03)) +
ggtext::geom_textbox(aes(
label = mean_length,
hjust = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
halign = case_when(mean_length < 15 ~ 0,
TRUE ~ 1)),
size = 6,
fill = NA,
box.colour = NA,
family = "Cabin",
fontface = "bold")
Now for the fun stuffā¦
themed_plot +
scale_y_continuous(expand = c(0, 0.5)) +
theme(strip.text = element_text(hjust = 0.03)) +
ggtext::geom_textbox(aes(
label = mean_length,
hjust = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
halign = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
colour = case_when(mean_length > 15 ~ "#FFFFFF",
TRUE ~ vit_c_palette[supplement])),
size = 6,
fill = NA,
box.colour = NA,
family = "Cabin",
fontface = "bold")
??????
themed_plot +
scale_y_continuous(expand = c(0, 0.5)) +
theme(strip.text = element_text(hjust = 0.03)) +
ggtext::geom_textbox(aes(
label = mean_length,
hjust = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
halign = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
colour = case_when(mean_length > 15 ~ "#FFFFFF",
TRUE ~ vit_c_palette[supplement])),
size = 6,
fill = NA,
box.colour = NA,
family = "Cabin",
fontface = "bold")
scale_colour_identity()
required!
themed_plot +
scale_y_continuous(expand = c(0, 0.5)) +
theme(strip.text = element_text(hjust = 0.03)) +
scale_colour_identity() +
ggtext::geom_textbox(aes(
label = mean_length,
hjust = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
halign = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
colour = case_when(mean_length > 15 ~ "#FFFFFF",
TRUE ~ vit_c_palette[supplement])),
size = 6,
fill = NA,
box.colour = NA,
family = "Cabin",
fontface = "bold")
We might as well add a bit of extra info (with text hierarchy!) to our labelsā¦
themed_plot +
scale_y_continuous(expand = c(0, 0.5)) +
theme(strip.text = element_text(hjust = 0.03)) +
scale_colour_identity() +
ggtext::geom_textbox(aes(
label = paste0("<span style=font-size:12pt>",
dose, "mg/day</span><br>",
mean_length, "mm"),
hjust = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
halign = case_when(mean_length < 15 ~ 0,
TRUE ~ 1),
colour = case_when(mean_length > 15 ~ "#FFFFFF",
TRUE ~ vit_c_palette[supplement])),
size = 6,
fill = NA,
box.colour = NA,
family = "Cabin",
fontface = "bold")
Easier than you think and makes a big difference! š¦ø
ggtext::geom_textbox()
See you in 5 minutes! š šØ ā
05:00
{gghighlight}
, Tee pipe and curved arrowsāThatās all well and good, but we all know summary data can be misleadingā¦ā
Find out more: cararthompson.com/talks/nhsr2022-ggplot-themes
I ā¤ļø š¦ {geomtextpath}
I ā¤ļø š¦ {geomtextpath}
I ā¤ļø š¦ {geomtextpath}
I ā¤ļø š¦ {geomtextpath}
More textboxes with markdown and conditional alignment (horizontal and vertical!)
themed_scatter_plot +
geomtextpath::geom_textline(stat = "smooth", aes(label = supplement),
hjust = 0.1,
vjust = 0.3,
fontface = "bold",
family = "Cabin") +
ggtext::geom_textbox(data = filter(min_max_gps,
dose %in% c(1, 2)),
aes(x = case_when(dose < 1.5 ~ dose + 0.05,
TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1,
TRUE ~ len * 0.9),
label = paste0("**<span style='font-family:Enriqueta'>",
guinea_pig_name,
"</span>** - ", len, " mm"),
hjust = case_when(dose < 1.5 ~ 0,
TRUE ~ 1),
halign = case_when(dose < 1.5 ~ 0,
TRUE ~ 1)),
family = "Cabin",
size = 4,
fill = NA,
box.colour = NA) +
scale_colour_manual(values = vit_c_palette)
Sometimes less is more!
themed_scatter_plot +
geomtextpath::geom_textline(stat = "smooth", aes(label = supplement),
hjust = 0.1,
vjust = 0.3,
fontface = "bold",
family = "Cabin") +
ggtext::geom_textbox(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05,
TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1,
TRUE ~ len * 0.9),
label = paste0("**<span style='font-family:Enriqueta'>",
guinea_pig_name,
"</span>** - ", len, " mm"),
hjust = case_when(dose < 1.5 ~ 0,
TRUE ~ 1),
halign = case_when(dose < 1.5 ~ 0,
TRUE ~ 1)),
family = "Cabin",
size = 4,
fill = NA,
box.colour = NA) +
scale_colour_manual(values = vit_c_palette)
Same principle, letās add in some arrows!
themed_scatter_plot +
geomtextpath::geom_textline(stat = "smooth", aes(label = supplement),
hjust = 0.1,
vjust = 0.3,
fontface = "bold",
family = "Cabin") +
ggtext::geom_textbox(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05, TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1, TRUE ~ len * 0.9),
label = paste0("**<span style='font-family:Enriqueta'>", guinea_pig_name,"</span>** - ", len, " mm"),
hjust = case_when(dose < 1.5 ~ 0,TRUE ~ 1),
halign = case_when(dose < 1.5 ~ 0, TRUE ~ 1)),
family = "Cabin", size = 4, fill = NA, box.colour = NA) +
geom_curve(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05,
TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1,
TRUE ~ len * 0.9),
xend = case_when(dose < 1.5 ~ dose + 0.02,
TRUE ~ dose - 0.02),
yend = case_when(min_or_max == "max"~ len + 0.5,
TRUE ~ len - 0.5)),
arrow = arrow(length = unit(0.1, "cm")),
alpha = 0.5) +
scale_colour_manual(values = vit_c_palette)
Same principle, letās add in some arrows!
themed_scatter_plot +
geomtextpath::geom_textline(stat = "smooth", aes(label = supplement),
hjust = 0.1,
vjust = 0.3,
fontface = "bold",
family = "Cabin") +
ggtext::geom_textbox(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05, TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1, TRUE ~ len * 0.9),
label = paste0("**<span style='font-family:Enriqueta'>", guinea_pig_name,"</span>** - ", len, " mm"),
hjust = case_when(dose < 1.5 ~ 0,TRUE ~ 1),
halign = case_when(dose < 1.5 ~ 0, TRUE ~ 1)),
family = "Cabin", size = 4, fill = NA, box.colour = NA) +
geom_curve(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05,
TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1,
TRUE ~ len * 0.9),
xend = case_when(dose < 1.5 ~ dose + 0.02,
TRUE ~ dose - 0.02),
yend = case_when(min_or_max == "max"~ len + 0.5,
TRUE ~ len - 0.5)),
curvature = 0.1,
arrow = arrow(length = unit(0.1, "cm")),
alpha = 0.5) +
scale_colour_manual(values = vit_c_palette)
Same principle, letās add in some arrows!
themed_scatter_plot +
geomtextpath::geom_textline(stat = "smooth", aes(label = supplement),
hjust = 0.1,
vjust = 0.3,
fontface = "bold",
family = "Cabin") +
ggtext::geom_textbox(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05, TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1, TRUE ~ len * 0.9),
label = paste0("**<span style='font-family:Enriqueta'>", guinea_pig_name,"</span>** - ", len, " mm"),
hjust = case_when(dose < 1.5 ~ 0,TRUE ~ 1),
halign = case_when(dose < 1.5 ~ 0, TRUE ~ 1)),
family = "Cabin", size = 4, fill = NA, box.colour = NA) +
geom_curve(data = filter(min_max_gps,
dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05,
TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1,
TRUE ~ len * 0.9),
xend = case_when(dose < 1.5 ~ dose + 0.02,
TRUE ~ dose - 0.02),
yend = case_when(min_or_max == "max"~ len + 0.5,
TRUE ~ len - 0.5)),
curvature = 0,
arrow = arrow(length = unit(0.1, "cm")),
alpha = 0.5) +
scale_colour_manual(values = vit_c_palette)
Nearly there, folks! Look how far weāve come!
{gghighlight}
, Tee pipe and curved arrowsBe brave - try theme_void()
Tweak the grid lines, using a matching colour - {monochromeR}
And add a bit of white space
{gghighlight}
, Tee pipe and curved arrows{gghighlight}
, Tee pipe and curved arrowsāBut I have way more conditions than this, how can I highlight a more subtle pattern?ā
Suppose we have data that has so many series that it is hard to identify them by their colours as the differences are so subtleā¦
Find out more: yutannihilation.github.io/gghighlight/
Nicola Rennieās Hollywood Age Gaps plot
Find out more: twitter.com/nrennie35
Creating label content on the fly
ToothGrowth %>%
mutate(guinea_pig_name = sample(unique(bakeoff::bakers$baker), 60),
supplement = case_when(supp == "OJ" ~ "Orange Juice",
supp == "VC" ~ "Vitamin C",
TRUE ~ as.character(supp))) %T>%
{
{
# Double assign to jump out of the pipe!
min_max_gps <<- group_by(., supplement, dose) %>%
filter(., len == min(len) | len == max(len)) %>%
mutate(min_or_max = case_when(len == max(len) ~ "max",
TRUE ~ "min"))
}
} %>%
ggplot(aes(x = dose, y = len, fill = supplement,
colour = supplement)) +
...
Conditional curvatures
?
themed_scatter_plot +
geomtextpath::geom_textline(stat = "smooth", aes(label = supplement), hjust = 0.1, vjust = 0.3, fontface = "bold", family = "Cabin") +
ggtext::geom_textbox(data = filter(min_max_gps, dose == 2),
aes(x = case_when(dose < 1.5 ~ dose + 0.05, TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1, TRUE ~ len * 0.9),
label = paste0("**<span style='font-family:Enriqueta'>", guinea_pig_name,"</span>** - ", len, " mm"),
hjust = case_when(dose < 1.5 ~ 0,TRUE ~ 1),
halign = case_when(dose < 1.5 ~ 0, TRUE ~ 1)),
family = "Cabin", size = 4, fill = NA, box.colour = NA) +
geom_curve(data = filter(min_max_gps,
dose == 2 &
min_or_max == "max"),
aes(x = case_when(dose < 1.5 ~ dose + 0.05, TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1, TRUE ~ len * 0.9),
xend = case_when(dose < 1.5 ~ dose + 0.02, TRUE ~ dose - 0.02),
yend = case_when(min_or_max == "max"~ len + 0.5,TRUE ~ len - 0.5)),
curvature = -0.1,
arrow = arrow(length = unit(0.1, "cm")),
alpha = 0.5) +
geom_curve(data = filter(min_max_gps,
dose == 2 &
min_or_max == "min"),
aes(x = case_when(dose < 1.5 ~ dose + 0.05, TRUE ~ dose - 0.05),
y = case_when(min_or_max == "max"~ len * 1.1, TRUE ~ len * 0.9),
xend = case_when(dose < 1.5 ~ dose + 0.02, TRUE ~ dose - 0.02),
yend = case_when(min_or_max == "max"~ len + 0.5, TRUE ~ len - 0.5)),
curvature = 0.1,
arrow = arrow(length = unit(0.1, "cm")),
alpha = 0.5) +
scale_colour_manual(values = vit_c_palette)
To avoid it getting too unwieldy, add the curvatures to your data and iterate.
labelled_plot # plot with everything but the arrows
for(curv in unique(my_data$curvature)) {
filtered_data <- filter(my_data,
curvature == curv)
labelled_plot <- labelled_plot +
annotate(geom = "curve",
x = filtered_data$label_x,
y = filtered_data$label_y,
xend = filtered_data$arrow_end_x,
yend = filtered_data$arrow_end_y,
size = 0.3,
colour = case_when(filtered_data$species == "Adelie" ~ penguin_palette$Adelie,
filtered_data$species == "Chinstrap" ~ penguin_palette$Chinstrap,
filtered_data$species == "Gentoo" ~ penguin_palette$Gentoo),
curvature = curv,
arrow = arrow(length = unit(1.5, "mm")))
}
labelled_plot # plot with as many different curvatures as you like!
To avoid it getting too unwieldy, add the curvatures to your data and iterate.
labelled_plot # plot with everything but the arrows
for(curv in unique(my_data$curvature)) {
filtered_data <- filter(my_data,
curvature == curv)
labelled_plot <- labelled_plot +
annotate(geom = "curve",
x = filtered_data$label_x,
y = filtered_data$label_y,
xend = filtered_data$arrow_end_x,
yend = filtered_data$arrow_end_y,
size = 0.3,
colour = case_when(filtered_data$species == "Adelie" ~ penguin_palette$Adelie,
filtered_data$species == "Chinstrap" ~ penguin_palette$Chinstrap,
filtered_data$species == "Gentoo" ~ penguin_palette$Gentoo),
curvature = curv,
arrow = arrow(length = unit(1.5, "mm")))
}
labelled_plot # plot with as many different curvatures as you like!
Find out more: cararthompson.com/talks/user2022
hello@cararthompson.com
Tw/Li: @cararthompson