Scripts

Scripts for my charts!

This is some of the codes I wrote to create my charts. Each chart has its own code right below it.

You can navigate through the site using the sidebar on desktop.

If some code is useful to you, please consider donating.

Random charts

Boa constrictor 🐍

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(ggplot2)
library(geomtextpath)

# data --------------------------------------------------------------------

data <- data.frame(a = c(2.8,2.8,3.8,2.9,2.8,3,4.5,5.6,5.7,4.4,5.8))

quote <- "'My drawing was not a picture of a hat. It was a picture of a <i>Boa constrictor</i> digesting an elephant.'"

scatter = data.frame(
  x = runif(10000,-5,18),
  y = runif(10000,-1,1)
)

# plot --------------------------------------------------------------------

data %>% 
  ggplot(aes(x = a))+
  geom_density(adjust = 1,
               fill = "#784707",
               color = "#462e0a",
               size = 3)+
  geom_textdensity(size = 5, fontface = 2, spacing = 30,
                   vjust = -0.35, hjust = 0.2,
                   label = quote,
                   color = "#462e0a",
                   rich = TRUE)+
  geom_segment(x = -2.5, xend = 12, y = 0, yend = 0,
               size = 3, color = "#462e0a",lineend = "round")+
  geom_point(x = 11.95, y = 0.002, size = 0.5)+
  annotate("text", x = 12, y = 0, label = "- The Little Prince by\nAntoine de Saint-Exupéry\nBruno Mioto - @BrunoHMioto",
           hjust = 1, vjust = 1.2, fontface = "bold",
           color = "#462e0a")+
  scale_y_continuous(expand = c(0,0))+
  scale_x_continuous(limits = c(-2.5,12))+
  theme_void()+
  theme(
    panel.background = element_rect(fill = "#f7f8f8", color = NA)
  )+
  coord_cartesian(ylim = c(-.4,0.8), xlim = c(-4,13))


ggsave("elephant.png", width = 10, height = 6)

Alcohol consumption 🍺

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(readr)
library(ggplot2)
library(elementalist)
library(janitor)
library(ggrepel)
library(rcartocolor)
library(ggforce)

# data --------------------------------------------------------------------

data <- read_csv("data/alcohol-consumption-by-15-19-year-old-males-vs-females.csv") %>% 
  clean_names()

countries <- read_csv("data/continents2.csv") %>%
  clean_names() %>% 
  select(alpha_3, region)

data2 <- data %>% 
  rename(male = 4,
         female = 5) %>% 
  filter(year == 2010,
         !is.na(male),
         !is.na(female)) %>% 
  left_join(countries, by = c("code" = "alpha_3"))

data_points <- tibble(
  x = c(15,38,38),
  y = c(5,5,28)
)

# plot --------------------------------------------------------------------

data2 %>% 
  ggplot(aes(x = female, y = male, 
             color = region))+
  geom_abline(color = "grey")+
  geom_text_repel(aes(label = ifelse(entity == "Brazil",
                                     entity,
                                     "")),
                  fontface = "bold",
                  max.overlaps = 40,
                  min.segment.length = 0,box.padding = 0.6,
                  show.legend = FALSE)+
  geom_text_repel(aes(label = ifelse(male > 25| female>10,
                                     entity,
                                     "")),
                  fontface = "bold",box.padding = 0.3,
                  max.overlaps = 20,
                  show.legend = FALSE)+
  geom_point(alpha = 0.7, 
             aes(
    size = population_historical_estimates))+
  annotate("text",
           x = 30, y = 30, label = paste0("\u2191 Men drink more\n\u2193 Women drink more"),
           angle = 33,fontface = "bold", color = "grey")+
  ggforce::geom_mark_hull(data = data_points, aes(x = x, y = y), color = "grey20", fill = "grey20",
                          linetype = "dashed",alpha = 0.1)+
  annotate("text",
          x = 31.5, y = 11.5, label = "A better\nworld?",
          lineheight = 0.8,
          fontface = "bold",
          family = "Open Sans",
          color = "grey40")+
  guides(size = "none")+
  #rcartocolor::scale_color_carto_d(palette = "Bold")+
  scale_color_manual(
    values = c("#7f3c8d",
               "#11a579",
               "#3969ac",
               "#e68310",
               "#e73f74")
  )+
  scale_x_continuous(labels = scales::label_number(scale = 1,suffix = "L"),
                     expand = expansion(mult = c(0,0.05)))+
  scale_y_continuous(labels = scales::label_number(scale = 1,suffix = "L"),
                     expand = expansion(mult = c(0,0.05)))+
  labs(
    title = "Average alcohol consumption with 15-19 year old, 2010",
    subtitle = "Measured in litres of pure alcohol per year",
    x = "Avg alcohol consumption per capita (Females)",
    y = "Avg alcohol consumption per capita (Males)",
    color = "Continent",
    caption = "Bruno Mioto @BrunoHMioto - Data: WHO, Global Health Observatory (GHO)"
  )+
  theme_classic()+
  theme(
    plot.title.position = "plot",
    plot.title = element_text(face = "bold"),
    legend.margin = margin(0,0,0,0,"pt"),
    legend.text = element_text(margin = margin(0,5,0,0, unit = 'pt')),
    legend.title = element_text(face = "bold"),
    axis.title = element_text(face = "bold"),
    axis.text = element_text(face = "bold", angle = c(1,3,-3,-5,6)),
    legend.position = "top",
    panel.grid.major = element_line_wiggle(2.5),
    panel.grid.minor = element_line_wiggle(2.5, color = "#f1f1f1"),
    axis.line = element_line_wiggle(3),
    text = element_text(family = "Open Sans")
  )+
  coord_cartesian(xlim = c(0,40),
                  ylim = c(0,40))


ggsave("test12_5.png", width = 6, height = 5)

Pfizer vaccine 💉

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(ggplot2)
library(ggrepel)
library(extrafont)
library(readr)

# data --------------------------------------------------------------------

data <- read_csv("https://raw.githubusercontent.com/brunomioto/dataviz/main/pfizer_vaccine/pfizer_vaccine_trial.csv")

# plot --------------------------------------------------------------------

data %>% 
  ggplot(aes(x = days_after_dose_1))+
  #dif
  geom_segment(aes(x = 110, xend = 110, y = 0.29413854, yend = 2.24653641),
               color = "#bdbdbd",
               size = 2)+
  geom_label(aes(x = 110, y = 1.4, label = "7.6x"),
             color = "#bdbdbd",
             fill = "#fcfcfc",
             size = 8,
             label.padding = unit(10, "pt"),
             label.size = NA,
             hjust = 0.43)+
  #placebo
  geom_step(aes(y = placebo),
            color = "#f76833",
            size = 1)+
  geom_point(data = data %>%  
               distinct(placebo, .keep_all = TRUE),
             aes(y = placebo),
             color = "#f76833",
             shape = 21,
             fill = NA)+
  geom_text_repel(aes(label = ifelse(days_after_dose_1 == 73, "Placebo group",""),
                      y = placebo),
                  nudge_x = -5,
                  box.padding = 0.5,
                  nudge_y = 0.3,
                  segment.curvature = 0.1,
                  segment.ncp = 3,
                  segment.angle = 20,
                  fontface = "bold",
                  color = "#f76833",
                  size = 4)+
  #vaccine
  geom_step(aes(y = vaccine),
            color = "#196299",
            size = 1)+
  geom_point(data = data %>%  
               distinct(vaccine, .keep_all = TRUE),
             aes(y = vaccine),
             color = "#196299",
             shape = 21,
             fill = NA)+
  geom_text_repel(aes(label = ifelse(days_after_dose_1 == 96, paste("Vaccinated group\nBNT162b2 (30\u03bcg)"),""),
                      y = vaccine),
                  nudge_x = -5,
                  box.padding = 0.5,
                  nudge_y = 0.3,
                  segment.curvature = 0.1,
                  segment.ncp = 3,
                  segment.angle = 20,
                  fontface = "bold",
                  color = "#196299",
                  size = 4)+
  scale_y_continuous(breaks = seq(0,2.4,0.4), expand = expansion(mult = c(0,0.05)))+
  scale_x_continuous(breaks = seq(0,119,7), expand = expansion(mult = c(0.01,0.02)))+
  coord_cartesian(ylim = c(0,2.4))+
  labs(
    title = "COVID-19 incidence rates in Pfizer/BioNTech's vaccine trial",
    x = "Days after first dose",
    y = "Cumulative incidence (%)",
    caption = "Chart: Bruno Mioto @BrunoHMioto - Source: New England Journal of Medicine"
  )+
  theme_minimal()+
  theme(
    plot.background = element_rect(fill = "#fcfcfc", color = NA),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    axis.line.x = element_line(),
    axis.ticks.x = element_line(),
    axis.title = element_text(face = "bold"),
    axis.text = element_text(face = "bold", size = 10),
    text = element_text(family = "Open Sans"),
    plot.title.position = "plot",
    plot.title = element_text(face = "bold", size = 16),
    plot.margin = margin(10,10,10,10, unit = "pt")
  )

ggsave("pfizer_trial.png", width = 10, height = 7)

Spider 🕷️

Code - Click to view
# packages ----------------------------------------------------------------

library(readr)
library(dplyr)
library(ggplot2)
library(ggtext)
library(ggthemes)
library(nflplotR) #we can use ggpath now

# data --------------------------------------------------------------------

data <- read_csv("species_export_20220408.csv")

# plot --------------------------------------------------------------------

#point
plot <- data %>% 
  group_by(year) %>% 
  count() %>% 
  ungroup() %>% 
  mutate(cum_sum = cumsum(n)) %>% 
  ggplot(aes(x = year, y = cum_sum))+
  geom_line(color = "grey50")+
  geom_point(
    aes(color = ifelse(year == 2022, "#735133", "grey50"))
  )+
  annotate(
    geom = "curve", x = 1950, y = 45000, xend = 2022, yend = 50000, 
    curvature = -.2,
    color = "#735133"
  ) +
  geom_richtext(x = 1950, 
                y = 40000, 
                size = 4.5,
                color = "#735133",
                label.color = NA,
                fill = "#f0f0f0",
                label = "<i>Guriurius minuano</i><br>Marta, Bustamante, Ruiz &<br>Rodrigues, 2022"
  )+
  annotate(
    nflplotR::GeomFromPath,
    x = 1787, y = 40000,
    path = "./spider_picture.png",
    width = 0.2
  ) +
  scale_color_identity()+
  scale_y_continuous(expand = expansion(mult = c(0.02,0.02)),
                     labels = scales::unit_format(big.mark = ",", unit = ""))+
  scale_x_continuous(breaks = c(seq(1750, 2022, 25),2022))+
  labs(
    title = "The world of spiders: The 50,000th species described!",
    x = "Year",
    y = "Number of species",
    caption = "Data: World Spider Catalog\nFigure:  Marta, Bustamante, Ruiz & Rodrigues (2022)"
  )+
  theme_fivethirtyeight() +
  theme(
    panel.grid.minor.y = element_blank(),
    axis.title = element_text(face = "bold"),
    plot.title.position = "plot",
    axis.title.x = element_text(margin = margin(5,0,-5,0)),
    plot.caption = element_text(margin = margin(0,0,0,0))
  )+
  coord_cartesian(ylim = c(0,NA))

Star Wars 🌌

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(ggplot2)
library(readr)
library(ggfx)

# data --------------------------------------------------------------------

data <- dplyr::starwars

list_char <- c("Luke Skywalker",
               "Darth Vader",
               "Obi-Wan Kenobi",
               "Yoda",
               "Mace Windu",
               "Leia Organa")

data2 <- data %>%
  mutate(height = height/100)

# plot --------------------------------------------------------------------

plot <- data2 %>% 
  ggplot(aes(x = height, y = height))+
    #Yoda
    with_outer_glow(
      geom_segment(data=filter(data2,name == "Yoda"),
                   aes(x = 0, xend = height, yend = name, y = name),
                   color = "#ffffff",
                   size = 3,
                   lineend = "round"),
      colour = "#02fe2c",
      sigma = 20,
      expand = 10
    )+ 
    #Leia
    with_outer_glow(
      geom_segment(data=filter(data2,name == "Leia Organa"),
                   aes(x = 0, xend = height, yend = name, y = name),
                   color = "#ffffff",
                   size = 3,
                   lineend = "round"),
      colour = "#006be4",
      sigma = 20,
      expand = 10
    )+ 
    #luke
    with_outer_glow(
      geom_segment(data=filter(data2,name == "Luke Skywalker"),
                   aes(x = 0, xend = height, yend = name, y = name),
                   color = "#ffffff",
                   size = 3,
                   lineend = "round"),
      colour = "#02fe2c",
      sigma = 20,
      expand = 10
    )+
    #Obi-Wan Kenobi
    with_outer_glow(
      geom_segment(data=filter(data2,name == "Obi-Wan Kenobi"),
                   aes(x = 0, xend = height, yend = name, y = name),
                   color = "#ffffff",
                   size = 3,
                   lineend = "round"),
      colour = "#006be4",
      sigma = 20,
      expand = 10
    )+ 
    #Mace Windu
    with_outer_glow(
      geom_segment(data=filter(data2,name == "Mace Windu"),
                   aes(x = 0, xend = height, yend = name, y = name),
                   color = "#ffffff",
                   size = 3,
                   lineend = "round"),
      colour = "#d413ef",
      sigma = 20
    )+
    #darth vader
    with_outer_glow(
      geom_segment(data=filter(data2,name == "Darth Vader"),
                   aes(x = 0, xend = height, yend = name, y = name),
                   color = "#ffffff",
                   size = 3,
                   lineend = "round"),
      colour = "#e00301",
      sigma = 30,
      expand = 10
    )+
    labs(
      title = "Height of some Star Wars characters",
      x = "Height, m",
      caption = "Bruno Mioto - @BrunoHMioto"
    )+
    scale_x_continuous(expand = expansion(mult = c(0,0.05)))+
    scale_y_discrete(limits = c("Yoda",
                                "Leia Organa",
                                "Luke Skywalker",
                                "Obi-Wan Kenobi",
                                "Mace Windu",
                                "Darth Vader"))+
    theme_minimal()+
    theme(
      plot.background = element_rect(fill = "black"),
      panel.background = element_rect(fill = "black"),
      panel.grid.major.y = element_line(linetype = "dashed", color = "grey"),
      panel.grid.minor.y = element_blank(),
      panel.grid.major.x = element_blank(),
      plot.title.position = "plot",
      plot.title = element_text(size = rel(1.5)),
      text = element_text(color = "white", face = "bold"),
      axis.text = element_text(color = "white"),
      axis.title.x = element_blank(),
      axis.text.x = element_text(angle = 90,vjust = 0.5, hjust = 1)
    )+
  coord_flip()


ggsave(plot = plot, "starwars_height_flip.png", height = 10, width = 5)

Copacabana 🏖️

Code - Click to view
# packages ----------------------------------------------------------------

library(tidyverse)
library(ggforce)
library(patchwork)

# data --------------------------------------------------------------------

col_1 <- c("a", "b", "c", "d")
col_2 <- c("e", "f", "g", "h")
col_3 <- c("i", "j", "k", "l")
value <- c(0, 5, 5, 5)

df <- data.frame(col_1, col_2, col_3, value)

df <- gather_set_data(df, 1:3)

df$y <- factor(df$y, levels = c("a", "b", "c", "d",
                                "f", "g", "h", "e",
                                "i", "j", "k", "l"
                                ))

# plot -------------------------------------------------

a <- df %>%
  ggplot(aes(x, id = id, split = y, value = value)) +
  geom_parallel_sets(
    fill = "black",
    axis.width = 0,
    sep = 0.5,
    strength = 0.7
  ) +
  geom_parallel_sets_axes(axis.width = 0) +
  scale_y_continuous(expand = c(0, 0)) +
  scale_x_discrete(expand = c(0, 0)) +
  theme_void()

# copacabana ------------------------------------------

(a|a|a|a|a)/(a|a|a|a|a)

ggsave("copacabana3.png", width = 6, height = 6)

Eye 👁️

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(ggplot2)
library(ggforce)
library(ggnewscale)

# data --------------------------------------------------------------------

data <- data.frame(x = runif(1000),
                   y = runif(1000))

circle <- data.frame(
  x0 = 0.1,
  y0 = 0.9,
  r = 0.06
)

# plot --------------------------------------------------------------------

eye <- ggplot(data)+
  #fundo
  geom_tile(aes(x = 0.5, y=1, fill = y)) +
  scale_fill_gradient2(low = '#ffffff', mid = "#f3f1f4", high = '#e8e4e9',
                       midpoint = 0.6) +
  ggnewscale::new_scale_fill()+
  #iris fundo
  geom_tile(aes(x = 0.5, y=0.5, fill = y)) +
  scale_fill_gradient2(low = '#2b5a74', mid = '#08303c', high = '#08303c',
                       midpoint = 0.8) +
  #iris linha
  geom_line(aes(x,y,color = y))+
  scale_color_gradient(low = "#412808", high = "#ad8d6d30")+
  #contorno
  geom_hline(yintercept = 1.5, size = 1.2)+
  ggnewscale::new_scale_fill()+
  #pupila
  geom_rect(xmin = 0, xmax = 1, ymin = 0, ymax = 0.4,
            fill = "#000000")+
  #reflexo
  geom_circle(data = circle, aes(x0 = x0, y0 = y0, r = r, fill = r),
              fill = "#ffffff")+
  scale_y_continuous(limits = c(0,1.5))+
  coord_polar()+
  theme_void()+
  theme(
    legend.position = "none",
    plot.background = element_rect(fill = "white", color = NA)
  )

# save --------------------------------------------------------------------

ggsave(plot = eye, "eye.png", width = 6, height = 6)

US Nuclear Tests 💣️

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(ggplot2)
library(ggfx)
library(ggpath)

# data --------------------------------------------------------------------

nuclear_tests <- read.csv("https://raw.githubusercontent.com/owid/notebooks/main/BastianHerre/nuclear_weapons/Arms%20Control%20Association%20(2020)%20nuclear%20weapons%20tests/nuclear_weapons_tests_states.csv")

usa_tests <- nuclear_tests %>% 
  filter(country_name == "United States") %>% 
  group_by(year) %>% 
  count(wt = nuclear_weapons_tests)

# plot --------------------------------------------------------------------

usa_tests %>% 
  ggplot(aes(x = year, y = n))+
  #1st bomb
  annotate(
    "curve",
    x = 1944.5, xend = 1944,
    y = 2, yend = 28,
    color = "white",
    curvature = -0.3
  )+
  annotate(
    "text",
    x = 1948,
    y = 32,
    label = "'Trinity' was the first\nUS bomb to be tested",
    family = "Open Sans",
    lineheight = 0.85,
    color = "white"
  )+
  #moratorium 
  annotate(
    "rect",
    xmin = 1958.5, xmax = 1960.5,
    ymin = -Inf, ymax = 102,
    fill = "#cbcbc2",
    alpha = 0.3
  )+
  annotate(
    "label",
    x = 1951,
    y = 87.5,
    label = "URSS, UK, and the US\nagreed to a moratorium\non nuclear weapon tests in 1958",
    size = 3,
    family = "Open Sans",
    lineheight = 0.87,
    color = "white",
    fill = "#171515",
    label.size = NA,
  )+
  annotate(
    "curve",
    x = 1955, xend = 1958.3,
    y = 92, yend = 95,
    color = "white",
    curvature = -0.2,
    linewidth = 0.3
  )+
  #record
  annotate(
    "label",
    x = 1975,
    y = 93,
    label = "After the moratorium was broken in August 1961,\nthe US tested 96 nuclear bombs in 1962",
    size = 3.5,
    family = "Open Sans",
    lineheight = 0.87,
    color = "white",
    fill = "#171515",
    label.size = NA,
  )+
  #last test
  annotate(
    "label",
    x = 1988,
    y = 31.25,
    label = "Following intense public pressure,\nthe US adheres to a testing\nmoratorium in 1992",
    size = 3,
    family = "Open Sans",
    lineheight = 0.87,
    color = "white",
    fill = "#171515",
    label.size = NA,
  )+
  annotate(
    "curve",
    x = 1992, xend = 1993,
    y = 7, yend = 28,
    color = "white",
    curvature = 0.2,
    linewidth = 0.3
  )+
  #tests label
  annotate(
    "label",
    x = 1942,
    y = 100,
    label = "tests",
    hjust = 0,
    vjust = 0.45,
    label.size = NA,
    fill = "#171515",
    color = "white",
    family = "Open Sans",
    fontface = "bold"
  )+
  geom_from_path(
    path = "https://upload.wikimedia.org/wikipedia/commons/a/ae/Nuclear_symbol.svg",
    x = 1994,
    y = 107,
    width = 0.07,
    stat = "unique"
  )+
  with_outer_glow(
    geom_col(fill = "#fefb8c"),
    colour = "#cb3200",
    sigma = 60
  )+
  scale_y_continuous(
    expand = expansion(mult = c(0,0.05)),
  )+
  labs(
    title = "Number of nuclear weapons tests by the United States",
    caption = "Bruno Mioto @BrunoHMioto - data: Arms Control Association (2020)"
  )+
  theme_grey(base_family = "Open Sans")+
  theme(
    plot.margin = margin(10,10,10,10,"pt"),
    plot.title.position = "plot",
    plot.title = element_text(face = "bold", 
                              size = 16,
                              color = "white"),
    plot.caption = element_text(color = "white"),
    plot.background = element_rect(fill = "#171515", color = NA),
    panel.background = element_blank(),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y = element_line(color = "#cbcbc2"),
    panel.grid.minor.y = element_line(color = "#cbcbc2",
                                      linetype = "dashed"),
    axis.ticks = element_blank(),
    axis.text = element_text(color = "white",
                             face = "bold"),
    axis.text.y = element_text(size = 10),
    axis.title = element_blank()
  )+
  coord_cartesian(xlim = c(1945, 1993),
                  ylim = c(0,100),
                  clip = "off")

# save --------------------------------------------------------------------

ggsave("figures/us_nuclear_tests.png", width = 8, height = 6, dpi = 600)

NFL Charts 🏈

Black Quarterbacks ✊

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(tidyr)
library(ggplot2)
library(ggtext)

# data --------------------------------------------------------------------

data <- tibble(
  name = c(
    "Doug Williams", "Steve McNair", "Donovan McNabb",
    "Colin Kaepernick", "Russell Wilson", "Russell Wilson",
    "Cam Newton", "Patrick Mahomes", "Patrick Mahomes",
    "Patrick Mahomes", "Patrick Mahomes","Jalen Hurts",
    "Patrick Mahomes", "Jalen Hurts"
  ),
  superbowl = c(
    22, 34, 39,
    47, 48, 49,
    50, 54, 55,
    57, 58, 57,
    59, 59
  ),
  team = c(
    "Washington Redskins", "Tennessee Titans", "Philadelphia Eagles",
    "San Francisco 49ers", "Seattle Seahawks", "Seattle Seahawks",
    "Carolina Panthers", "Kansas City Chiefs", "Kansas City Chiefs",
    "Kansas City Chiefs", "Kansas City Chiefs","Philadelphia Eagles",
    "Kansas City Chiefs", "Philadelphia Eagles"
  ),
  conference = c(
    "NFC","AFC","NFC",
    "NFC","NFC","NFC",
    "NFC","AFC","AFC",
    "AFC","AFC","NFC",
    "AFC","NFC"
  ),
  black = c(
    TRUE, TRUE, TRUE,
    TRUE, TRUE, TRUE,
    TRUE, TRUE, TRUE,
    TRUE, TRUE, TRUE,
    TRUE, TRUE
    ),
  win = c(
    TRUE, FALSE, FALSE,
    FALSE, TRUE, FALSE,
    FALSE, TRUE, FALSE,
    TRUE, TRUE, FALSE,
    FALSE, FALSE
    )
)


data2 <- tibble(
  superbowl = rep(1:58, 2),
  name = NA,
  team = NA,
  conference = NA,
  black = FALSE,
  win = NA
) |>
  bind_rows(data) |>
  arrange(superbowl, name) |>
  group_by(superbowl) |>
  slice_head(n = 2) |>
  mutate(
    conference = case_when(
      !is.na(conference) ~ conference,
      any(conference == "NFC", na.rm = TRUE) ~ "AFC",
      any(conference == "AFC", na.rm = TRUE) ~ "NFC",
      TRUE ~ ifelse(row_number() == 1, "AFC", "NFC")
    )
  )



# plot --------------------------------------------------------------------

data2 |>
  ggplot(aes(x = superbowl, y = conference))+
  #1st black qb
  annotate(
    "segment",
    x = 22,
    xend = 22,
    y = 2,
    yend = 2.15,
    color = "black"
  )+
  geom_richtext(
    x = 22,
    y = 2.4,
    label = "<b>Doug Williams</b> was the 1<sup>st</sup><br>Black QB to start a SB",
    lineheight = 0.8,
    fill = NA, label.color = NA,
    inherit.aes = FALSE,
    stat = "unique"
  )+
  #2nd qb to win
  annotate(
    "curve",
    x = 48,
    y = 2,
    xend = 46,
    yend = 2.4,
    curvature = 0.45
  )+
  geom_richtext(
    x = 41,
    y = 2.4,
    label = "<b>R. Wilson</b> was the 2<sup>nd</sup><br>Black QB to win a SB",
    lineheight = 0.8,
    fill = NA, label.color = NA,
    inherit.aes = FALSE,
    stat = "unique"
  )+
  #3rd black qb to win
  annotate(
    "curve",
    x = 54,
    y = 1,
    xend = 52,
    yend = 0.6,
    curvature = -0.45
  )+
  geom_richtext(
    x = 45.5,
    y = 0.6,
    label = "<b>P. Mahomes</b> was only the 3<sup>rd</sup><br>Black QB to win a Super Bowl",
    lineheight = 0.8,
    fill = NA, label.color = NA,
    inherit.aes = FALSE,
    stat = "unique"
  )+
  #1st and 2nd both black qbs
  annotate(
    "rect",
    xmin = 56.5,
    xmax = 57.5,
    ymin = 0.85,
    ymax = 2.15,
    alpha = 0.3
  )+
  annotate(
    "text",
    x = 58,
    y = 2.4,
    label = "Only Super Bowls with\nboth Black starting QBs",
    lineheight = 0.8
  )+
  #2nd both black qbs
  annotate(
    "rect",
    xmin = 58.5,
    xmax = 59.5,
    ymin = 0.85,
    ymax = 2.15,
    alpha = 0.3
  )+
  #SB 59
  annotate(
    "segment",
    x = 59,
    y = c(1,2),
    xend = 59.8,
    yend = c(1,2)
  )+
  annotate(
    "text",
    x = 60,
    y = c(1,2),
    hjust = 0,
    label = c("P. Mahomes","J. Hurts"),
    lineheight = 0.8,
    fontface = "bold"
  )+
  #plot
  geom_point(
    aes(fill = black,
        color = win),
    shape = 21,
    size = 4,
    stroke = 0.7
  )+
  annotate(
    "text",
    x = 0,
    y = c(1,2,1.5),
    label = c("AFC","NFC","Super Bowl"),
    fontface = "bold",
    hjust = 1
  )+
  annotate(
    "text",
    x = c(1,seq(5,55,5),59),
    y = 1.5,
    label = c(1,seq(5,55,5),59),
    fontface = "bold"
  )+
  annotate(
    "text",
    x = c(1,seq(5,55,5),59),
    y = 1.25,
    label = "|",
  )+
  annotate(
    "text",
    x = c(1,seq(5,55,5),59),
    y = 1.75,
    label = "|",
  )+
  scale_fill_manual(
    values = c(
      "TRUE" = "#0A90C5",
      "FALSE" = "#FC670D"
    )
  )+
  scale_color_manual(
    values = c(
      "TRUE" = "black",
      "FALSE" = "transparent"
    ),na.value = "transparent"
  )+
  scale_y_discrete()+
  labs(
    title = "Breaking Barriers: A New Era for Black Quarterbacks in the Super Bowl",
    subtitle = "Representation matters, just let them play",
    caption = "Data: Wikipedia - Made by Bruno Mioto - www.brunomioto.com"
  )+
  theme_void()+
  theme(
    legend.position = "none",
    plot.background = element_rect(fill = "#f1f1f1",color = NA),
    plot.caption = element_text(hjust = 0),
    plot.margin = margin(5,5,10,10,"pt"),
    plot.title = element_text(face = "bold")
  )+
  coord_cartesian(
    xlim = c(-2.5,63)
  )

ggsave("figures/plot_black_qbs.png", width = 12, height = 2.7, dpi = 1200)

Time to throw ⏱️

Code - Click to view
# packages ----------------------------------------------------------------

library(nflreadr)
library(dplyr)
library(ggplot2)
library(nflplotR)
library(ggh4x)
library(ggbeeswarm)

# data --------------------------------------------------------------------

tt <- nflreadr::load_nextgen_stats(stat_type = "passing",seasons = 2022) %>%
  mutate(team_abbr = ifelse(team_abbr == "LAR", "LA", team_abbr))


tt_qbs <- tt %>%
  filter(week == 0) %>%
  group_by(team_abbr) %>%
  arrange(desc(attempts)) %>%
  slice_head() %>%
  select(player_display_name, team_abbr, player_gsis_id) %>%
  ungroup()

tt_top <- tt %>%
  filter(player_gsis_id %in% tt_qbs$player_gsis_id,
         (player_display_name != "Baker Mayfield" | team_abbr != "CAR")
         )

tt_avg <- tt %>%
  summarise(avg = mean(avg_time_to_throw))

tt_all <- nflreadr::load_nextgen_stats(stat_type = "passing", seasons = 2016:2022) %>%
  mutate(team_abbr = ifelse(team_abbr == "LAR", "LA", team_abbr))

tt_all2 <- tt_all %>%
  filter(player_gsis_id %in% tt_top$player_gsis_id,
         week != 0) %>%
  group_by(player_gsis_id) %>%
  arrange(desc(season)) %>%
  mutate(team_abbr = ifelse(season == 2022, team_abbr, NA)) %>%
  tidyr::fill(team_abbr) %>%
  filter(season != 2022) %>%
  ungroup() %>%
  add_row(team_abbr = "PIT")


tt_top$team_abbr2 <- nfl_team_factor(tt_top$team_abbr)
tt_all2$team_abbr2 <- nfl_team_factor(tt_all2$team_abbr)


label_names <- c(
  "MIA" = "Tua Tagovailoa",
  "LV" = "Derek Carr",
  "NYJ" = "Zach Wilson",
  "NYG" = "Daniel Jones",
  "BUF" = "Josh Allen",
  "DAL" = "Dak Prescott",
  "SF" = "Jimmy Garoppolo",
  "PIT" = "Kenny Pickett",
  "TEN" = "Ryan Tannehill",
  "PHI" = "Jalen Hurts",
  "ARI" = "Kyler Murray",
  "CAR" = 'Sam Darnold',
  "HOU" = "Davis Mills",
  "NE" = "Mac Jones",
  "LAC" = "Justin Herbert",
  "BAL" = "Lamar Jackson",
  "IND" = "Matt Ryan",
  "MIN" = "Kirk Cousins",
  "JAX" = "Trevor Lawrence",
  "SEA" = "Geno Smith",
  "WAS" = "Carson Wentz",
  "LA" = "Baker Mayfield",
  "KC" = "Patrick Mahomes",
  "GB" = "Aaron Rodgers",
  "TB" = "Tom Brady",
  "CLE" = "Jacoby Brissett",
  "NO" = "Andy Dalton",
  "ATL" = "Marcus Mariota",
  "CHI" = "Justin Fields",
  "DEN" = "Russell Wilson",
  "DET" = "Jared Goff",
  "CIN" = "Joe Burrow"

)

# plot --------------------------------------------------------------------

g <- tt_top %>%
  ggplot(aes(x = avg_time_to_throw, y = player_display_name))+
  nflplotR::geom_nfl_logos(aes(team_abbr = team_abbr2), width = 0.4,
                           stat = "unique",
                           alpha = 0.2,
                           x = 3.7, y = 1)+
  geom_beeswarm(data = tt_all2 %>% filter(week != 0),
                size = 2.8,
                alpha = 0.2,
                shape = 16,
                cex= 4,
                priority='random',
                color = "grey50")+
  geom_vline(data = tt_avg,
             aes(xintercept = avg),
             color = "#0570b0",
             linetype = "dashed")+
  geom_vline(data = . %>% filter(week == 0),
             aes(xintercept = avg_time_to_throw))+
  geom_beeswarm(data = . %>% filter(week != 0),
                size = 2.5,
                cex=4,
                priority='random',
                shape = 21,
                aes(color = team_abbr2,
                    fill = team_abbr2))+
  geom_text(data = . %>% filter(week == 0),
            x = 1.9, y = 1.4,
            hjust = 0,
            family = "Open Sans",
            aes(label = paste0(scales::number(avg_time_to_throw,accuracy = 0.01)," s")))+
  scale_color_nfl(type = "secondary")+
  scale_fill_nfl(type = "primary")+
  facet_wrap2(team_abbr2~.,
              labeller = as_labeller(label_names),
              scales = "free_y",
              axes = "x",
              remove_labels = "x",
              ncol = 4)+
  labs(
    title = "Average time to throw for each QB - 2022 Regular season",
    subtitle = glue::glue("Current season highlighted - QB with most attempts per team. NFL 2022 season average ({round(tt_avg$avg,2)} sec) in blue."),
    caption = "Bruno Mioto @BrunoHMioto - Data: Next Gen Stats with nflreadr"
  )+
  theme_classic()+
  theme(
    text = element_text(family = "Open Sans"),
    plot.title.position = "plot",
    plot.title = element_text(face = "bold", size = 18),
    plot.subtitle = element_text(face = "bold"),
    axis.text.x = element_text(face = "bold"),
    axis.text.y = element_blank(),
    axis.title = element_blank(),
    axis.ticks.y = element_blank(),
    axis.ticks.x = element_line(color = "black", size = 0.8),
    axis.line.y = element_blank(),
    axis.line.x = element_line(size = 0.8),
    panel.grid.major.x = element_line(),
    plot.background = element_rect(fill = "#f0f0f0"),
    strip.background = element_blank(),
    strip.text = element_text(face = "bold", size = 10)
  )


ggsave(plot = g, "ttt_2022_season_logo.png", width = 9, height = 9)

Players by draft round 🎟️

Code - Click to view
# packages ----------------------------------------------------------------

library(readr)
library(dplyr)
library(ggplot2)
library(nflplotR)
library(nflfastR)
library(ggchicklet)
library(ggh4x)

# data --------------------------------------------------------------------

url <- "https://raw.githubusercontent.com/ajreinhard/NFL-public/main/misc-data/2013_to_2022_init53.csv"

rosters <- read_csv(url)

roster_2022 <- rosters %>% 
  filter(season == 2022) %>% 
  mutate(draft_rnd = ifelse(draft_type == "Undrafted","UFA",draft_rnd))

round_players <- roster_2022 %>% 
  count(team, draft_rnd) %>% 
  left_join(nflfastR::teams_colors_logos,
          by = c("team" = "team_abbr"))


round_players$team <- nfl_team_factor(round_players$team)

round_players$team_division <- factor(round_players$team_division,
                                      levels = c("AFC West","AFC North","AFC South","AFC East",
                                                 "NFC West","NFC North","NFC South","NFC East"))

round_players$draft_rnd <- factor(round_players$draft_rnd,
       levels = c("1","2","3","4","5","6","7","UFA"))

# plot --------------------------------------------------------------------

round_players %>% 
  ggplot(aes(x = team, y = n, fill = draft_rnd))+
  geom_chicklet()+
  geom_text(position = position_stack(vjust = 0.5,reverse = TRUE),
            aes(label = ifelse(n > 2, n, "")),
            color = "white")+
  facet_wrap2(~team_division, ncol = 4, scales = "free_y", axes = "all")+
  scale_y_continuous(breaks = seq(0,50,10),
                     expand = c(0,0))+
  scale_fill_manual(
    values = c('#dc3913',
               '#3366cc',
               '#0f9618',
               '#992299',
               '#0099c6',
               '#dd4477',
               '#e67300',
               '#737373')
  )+
  labs(
    title = "Number of players of each team by draft round in the 2022 season",
    fill = "Draft round",
    caption = "Bruno Mioto @BrunoHMioto - Data: TheFootballDB by @reinhurdler"
  )+
  guides(fill = guide_legend(nrow = 1,
                             label.position = "bottom"))+
  theme_minimal()+
  theme(
    plot.title = element_text(size = rel(1.5), face = "bold"),
    plot.title.position = "plot",
    plot.background = element_rect(fill = "white", color = NA),
    plot.margin = margin(15,15,15,15,unit = "pt"),
    legend.title = element_text(face = "bold"),
    axis.text.y = element_nfl_logo(size = 0.7),
    axis.title = element_blank(),
    legend.position = "top",
    legend.spacing.x = unit(0.1, 'cm'),
    legend.spacing.y = unit(0, 'cm'),
    legend.key.height =  unit(1, "cm"),
    legend.box.margin = margin(-5,0,-10,0,unit = "pt"),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.x = element_line(color = "#999999"),
    strip.text = element_text(size = rel(1.2),
                              face = "bold"),
    text = element_text(family = "Open Sans")
  )+
  coord_flip()

ggsave("rnd_players_2022.png", width = 10, height = 6)

Early-Late downs QBs slope 🚀

Code - Click to view
# packages ----------------------------------------------------------------

library(nflreadr)
library(nflplotR)
library(dplyr)
library(ggplot2)
library(ggrepel)
library(tidyr)
library(ggtext)

# data --------------------------------------------------------------------

pbp <- load_pbp(seasons = 2022)
rosters <- load_rosters(2022)

snap_counts <- nflreadr::load_snap_counts()

snap_counts_topQB <- snap_counts %>%
  filter(position == "QB") %>%
  group_by(team, player, pfr_player_id) %>%
  summarise(total_off_snaps = sum(offense_snaps)) %>%
  filter(!pfr_player_id %in% c("RushCo00")) %>%
  group_by(team) %>%
  arrange(desc(total_off_snaps)) %>%
  slice_head()

qb_active <- rosters %>%
  filter((pfr_id %in% c(snap_counts_topQB$pfr_player_id))|
           gsis_id == "00-0038102" #Kenny Pickett
  )

QB_EPA <- pbp %>%
  filter(!is.na(qb_epa),
         passer_id %in% qb_active$gsis_id,
         qb_dropback == 1,
         down <= 4
  ) %>%
  mutate(down_type = ifelse(down %in% c(1,2), "early", "late")) %>%
  group_by(name, passer_id, posteam, season, down_type) %>%
  summarize(tot_epa = sum(qb_epa),
            mean_epa = mean(qb_epa),
            n = n()) %>%
  ungroup() %>%
  left_join(rosters, by = c("passer_id" = "gsis_id"))

QB_EPA_better <- QB_EPA %>%
  select(-c(tot_epa,n)) %>%
  pivot_wider(
    names_from = down_type,
    values_from = mean_epa
  ) %>%
  pull(name)

# plot --------------------------------------------------------------------

QB_EPA %>%
  ggplot(aes(x = down_type, y = mean_epa, color = posteam, group = posteam))+
  annotate(
    "segment",
    x = -Inf, xend = 2,
    y = seq(-0.4,0.8,0.2), yend = seq(-0.4,0.8,0.2),
    color = "#d1d1d1",
    linetype = "dashed",
    linewidth = 0.4)+
  geom_vline(xintercept = c(1,2))+
  geom_line(data = . %>% filter(!name %in% QB_EPA_better),
            color = "grey")+
  geom_point(data = . %>% filter(!name %in% QB_EPA_better),
             color = "grey",
             shape = 21,
             fill = "white")+
  geom_text_repel(data = . %>% filter(down_type == "late",
                                      name %in% QB_EPA_better),
                  aes(x = 2,
                      label = last_name),
                  nudge_x = 0.05,
                  hjust = 0,
                  direction = "y",
                  min.segment.length = unit(8,"pt"),
                  fontface = "bold",
                  family = "Open Sans"
  )+
  geom_line(data = . %>% filter(name %in% QB_EPA_better),
            linewidth = 1)+
  geom_point(data = . %>% filter(name %in% QB_EPA_better),
             shape = 21,
             size = 2,
             fill = "white")+
  annotate(
    "text",
    x = 1.5, y =0.46, angle = 35,
    label = "Better on late downs"
  )+
  annotate(
    "text",
    x = 1.5, y = -0.31, angle = -32,
    label = "Worse on late downs",
    color = "grey"
  )+
  scale_x_discrete(
    expand = expansion(mult = c(0.2,0.4)),
    labels = c(
      "Early Downs\n(1st/2nd)",
      "Late Downs\n(3rd/4th)"
    )
  )+
  scale_y_continuous(
    breaks = seq(-1,1,0.2)
  )+
  scale_color_nfl()+
  labs(
    title = "<span style='color:#E31837'>Mahomes</span> and <span style='color:#008E97'>Tua</span> are another level for late downs",
    y = "EPA/dropback",
    caption = "Bruno Mioto @BrunoHMioto - data: @nflverse"
  )+
  theme_minimal()+
  theme(
    text = element_text(family = "Open Sans"),
    plot.title = element_markdown(size = 13, face = "bold"),
    plot.title.position = "plot",
    plot.background = element_rect(fill = "#f1f1f1", color = NA),
    axis.title.x = element_blank(),
    axis.text.x = element_text(face = "bold"),
    panel.grid = element_blank()
  )

ggsave("slope_early_late_downs.png", width = 5, height = 6)

Mahomes Scale 📐

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(nflreadr)
library(nflplotR)
library(ggplot2)
library(ggrepel)
library(tidyr)
library(elementalist)
library(stringr)
library(scales)

# data --------------------------------------------------------------------

pbp <- nflreadr::load_pbp(seasons = c(2018:2022))

QB_data <- pbp %>%
  dplyr::filter(
    !is.na(qb_epa),
    qb_dropback == 1,
    down <= 4
    ) %>%
  dplyr::mutate(down_var = ifelse(down <= 2, 
                                  "early", 
                                  "late")
                ) %>% 
  dplyr::group_by(name, 
                  passer_id, 
                  posteam, 
                  down_var
                  ) %>% 
  dplyr::summarize(
    mean_epa = mean(qb_epa),
    n = n()
    ) %>% 
  dplyr::filter(n >= 100) %>% 
  tidyr::pivot_wider(
    names_from = down_var,
    values_from = c(mean_epa,n)
    ) %>% 
  dplyr::filter(
    !is.na(mean_epa_early),
    !is.na(mean_epa_late)
    ) %>% 
  dplyr::mutate(
    n_combined = n_early+n_late,
    name = stringr::str_extract(name, "(?<=\\.).*")
    )

# plot --------------------------------------------------------------------

# to ggrepel with bubble size work
my_pal <- function(range = c(1,6)) {
  force(range)
  function(x) scales::rescale(x, to = range, from = c(0, 1))
}

QB_data %>% 
  ggplot(aes(x = mean_epa_early, y = mean_epa_late))+
  geom_hline(yintercept = 0, linetype = "dashed",
             color = "grey30")+
  geom_vline(xintercept = 0, linetype = "dashed",
             color = "grey30")+
  geom_path(aes(x = c(0:0.1), y = c(0:0.2)),
            color = "blue")+
  geom_point(aes(fill = posteam, color = posteam, 
                 size = n_combined),
             shape = 21)+
  continuous_scale(
    aesthetics = c("size", "point.size"), scale_name = "size",
    palette = my_pal(c(2,6)),
    guide = guide_legend(override.aes = list(label = "")) # hide "a" in legend
  ) +
  geom_text_repel(aes(label = name,
                      point.size = n_combined,
                      fontface = ifelse(name == "Mahomes", "bold", "plain")),max.time = 5,
                  max.overlaps = 7)+
  scale_x_continuous(breaks = seq(-0.5,0.5,0.1),
                     limits = c(-0.02,NA))+
  scale_y_continuous(breaks = seq(-0.5,0.5,0.1),
                     limits = c(-0.21,NA))+
  scale_fill_nfl("primary")+
  scale_color_nfl("secondary")+
  labs(
    title = "Introducing the Mahomes Scale",
    subtitle = "For players breaking records and charts (2018-2022 min. 100 dropbacks)",
    caption = "Bruno Mioto @BrunoHMioto - data: nflfastR",
    x = "EPA/Play on early downs (1st&2nd)",
    y = "EPA/Play on late downs (3rd&4th)"
  )+
  theme_classic()+
  theme(
    text = element_text(family = "Open Sans"),
    axis.line = element_line(linewidth = 1),
    axis.line.x = element_line_multicolour(c("black", "black", "black","black","black","black", "#E31837"),
                                           size = 1),
    axis.line.y = element_line_multicolour(c("black", "black", "#E31837", "#E31837"),
                                           size = 1),
    axis.text = element_text(face = "bold", color = "black"),
    axis.text.y = element_text(color = c(rep("black",4),"#d02138","#e31d3b","#E31837")),
    axis.ticks.y = element_line(color = c(rep("black",4),"#d02138","#e31d3b","#E31837")),
    axis.ticks.x = element_line(color = "black"),
    axis.title = element_text(face = "bold"),
    panel.grid.major.x = element_line(color = "#dddddd"),
    panel.grid.major.y = element_line(color = c("#E3A3AD","#E38A98","#E36679","#dddddd","#dddddd","#dddddd", "#dddddd")),
    plot.title = element_text(face = "bold", size = 18),
    plot.title.position = "plot",
    plot.background = element_rect(fill = "#f1f1f1", color = NA),
    panel.background = element_rect(fill = NA, color = NA),
    legend.position = "none"
  )+
  coord_cartesian(xlim = c(-0.02,NA),
                  ylim = c(-0.2,NA))


ggsave("figures/mahomes_scale.png", width = 8, height = 6.5)

EPA&CPOE Scatterplot 💫

Code - Click to view
# packages ----------------------------------------------------------------

library(dplyr)
library(nflreadr)
library(nflplotR)
library(ggplot2)
library(ggdensity)
library(ggforce)
library(ggrepel)
library(ggtext)
library(ggthemes)

# data --------------------------------------------------------------------

pbp_2006_2009 <- progressr::with_progress(load_pbp(seasons = 2006:2009))

pbp_2010_2022 <- progressr::with_progress(load_pbp(seasons = 2010:2022))

pbp_all <- pbp_2006_2009 %>% 
  bind_rows(pbp_2010_2022)

players <- load_players()

passerID <- "00-0033873" #P. Mahomes

QB_CPOE <- pbp_all %>% 
  filter(!is.na(cpoe),
         !is.na(passer_id),
         down <= 4,
         game_half != "Overtime") %>% 
  group_by(game_id, game_half, passer_id) %>% 
  mutate(rows = n()) %>% 
  filter(rows >= 5) %>% 
  ungroup() %>% 
  group_by(game_id, game_half, passer_id) %>% 
  summarize(cmp = mean(complete_pass),
            mean_cpe = mean(cp, na.rm = TRUE),
            mean_cpoe = mean(cpoe, na.rm = TRUE))


QB_EPA <- pbp_all %>%
  filter(!is.na(qb_epa),
         !is.na(passer_id),
         rush == 1 | pass == 1,
         down <= 4,
         game_half != "Overtime"
  ) %>%
  group_by(game_id,game_half, passer_id) %>% 
  mutate(rows = n()) %>% 
  filter(rows >= 5) %>% 
  ungroup() %>% 
  group_by(game_id, passer_id,game_half, posteam, defteam, season, week, game_date) %>% 
  summarize(tot_epa = sum(qb_epa),
            mean_epa = mean(qb_epa)) 


last_game <- QB_EPA %>% 
  filter(passer_id %in% passerID) %>% 
  tail(2)



all_epa_cpoe <- QB_EPA %>% 
  left_join(QB_CPOE) %>% 
  filter(!is.na(cmp))

ggplot(all_epa_cpoe, aes(x=mean_cpoe, y=mean_epa))+
  geom_hdr(method = "mvnorm")+
  geom_vline(xintercept = 0, color = "#F0F0F0")+
  geom_hline(yintercept = 0, color = "#F0F0F0")+
  geom_vline(xintercept = 0, linetype = "dashed", color = "#333333")+
  geom_hline(yintercept = 0, linetype = "dashed", color = "#333333")+
  #career
  geom_point(data = all_epa_cpoe %>% 
               filter(passer_id %in% passerID),
             fill = "#F0F0F0",
             color = "black",
             shape = 21,
             size=4,
             alpha = 0.8)+
  #text
  geom_text_repel(data = all_epa_cpoe %>% 
                    filter(passer_id %in% passerID),
                  size = 5, 
                  box.padding = 1.2,
                  min.segment.length = 0,
                  segment.curvature = -0.1,
                  segment.ncp = 3,
                  #segment.angle = 20,
                  segment.size = 0.8,
                  force = 3,
                  force_pull = 0.2,
                  max.overlaps = Inf,
                  #max.time = 5,max.iter = 1000000,
                  lineheight = 0.8,
                  bg.color = "#fafafa",
                  aes(label = ifelse(game_id %in% last_game$game_id &
                                       game_half == "Half1",
                                     #glue::glue("1st Half\nvs. {last_game$defteam}"),
                                     "",
                                     ifelse(game_id %in% last_game$game_id &
                                              game_half == "Half2",
                                            glue::glue("2nd Half\nSB LVII"),"")))
  )+
  #text
  geom_text_repel(data = all_epa_cpoe %>% 
                    filter(passer_id %in% passerID),
                  #x = -7, y = 0.75,
                  xlim = -13,
                  ylim = 0.6,
                  size = 5, 
                  box.padding = 1.2,
                  min.segment.length = 0,
                  segment.curvature = 0.1,
                  segment.ncp = 2,
                  segment.angle = 30,
                  segment.size = 0.8,
                  force = 3,
                  force_pull = 0.2,
                  max.overlaps = Inf,
                  #max.time = 5,max.iter = 1000000,
                  lineheight = 0.8,
                  bg.color = "#fafafa",
                  aes(label = ifelse(game_id %in% last_game$game_id &
                                       game_half == "Half1",
                                     glue::glue("1st Half\nSB LVII"),
                                     ""))
  )+
  #this season
  geom_point(data = all_epa_cpoe %>% 
               filter(passer_id %in% passerID &
                        season == 2022),
             aes(fill = posteam),
             color = "white",
             shape = 21,
             size=4)+
  #last game
  geom_point(data = all_epa_cpoe %>% 
               filter(passer_id %in% passerID &
                        game_id %in% last_game$game_id),
             aes(fill = posteam),
             color = "black",
             show.legend = FALSE,
             shape = 21,
             size=4,
             stroke = 1.2)+
  #annotate 1.5-1.5 -35-35
  annotate(geom = "text",
         x = 21, y = -1.17,
         label = "Passing accuracy")+
  annotate(geom = "segment",
           size = 1,
           x = 13, y = -1.25, xend = 30, yend = -1.25,
           arrow = arrow(length = unit(0.5, "cm")),
           lineend = "round",
           linejoin = "round")+
  annotate(geom = "text",
           angle = 90,
           x = -32.5, y = 0.85,
           label = "Scoring efficiency")+
  annotate(geom = "segment",
           size = 1,
           x = -34, y = 0.55, xend = -34, yend = 1.3,
           arrow = arrow(length = unit(0.5, "cm")),
           lineend = "round",
           linejoin = "round")+
  scale_fill_nfl()+
  #scale_color_identity()+
  guides(alpha = guide_legend(reverse = TRUE))+
  labs(#title = glue::glue("EPA/play and CPOE in half games of {last_game$full_name}<br> career and <span style='color:{last_game$team_color}'>2022 season</span>"),
      title = "EPA/play and CPOE in half games of Patrick Mahomes<br>career and <span style='color:#E31837'>2022 season</span>", 
      subtitle = "NFL QB games from 2006 to 2022 season",
       y = "Expected Points Added per play (EPA/Play)",
       x = "Completion % over expected (CPOE)",
       alpha = "NFL QB games density",
       caption = "Bruno Mioto @BrunoHMioto - Data: nflfastR")+
  theme_classic()+
  theme(
    text = element_text(family = "Open Sans"),
    strip.text = element_text(face = "bold"),
    axis.text = element_text(face = "bold"),
    axis.title = element_text(face = "bold"),
    axis.ticks = element_blank(),
    axis.line = element_blank(),
    plot.title = element_markdown(size = 20, face = "bold", color = "#3c3c3c"),
    plot.subtitle = element_text(size = 12),
    plot.title.position = "plot",
    plot.margin = margin(15,15,15,15,"pt"),
    legend.position = "top",
    panel.grid.major = element_line(),
    plot.background = element_rect(fill = "#fafafa"),
    panel.background = element_rect(fill = "#fafafa"),
    legend.background = element_rect(fill = "#fafafa"),
  )+
  coord_cartesian(ylim = c(-1.3, 1.3),
                  xlim = c(-35, 35),
                  clip = "off")


ggsave(filename = "CPOE&EPA_P_Mahomes_SB_2022_2.png",
       #filename = glue::glue("CPOE&EPA_{last_game$full_name}_{last_game$game_id}_bengals_games.png"),
       width = 8,
       height = 8)