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)

The first issue is that you specified the
coloraes as a global aesthetic. AsmlbplotR::geom_mlb_headshotsinherits the global aes your headshots get colored according topitch_name. To fix that I addedcolor=NULLbut you could also useinherit.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 usingdata = ~distinct(.x, pitcher, player_name)inmlbplotR::geom_mlb_headshots: