How do I correctly add images to a facet-wrapped ggplot? My current output returns images as monochromatic silhouettes

55 Views Asked by At

I am attempting to create a facet-wrapped scatterplot that displays pitch movement data for three major league pitchers. The only thing I can't figure out is how to add headshot images of these three players to each pitcher's data. My current code 1) takes several minutes grabbing/placing the player headshots, despite there only being three images, and 2) outputs the headshots as red silhouettes. Why are the images appearing this way and how can I fix? Code and visual of output below.

library(baseballr)
library(dplyr)
library(tidyverse)
library(grid)
library(mlbplotR)
library(ggplot2)

# pull data for three different pitchers
senga <- baseballr::statcast_search_pitchers(start_date = "2023-03-30",
                                             end_date = "2023-04-03",
                                             pitcherid = 673540)
strider <- baseballr::statcast_search_pitchers(start_date = "2023-03-30",
                                               end_date = "2023-04-03",
                                               pitcherid = 675911)
kirby <- baseballr::statcast_search_pitchers(start_date = "2023-03-30",
                                             end_date = "2023-04-03",
                                             pitcherid = 669923)
# bind to one df
df <- rbind(senga,strider,kirby) %>%
  filter(pitch_type != 'PO')

player_cleaned_data <- all_data %>% 
  # Only keep rows with pitch movement readings and during the regular season
  filter(!is.na(pfx_x), !is.na(pfx_z),
                game_type == "R") %>% 
  mutate(pfx_x_in_pv = -12*pfx_x,
                pfx_z_in = 12*pfx_z)

# Make a named vector to scale pitch colors with
pitch_colors <- c("4-Seam Fastball" = "red",
                  "2-Seam Fastball" = "orange",
                  "Sinker" = "cyan",
                  "Cutter" = "firebrick",
                  "Fastball" = "gray",
                  "Curveball" = "blue",
                  "Knuckle Curve" = "pink",
                  "Slider" = "orange",
                  "Changeup" = "#4cbb17",
                  "Forkball" = "hotpink",
                  "Split-Finger" = "#FC6C85",
                  "Sweeper" = "coral",
                  "Knuckleball" = "black")

# Find unique pitch types to not have unnecessary pitches in legend
pitch_types <- unique(player_cleaned_data$pitch_name)

player_cleaned_data %>% 
  ggplot(aes(x = pfx_x_in_pv, y = pfx_z_in, color = pitch_name)) +
  geom_vline(xintercept = 0) +
  geom_hline(yintercept = 0) +
  geom_point(size = 2, alpha = 0.45) +
  # Scale the pitch colors to match what we defined above
  scale_color_manual(values = pitch_colors,
                     limits = pitch_types) +
  # Scale axes and add " to end of labels to denote inches
  scale_x_continuous(limits = c(-25,25),
                     breaks = seq(-20,20, 5),
                     labels = scales::number_format(suffix = "\"")) +
  scale_y_continuous(limits = c(-25,25),
                     breaks = seq(-20,20, 5),
                     labels = scales::number_format(suffix = "\"")) +
  theme(text = element_text(family = "Trebuchet MS"),
        plot.title = element_text(size = 20, face = 'bold'),
        axis.title = element_text(size = 12),
        legend.title = element_blank(),
        legend.text = element_text(size = 11),
        plot.caption = element_text(size = 10),
        legend.key = element_rect(fill = NA)) +
  coord_equal() +
  labs(title = "Pitch Movement Profiles",
       subtitle = "2023 MLB Season | Pitcher's POV",
       caption = "Data: Baseball Savant via baseballr", 
       x = "Horizontal Break",
       y = "Induced Vertical Break", color = "Pitch Name") +
  facet_wrap(~full_name) +
  mlbplotR::geom_mlb_headshots(aes(player_id = pitcher), x = 17, y = 17, width = .2)

enter image description here

1

There are 1 best solutions below

1
stefan On BEST ANSWER

The first issue is that you specified the color aes as a global aesthetic. As mlbplotR::geom_mlb_headshots inherits the global aes your headshots get colored according to pitch_name. To fix that I added color=NULL but you could also use inherit.aes=FALSE. Second, the issue with the long rendering time is that you add a picture for each row of your data, i.e. you add the images multiple times. Instead, you only need a reduced dataset which could be achieved using data = ~distinct(.x, pitcher, player_name) in mlbplotR::geom_mlb_headshots:

library(baseballr)
library(tidyverse)
library(grid)
library(mlbplotR)
library(ggplot2)

player_cleaned_data %>%
  ggplot(aes(x = pfx_x_in_pv, y = pfx_z_in, color = pitch_name)) +
  geom_vline(xintercept = 0) +
  geom_hline(yintercept = 0) +
  geom_point(size = 2, alpha = 0.45) +
  # Scale the pitch colors to match what we defined above
  scale_color_manual(
    values = pitch_colors,
    limits = pitch_types
  ) +
  # Scale axes and add " to end of labels to denote inches
  scale_x_continuous(
    limits = c(-25, 25),
    breaks = seq(-20, 20, 5),
    labels = scales::number_format(suffix = "\"")
  ) +
  scale_y_continuous(
    limits = c(-25, 25),
    breaks = seq(-20, 20, 5),
    labels = scales::number_format(suffix = "\"")
  ) +
  theme(
    text = element_text(family = "Trebuchet MS"),
    plot.title = element_text(size = 20, face = "bold"),
    axis.title = element_text(size = 12),
    legend.title = element_blank(),
    legend.text = element_text(size = 11),
    plot.caption = element_text(size = 10),
    legend.key = element_rect(fill = NA)
  ) +
  coord_equal() +
  labs(
    title = "Pitch Movement Profiles",
    subtitle = "2023 MLB Season | Pitcher's POV",
    caption = "Data: Baseball Savant via baseballr",
    x = "Horizontal Break",
    y = "Induced Vertical Break", color = "Pitch Name"
  ) +
  facet_wrap(~player_name) +
  mlbplotR::geom_mlb_headshots(
    data = ~distinct(.x, pitcher, player_name),
    aes(player_id = pitcher, color = NULL),
    x = 17, y = 17, width = .2
  )