Skip to content

Instantly share code, notes, and snippets.

@bayesball
Created November 2, 2021 16:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bayesball/12a3c39189ebac34effa34fb11839019 to your computer and use it in GitHub Desktop.
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.
---
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