Aaron Judge HR Race 2022
A short project visualizing Aaron Judge's 2022 home run chase using R, comparing his season to Roger Maris (1961) and Barry Bonds (2001).
This is a little project I did while learning R where I compared Aaron Judge’s 2022 season home run total to that of Roger Maris in 1961 and Barry Bonds in 2001. I started by importing all the data from https://baseball-reference.com. The data looked something like this:
judge = read_html("https://www.baseball-reference.com/players/gl.fcgi?id=judgeaa01&t=b&year=2022") %>%
html_nodes("table") %>%
html_table()
judge = data.frame(judge[[5]])
maris = read_html("https://www.baseball-reference.com/players/gl.fcgi?id=marisro01&t=b&year=1961") %>%
html_nodes("table") %>%
html_table()
maris = data.frame(maris[[5]])
bonds = read_html("https://www.baseball-reference.com/players/gl.fcgi?id=bondsba01&t=b&year=2001") %>%
html_nodes("table") %>%
html_table()
bonds = data.frame(bonds[[5]])
head(judge)
Rk Gcar Gtm Date Team Var.6 Opp Result Inngs PA AB R H X2B X3B HR
1 1 573 1 2022-04-08 NYY BOS W, 6-5 (11) CG(11) 5 5 1 2 1 0 0
2 2 574 2 2022-04-09 NYY BOS W, 4-2 CG 4 3 1 0 0 0 0
3 3 575 3 2022-04-10 NYY BOS L, 3-4 CG 5 5 0 2 0 0 0
4 4 576 4 2022-04-11 NYY TOR L, 0-3 CG 4 3 0 0 0 0 0
5 5 577 5 2022-04-12 NYY TOR W, 4-0 CG 4 4 0 1 1 0 0
6 6 578 6 2022-04-13 NYY TOR L, 4-6 CG 5 4 1 2 0 0 1
RBI SB CS BB SO BA OBP SLG OPS TB GIDP HBP SH SF ROE IBB BAbip aLI
1 0 0 0 0 1 .400 .400 .600 1.000 3 0 0 0 0 0 0 .500 1.20
2 0 0 0 1 0 .250 .333 .375 .708 0 0 0 0 0 0 0 .000 1.01
3 0 1 0 0 2 .308 .357 .385 .742 2 1 0 0 0 0 0 .667 1.86
4 0 0 0 1 2 .250 .333 .313 .646 0 0 0 0 0 0 0 .000 1.27
5 0 0 0 0 0 .250 .318 .350 .668 2 0 0 0 0 0 0 .250 0.56
6 1 0 0 1 1 .292 .370 .500 .870 5 0 0 0 0 0 0 .500 1.33
WPA acLI cWPA RE24 DFS.DK. DFS.FD. BOP Pos
1 -0.002 1.17 0.00% -0.19 10.00 12.20 2 RF
2 0.006 1.02 0.00% -0.13 4.00 6.20 2 CF RF
3 -0.043 1.89 -0.03% 0.41 11.00 12.00 3 RF
4 -0.039 1.31 -0.02% -0.28 2.00 3.00 3 RF
5 -0.037 0.55 -0.02% 0.49 5.00 6.00 2 CF RF
6 0.119 1.36 0.07% 0.94 19.00 24.70 2 RF
I obviously didn’t need all of these columns, and some of them are the wrong data type, so the next thing I did remove all the columns I didn’t need, and convert the ones I did need from character types to what I wanted. Now it looked like this:
# change "Date" column values fram characters to "Date" type
judge["Date"] <- as.Date(judge$Date)
maris["Date"] <- as.Date(maris$Date)
bonds["Date"] <- as.Date(bonds$Date)
# change "HR" column values from characters to numeric
judge["HR"] <- suppressWarnings(as.numeric(judge$HR))
maris["HR"] <- suppressWarnings(as.numeric(maris$HR))
bonds["HR"] <- suppressWarnings(as.numeric(bonds$HR))
# change "Gtm" column values from characters to numeric (and remove games they missed)
judge["Gtm"] <- as.numeric(str_first_number(judge$Gtm))
maris["Gtm"] <- as.numeric(str_first_number(maris$Gtm))
bonds["Gtm"] <- as.numeric(str_first_number(bonds$Gtm))
# select only the columns I want (and remove null values)
judge = na.omit(select(judge, Gtm, Date, HR))
maris = na.omit(select(maris, Gtm, Date, HR))
bonds = na.omit(select(bonds, Gtm, Date, HR))
head(judge)
Gtm Date HR
1 1 2022-04-08 0
2 2 2022-04-09 0
3 3 2022-04-10 0
4 4 2022-04-11 0
5 5 2022-04-12 0
6 6 2022-04-13 1
This looks a lot better, but it’s still not quite what I wanted. For one I wanted cumulative home run totals, not the per-game numbers. I also wanted to combine double-headers so that the tables were only organized by date, as well as remove the games in which they didn’t hit home runs in order to clean up the data.
# add column with cumulative homer total
cumHRs = cumsum(judge["HR"])
colnames(cumHRs) = "cumHRs"
judge = cbind(judge, cumHRs)
cumHRs = cumsum(maris["HR"])
colnames(cumHRs) = "cumHRs"
maris = cbind(maris, cumHRs)
cumHRs = cumsum(bonds["HR"])
colnames(cumHRs) = "cumHRs"
bonds = cbind(bonds, cumHRs)
# remove games in which they don't homer
judge = judge[which(judge$HR > 0 | judge$cumHRs == 0), ]
maris = maris[which(maris$HR > 0 | maris$cumHRs == 0), ]
bonds = bonds[which(bonds$HR > 0 | bonds$cumHRs == 0), ]
tail(judge)
Gtm Date HR cumHRs
137 136 2022-09-07 1 55
143 142 2022-09-13 2 57
147 146 2022-09-18 2 59
148 147 2022-09-20 1 60
156 155 2022-09-28 1 61
163 161 2022-10-04 1 62
Finally, I could combine all three players’ tables and create an animated graph showing the home run race over time. Here is the final product:
# add column with players' names (for graphing purposes)
judge = cbind(Player = "Judge", judge)
maris = cbind(Player = "Maris", maris)
bonds = cbind(Player = "Bonds", bonds)
# combine the player data
allGames = rbind(judge, maris, bonds)
# create animated graph
graph <- ggplot(allGames, aes(x=Gtm, y=cumHRs, group=Player, color=Player)) +
geom_line() +
geom_point() +
ggtitle("MLB Home Run Race") +
theme_ipsum() +
ylab("Home Runs") +
xlab("Game") +
transition_reveal(Gtm) +
theme(aspect.ratio=3/4)
animate(graph, fps = 7, end_pause = 10)

The cool thing about this program is that even though I wrote this while the season was ongoing, it gathers the data from the web so that it’s always up to date. Also because of this, I can easily edit the code if I decide to compare different players in different seasons.