---
title: "2023 MLB Season Offensive Stats"
output:
flexdashboard::flex_dashboard:
storyboard: true
social: menu
source: embed
---
```{r setup, include=FALSE}
library(flexdashboard)
library(tidyverse)
library(ggthemes)
library(Lahman)
knitr::opts_chunk$set(echo = TRUE)
```
### Top 6 Hitters
```{r echo=FALSE}
data_2023 <- Batting %>%
filter(yearID == 2023) %>%
select(playerID, H, X2B, X3B, HR) %>%
mutate(
singles = H - (X2B + X3B + HR),
total_hits = H
)
player_names <- data.frame(
playerID = c("acunaro01", "freemfr01", "arraelu01", "semiema01", "rodriju01", "bettsmo01"),
player_name = c("Ronald Acuna Jr", "Freddie Freeman", "Luis Arraez", "Marcus Semien", "Julio Rodriguez", "Mookie Betts")
)
top_6_hitters <- data_2023 %>%
filter(playerID %in% player_names$playerID) %>%
arrange(desc(total_hits)) %>%
slice(1:6) %>%
select(playerID, total_hits)
data_2023_with_names <- data_2023 %>%
filter(playerID %in% player_names$playerID) %>%
left_join(player_names, by = "playerID") %>%
mutate(
singles = H - (X2B + X3B + HR),
X2B = X2B,
X3B = X3B,
HR = HR
) %>%
select(player_name, singles, X2B, X3B, HR, total_hits) %>%
pivot_longer(cols = c(singles, X2B, X3B, HR), names_to = "hit_type", values_to = "hit_count")
data_2023_with_names$hit_type <- recode(data_2023_with_names$hit_type,
"X2B" = "Doubles",
"X3B" = "Triples",
"singles" = "Singles",
"HR" = "Home Runs")
data_2023_with_names$player_name <- factor(data_2023_with_names$player_name,
levels = c("Ronald Acuna Jr", "Freddie Freeman", "Luis Arraez",
"Marcus Semien", "Julio Rodriguez", "Mookie Betts"))
ggplot(data_2023_with_names, aes(x = "", y = hit_count, fill = hit_type)) +
geom_bar(stat = "identity", width = 1, color = "black") +
coord_polar(theta = "y") +
facet_wrap(~ player_name, scales = "free_y") +
labs(title = "Distribution of Hits for Top 6 Hitters (2023)",
fill = "Hit Type") +
theme_void() +
theme(legend.position = "top") +
geom_text(aes(label = paste(hit_count, "\n", hit_type)), position = position_stack(vjust = 0.5)) +
scale_fill_manual(values = c("Singles" = "#5ABF7E", "Doubles" = "#F4A300", "Triples" = "#1E90FF", "Home Runs" = "#FF5733")) +
geom_text(data = data_2023_with_names, aes(x = 1.5, y = 0, label = paste("Total Hits: ", total_hits)),
inherit.aes = FALSE, size = 3, hjust = 0.5, vjust = -2, color = "black")
```
***
- This is a group of pie charts showing the 6 players with the most hits and the distribution of the types.
- It shows their totals hits and how many were singles, doubles, triples, or home runs.
- Some player notes:
- Arraez, with the third most hits, has significantly more singles and less home runs than the rest.
- Acuna has the most hits. He has the most home runs and second most singles.
### Top 10 Home Run Hitters
```{r echo=FALSE}
top_hr_2023 <- Batting %>%
filter(yearID == 2023) %>%
group_by(playerID) %>%
summarise(HR = sum(HR, na.rm = TRUE)) %>%
arrange(desc(HR)) %>%
slice_head(n = 10) %>%
left_join(People, by = "playerID") %>%
mutate(player_name = paste(nameFirst, nameLast))
ggplot(top_hr_2023, aes(x = reorder(player_name, HR), y = HR)) +
geom_bar(stat = "identity", fill = "#E69F00") +
coord_flip() +
labs(title = "Top 10 Home Run Hitters (2023)", x = "Player", y = "Home Runs") +
theme_minimal()
```
***
- This bar graph shows the top 10 home run hitters.
- There is a 7 home run differential between first, Olson, and second, Schwarber.
- From second to tenth place, the greatest differential in home runs between two consecutive places is only 3.
### Home Runs vs Strikeouts
```{r echo=FALSE}
hr_k_2023 <- Batting %>%
filter(yearID == 2023) %>%
select(playerID, HR, SO) %>%
filter(!is.na(HR), !is.na(SO), SO > 0)
ggplot(hr_k_2023, aes(x = SO, y = HR)) +
geom_point(alpha = 0.6, color = "#D55E00", size = 2) +
geom_smooth(method = "lm", se = TRUE, color = "#0072B2") +
labs(
title = "Home Runs vs Strikeouts (2023)",
subtitle = "With Linear Trend Line",
x = "Strikeouts", y = "Home Runs"
) +
theme_minimal()
```
***
- This is a scatter plot showing the relationship between strikeouts and home runs.
- The line shows the general trend of players striking out more also hitting more home runs but there are many exceptions.
- The player with the most strikeouts, Kyle Schwarber, ending up hitting the second most home runs.
### Walks vs Hits
```{r echo=FALSE}
walks_hits_2023 <- Batting %>%
filter(yearID == 2023, AB > 50) %>%
select(playerID, BB, H)
ggplot(walks_hits_2023, aes(x = BB, y = H)) +
geom_point(color = "purple", alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE, color = "black") +
labs(
title = "Walks vs. Hits (2023)",
x = "Walks (BB)",
y = "Hits (H)"
) +
theme_minimal()
```
***
- This scatter plot looks at walks and hits.
- The trend line shows that players that are walked more tend to hit more too.
### Batting Averages
```{r echo=FALSE}
avg_league_2023 <- Batting %>%
filter(yearID == 2023, AB > 50) %>%
mutate(AVG = H / AB) %>%
left_join(Teams %>% filter(yearID == 2023) %>% select(teamID, lgID), by = "teamID")
avg_league_2023 <- Batting %>%
filter(yearID == 2023, AB > 50) %>%
mutate(AVG = H / AB)
avg_league_2023 <- avg_league_2023 %>%
left_join(Teams %>% filter(yearID == 2023) %>% select(teamID, yearID, lgID),
by = c("teamID", "yearID"))
avg_league_2023 <- avg_league_2023 %>%
rename(lgID = lgID.y)
avg_league_2023 <- avg_league_2023 %>%
select(-lgID.x)
ggplot(avg_league_2023, aes(x = AVG)) +
geom_density(fill = "#56B4E9", alpha = 0.7) +
labs(title = "Density Plot of Batting Averages (2023)", x = "Batting Average", y = "Density") +
theme_minimal()
```
***
- This a density plot of the batting average of players in the league.
- The batting average that the most players have is a little greater than 0.250.
- As the batting average approaches the peak, a little greater than 0.250, the amount of players gradually rises, but after the peak, there is a steep drop that starts to flatten closer to 0.300.
### Batting Averages By Division
```{r echo=FALSE}
avg_division_2023 <- Batting %>%
filter(yearID == 2023, AB > 50) %>%
mutate(AVG = H / AB)
avg_division_2023 <- avg_division_2023 %>%
left_join(
Teams %>% filter(yearID == 2023) %>% select(teamID, yearID, lgID, divID),
by = c("teamID", "yearID")
)
avg_division_2023 <- Batting %>%
filter(yearID == 2023, AB > 50) %>%
mutate(AVG = H / AB)
avg_division_2023 <- avg_division_2023 %>%
left_join(
Teams %>% filter(yearID == 2023) %>% select(teamID, yearID, lgID, divID),
by = c("teamID", "yearID")
)
avg_division_2023 <- avg_division_2023 %>%
mutate(division = recode(
paste(as.character(lgID.y), as.character(divID)),
"AL E" = "AL East",
"AL C" = "AL Central",
"AL W" = "AL West",
"NL E" = "NL East",
"NL C" = "NL Central",
"NL W" = "NL West"
))
avg_division_2023$division <- factor(avg_division_2023$division,
levels = c("AL East", "AL Central", "AL West",
"NL East", "NL Central", "NL West"))
ggplot(avg_division_2023, aes(x = division, y = AVG, fill = division)) +
geom_boxplot() +
scale_y_continuous(
breaks = seq(0.000, 0.400, by = 0.025),
limits = c(0.000, 0.400)
) +
labs(
title = "Batting Average by Division (2023)",
x = "Division", y = "Batting Average"
) +
theme_minimal()
```
***
- This box plot compares the batting averages of each division.
- The division with the best batting average is the NL East. They boast the player in the MLB with the highest batting average, just above 0.350, and the best range of batting average from the minimum to maximum.
- The player with the worst batting average in the MLB plays in the AL Central, however the worst minimum value is in the NL West.
- The AL East has the most outliers in their batting average range.