Skip to content

Instantly share code, notes, and snippets.

@AlbertRapp
Last active November 4, 2022 03:14
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AlbertRapp/438102c458fc8fbdffcb6feb76ff93f7 to your computer and use it in GitHub Desktop.
Save AlbertRapp/438102c458fc8fbdffcb6feb76ff93f7 to your computer and use it in GitHub Desktop.
ggplot flowchart
---
output: html_document
editor_options:
chunk_output_type: console
---
This is based on Nicola Rennie's blog post https://nrennie.rbind.io/blog/2022-06-06-creating-flowcharts-with-ggplot2/
The flowchart is taken from https://imgur.com/gallery/79VHL
```{r}
library(tidyverse)
library(igraph)
library(showtext)
setwd(here::here('flowchart_ggplot/'))
camcorder::gg_record(
dir = 'img',
height = 16,
width = 16,
units = 'cm'
)
font_add_google('Fira Sans', 'firasans')
font_add_google('Oleo Script', 'oleo_heading')
showtext_auto()
showtext_opts(dpi = 300)
```
Initial data set containing columns `from` and `to`.
```{r}
dat <- tribble(
~from, ~to,
'Are you a horse?', 'No',
'Are you a horse?', 'Yes',
'Are you a horse?', 'Maybe',
'Maybe', 'How many legs\ndo you walk on?',
'Yes', 'How many legs\ndo you walk on?',
'No', 'You\'re not a horse',
'How many legs\ndo you walk on?', 'Two',
'How many legs\ndo you walk on?', 'Four',
'Two', 'You\'re not a horse_2',
'Four', 'Really?',
'Really?', 'No_2',
'Really?', 'Yes_2',
'No_2', 'Can you read\nand write?',
'Yes_2', 'Can you read\nand write?',
'Can you read\nand write?', 'Yes_3',
'Can you read\nand write?', 'No_3',
'Yes_3', 'You\'re not a horse_3',
'No_3', 'You\'re reading this,\naren\'t you?',
'You\'re reading this,\naren\'t you?', 'Yes_4',
'Yes_4', 'You\'re not a horse_4'
)
```
Create graph and layout
```{r}
graph <- graph_from_data_frame(dat, directed = TRUE)
coords <- graph %>%
layout_as_tree() %>%
as_tibble(.name_repair = ~c('x', 'y'))
output <- coords %>%
mutate(
step = vertex_attr(graph, 'name'),
label = str_remove(step, '\\_.+'),
x = -2.5 * x,
y = 5 * y,
type = case_when(
str_detect(label, '\\?') ~ "Question",
str_detect(step, 'You\'re not a horse') ~ 'Outcome',
T ~ 'Answer'
)
)
```
Make boxes
```{r}
box_width <- 1.2
box_height <- 1.25
boxes <- output %>%
mutate(
xmin = x - box_width,
xmax = x + box_width,
# Adjusted Nicola's code here to make a couple of boxes larger
ymin = case_when(
str_detect(step, '(legs|reading|write)') ~ y - 1.5 * box_height,
T ~ y - box_height
),
ymax = case_when(
str_detect(step, '(legs|reading|write)') ~ y + 1.5 * box_height,
T ~ y + box_height
)
)
```
Make edges
```{r}
# This is the hard part in this plot.
# I only copy-and-pasted this from Nicola's post
edges <- dat %>%
mutate(id = row_number()) %>%
pivot_longer(cols = c("from", "to"),
names_to = "s_e",
values_to = "step") %>%
left_join(boxes, by = "step") %>%
select(-c(label, type, y, xmin, xmax)) %>%
mutate(y = ifelse(s_e == "from", ymin, ymax)) %>%
select(-c(ymin, ymax)) %>%
# Adjusted Nicola's code here to redirect two arrows
mutate(
x = case_when(
s_e == 'to' & id %in% c(5, 14) ~ x - box_width,
T ~ x
)
)
```
Plotting
```{r}
base_colors <- thematic::okabe_ito(2)
ggplot() +
geom_path(
data = edges,
aes(x, y, group = id),
arrow = arrow(length = unit(0.25, 'cm'))
) +
geom_rect(
data = boxes,
aes(
xmin = xmin,
xmax = xmax,
ymin = ymin,
ymax = ymax,
fill = type
)
) +
geom_text(
data = boxes,
aes(
x = x,
y = y,
label = label
),
lineheight = 1,
family = 'firasans'
) +
theme_void() +
theme(
legend.position = 'none',
plot.background = element_rect(fill = 'white', colour = NA)
) +
scale_fill_manual(values = c(
'Question' = base_colors[1],
'Answer' = colorspace::lighten(base_colors[1], 0.5),
'Outcome' = colorspace::lighten(base_colors[2], 0.1)
)) +
annotate(
'text',
x = -1.75,
y = min(boxes$ymin),
family = 'firasans',
hjust = 0,
vjust = 0,
lineheight = 1.25,
label = 'imgur 79VHL\n@rappa753\n@nrennie35'
) +
annotate(
'text',
x = -2,
y = min(boxes$ymin),
family = 'firasans',
fontface = 'bold',
hjust = 1,
vjust = 0,
lineheight = 1.25,
label = 'Inspiration:\nGraphic:\nCode template:'
) +
annotate(
'text',
x = -0.3,
y = min(boxes$ymin) + 11,
size = 10,
family = 'oleo_heading',
hjust = 1,
vjust = 0,
lineheight = 1.5,
label = 'Are you a horse?'
) +
annotate(
'text',
x = -0.3,
y = min(boxes$ymin) + 8.5,
size = 5.5,
family = 'oleo_heading',
hjust = 1,
vjust = 0,
lineheight = 1.5,
label = 'Find out via this flow chart.'
) +
coord_cartesian(
xlim = c(-4.5, 5)
)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment