Created
November 2, 2021 16:11
-
-
Save bayesball/12a3c39189ebac34effa34fb11839019 to your computer and use it in GitHub Desktop.
Markdown file to compute random effects estimates of team defenses on ground balls.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
title: "AVG on Ground Balls" | |
author: "Jim Albert" | |
date: "11/2/2021" | |
output: html_document | |
--- | |
```{r setup, include=FALSE} | |
knitr::opts_chunk$set(echo = TRUE, | |
message = FALSE, | |
warning = FALSE) | |
``` | |
Read in the necessary packages. | |
```{r} | |
library(dplyr) | |
library(readr) | |
library(ggplot2) | |
library(lme4) | |
library(ggrepel) | |
``` | |
Couple of helper functions to read in. | |
```{r} | |
increasefont <- function (){ | |
theme(text = element_text(size = 18)) | |
} | |
centertitle <- function () { | |
theme(plot.title = element_text(colour = | |
"blue", size = 18, | |
hjust = 0.5, vjust = 0.8, angle = 0)) | |
} | |
``` | |
Read in the Statcast data from my hard drive. | |
```{r} | |
statcast2021 <- read_csv("~/Dropbox/2021 WORK/Statcast2021/ORIGINAL/data/statcast2021.csv") | |
``` | |
Data work. Creating the fielding team and hit variables. Only considering ground balls. | |
```{r} | |
hits <- c("single", "double", "triple", | |
"home_run") | |
statcast2021 %>% | |
filter(type == "X") %>% | |
mutate(BAT_TEAM = ifelse(inning_topbot == "Top", | |
away_team, home_team), | |
FIELD_TEAM = ifelse(inning_topbot == "Top", | |
home_team, away_team), | |
H = ifelse(events %in% hits, 1, 0)) %>% | |
filter(launch_angle < 10) %>% | |
select(game_year, BAT_TEAM, | |
FIELD_TEAM, launch_angle, launch_speed, | |
H, if_fielding_alignment, stand) -> scip | |
``` | |
Some minor editing to remove one weird case. | |
```{r} | |
filter(scip, if_fielding_alignment != "-0.014") -> | |
scip | |
``` | |
Find AVG for each type of batting side and fielding alignment: | |
```{r} | |
scip %>% | |
group_by(stand, if_fielding_alignment) %>% | |
summarize(N = n(), AVG = mean(H)) -> S1 | |
``` | |
Graph these situational AVGs. | |
```{r} | |
ggplot(S1, aes(if_fielding_alignment, AVG, | |
label = stand, | |
color = stand)) + | |
geom_text(size = 6, | |
fontface = "bold") + | |
increasefont() + | |
centertitle() + | |
ggtitle("2021 Groundball AVG by Fielding Alignment and Stand") | |
``` | |
Fit random effects model. | |
```{r} | |
fit <- glmer(H ~ stand + if_fielding_alignment + | |
stand * if_fielding_alignment + | |
(1 | FIELD_TEAM), | |
family = binomial, | |
data = scip) | |
``` | |
Collect random effects into a data frame. | |
```{r} | |
ranef(fit)$FIELD_TEAM -> RE | |
RE$Team <- row.names(RE) | |
row.names(RE) <- NULL | |
names(RE)[1] <- "randeff" | |
``` | |
Add league info to data frame. | |
```{r} | |
RE$League <- c("NL", "NL", "NL", "NL", "AL", "NL", "NL", "NL", | |
"AL", "NL", "AL", "NL", "NL", "AL", "AL", "NL", | |
"AL", "AL", "AL", "AL", "NL", "NL", "AL", "NL", | |
"AL", "AL", "AL", "NL", "AL", "AL") | |
``` | |
Graph random effects estimates for each league. | |
```{r} | |
ggplot(RE, aes(randeff, League, label = Team)) + | |
geom_text_repel() + | |
increasefont() + | |
ggtitle("2021 Team Random Effects") + | |
centertitle() + | |
xlab("Random Effect") | |
``` | |
I collected the Fielding Bible team fielding estimates into a csv file -- read this into R and merge with random effects data frame. | |
```{r} | |
fb <- read_csv("fieldingbible.csv") | |
inner_join(RE, fb, by = c("Team" = "T_abb")) -> fb | |
``` | |
Computing infield FB measurements. | |
```{r} | |
fb %>% | |
mutate(Infield = X1B + X2B + X3B + XSS) -> | |
fb | |
``` | |
Scatterplot of RE and FB estimates. | |
```{r} | |
ggplot(fb, aes(randeff, Infield, | |
label = Team)) + | |
geom_text() + | |
increasefont() + | |
centertitle() + | |
ylab("FB Infield") + | |
xlab("Random Effect") + | |
ggtitle("Scatterplot of Fielding Bible Infield and Random Effects") | |
``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment