Update app.R
Browse files
app.R
CHANGED
|
@@ -152,6 +152,9 @@ team_leagues <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects",
|
|
| 152 |
college_guts <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_GUTS.csv")
|
| 153 |
college_join <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_join.csv")
|
| 154 |
|
|
|
|
|
|
|
|
|
|
| 155 |
#Team logo
|
| 156 |
logo_b64 <- base64enc::base64encode("HHLogo.png")
|
| 157 |
logo_uri <- paste0("data:image/png;base64,", logo_b64)
|
|
@@ -160,6 +163,102 @@ logo_uri <- paste0("data:image/png;base64,", logo_b64)
|
|
| 160 |
#Clean data
|
| 161 |
# ============================================================
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
#Function that predicts stuffplus
|
| 164 |
predict_stuffplus <- function(data) {
|
| 165 |
|
|
@@ -282,6 +381,8 @@ clean_college_data <- function(data, teams = NA) {
|
|
| 282 |
TaggedPitchType == "Undefined" ~ "Other",
|
| 283 |
T ~ TaggedPitchType
|
| 284 |
))
|
|
|
|
|
|
|
| 285 |
|
| 286 |
#Main aggregations
|
| 287 |
data <- data %>%
|
|
|
|
| 152 |
college_guts <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_GUTS.csv")
|
| 153 |
college_join <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_join.csv")
|
| 154 |
|
| 155 |
+
#Reference arsenal data (only pitches thrown over 5% for retagging)
|
| 156 |
+
pitcher_arsenals_over_5perc <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "pitcher_arsenals_over_5perc.csv")
|
| 157 |
+
|
| 158 |
#Team logo
|
| 159 |
logo_b64 <- base64enc::base64encode("HHLogo.png")
|
| 160 |
logo_uri <- paste0("data:image/png;base64,", logo_b64)
|
|
|
|
| 163 |
#Clean data
|
| 164 |
# ============================================================
|
| 165 |
|
| 166 |
+
#Function that retags pitches into a column of a Trackman dataset
|
| 167 |
+
retag_pitches <- function(data) {
|
| 168 |
+
|
| 169 |
+
#Uses create_pitcher_arsenals function to get arsenals from reference set (college season)
|
| 170 |
+
arsenal_percentages <- pitcher_arsenals_over_5perc %>%
|
| 171 |
+
mutate(in_reference = TRUE)
|
| 172 |
+
|
| 173 |
+
# get row ID for joins later
|
| 174 |
+
data <- data %>% mutate(row_id = row_number())
|
| 175 |
+
|
| 176 |
+
#aggregation that prepares data for prediction (removes NAs, creates features, flips HorzBreak and RelSide)
|
| 177 |
+
predict_data <- data %>%
|
| 178 |
+
filter(
|
| 179 |
+
!Pitcher %in% unique(tagged_sample$Pitcher),
|
| 180 |
+
!TaggedPitchType %in% c("Other", "Undefined", "FastBall", "Knuckleball"),
|
| 181 |
+
!is.na(TaggedPitchType), !is.na(RelSpeed),
|
| 182 |
+
!is.na(InducedVertBreak), !is.na(Extension)
|
| 183 |
+
) %>%
|
| 184 |
+
mutate(
|
| 185 |
+
max_velo_diff = max(RelSpeed, na.rm = TRUE) - RelSpeed,
|
| 186 |
+
max_vert_diff = InducedVertBreak[which.max(RelSpeed)] - InducedVertBreak,
|
| 187 |
+
max_ext_diff = Extension[which.max(RelSpeed)] - Extension,
|
| 188 |
+
.by = c(GameUID, Pitcher)
|
| 189 |
+
) %>%
|
| 190 |
+
mutate(
|
| 191 |
+
RelSide = case_when(
|
| 192 |
+
PitcherThrows == "Right" ~ RelSide,
|
| 193 |
+
PitcherThrows == "Left" ~ -RelSide,
|
| 194 |
+
PitcherThrows %in% c("Both", "Undefined") & RelSide > 0 ~ RelSide,
|
| 195 |
+
PitcherThrows %in% c("Both", "Undefined") & RelSide < 0 ~ -RelSide),
|
| 196 |
+
HorzBreak = case_when(
|
| 197 |
+
PitcherThrows == "Right" ~ HorzBreak,
|
| 198 |
+
PitcherThrows == "Left" ~ -HorzBreak,
|
| 199 |
+
PitcherThrows %in% c("Both", "Undefined") & HorzBreak > 0 ~ HorzBreak,
|
| 200 |
+
PitcherThrows %in% c("Both", "Undefined") & HorzBreak < 0 ~ -HorzBreak)
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
#Predcted probabilities from model
|
| 204 |
+
probs <- predict(final_fit, predict_data, type = "prob")
|
| 205 |
+
pred_col_names <- names(probs)
|
| 206 |
+
|
| 207 |
+
#Get row_id for join
|
| 208 |
+
probs$row_id <- predict_data$row_id
|
| 209 |
+
|
| 210 |
+
#Join over probabilities
|
| 211 |
+
data <- data %>%
|
| 212 |
+
left_join(arsenal_percentages, by = "Pitcher") %>%
|
| 213 |
+
left_join(probs, by = "row_id") %>%
|
| 214 |
+
select(-row_id)
|
| 215 |
+
|
| 216 |
+
#Determine if pitcher has reference data (pitched in college season)
|
| 217 |
+
has_arsenal <- !is.na(data$in_reference)
|
| 218 |
+
|
| 219 |
+
# mask only pitchers who are in the reference, everyone else predicts normally, meaning a pitcher not in the reference has no
|
| 220 |
+
#arsenal constraint and is just given the model prediction. everyone else moves to the next steps:
|
| 221 |
+
pitch_to_perc <- c(
|
| 222 |
+
.pred_Fastball = "perc_FF", .pred_Cutter = "perc_CT",
|
| 223 |
+
.pred_Sinker = "perc_SI", .pred_Slider = "perc_SL",
|
| 224 |
+
.pred_Sweeper = "perc_SW", .pred_Curveball = "perc_CU",
|
| 225 |
+
.pred_Changeup = "perc_CH", .pred_Splitter = "perc_SP"
|
| 226 |
+
)
|
| 227 |
+
for (pc in intersect(names(pitch_to_perc), names(data))) {
|
| 228 |
+
ac <- pitch_to_perc[[pc]]
|
| 229 |
+
mult <- ifelse(has_arsenal, as.numeric(coalesce(data[[ac]], 0) > 0), 1)
|
| 230 |
+
data[[pc]] <- data[[pc]] * mult
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
#Build a matrix of the (now masked) class probabilities
|
| 234 |
+
prob_mat <- as.matrix(data[pred_col_names])
|
| 235 |
+
|
| 236 |
+
#Flag rows that actually got scored, unscored rows stay all-NA and are skipped
|
| 237 |
+
predicted <- rowSums(!is.na(prob_mat)) > 0
|
| 238 |
+
|
| 239 |
+
#Start everyone as NA, only scored rows get a tag
|
| 240 |
+
pt <- rep(NA_character_, nrow(data))
|
| 241 |
+
|
| 242 |
+
#Pull just the scored rows, zero out NAs so max.col can run
|
| 243 |
+
m <- prob_mat[predicted, , drop = FALSE]
|
| 244 |
+
m[is.na(m)] <- 0
|
| 245 |
+
|
| 246 |
+
#Pick the highest surviving probability per row as the predicted pitch
|
| 247 |
+
pt[predicted] <- pred_col_names[max.col(m, ties.method = "first")]
|
| 248 |
+
|
| 249 |
+
#Strip the ".pred_" prefix to get a clean pitch type, drop the helper column
|
| 250 |
+
data$TaggedPitchType <- sub("^\\.pred_", "", pt)
|
| 251 |
+
data %>% select(-in_reference)
|
| 252 |
+
|
| 253 |
+
#make all splitters change ups
|
| 254 |
+
data <- data %>%
|
| 255 |
+
mutate(TaggedPitchType = ifelse(TaggedPitchType == "Splitter", "Changeup", TaggedPitchType))
|
| 256 |
+
|
| 257 |
+
return(data)
|
| 258 |
+
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
|
| 262 |
#Function that predicts stuffplus
|
| 263 |
predict_stuffplus <- function(data) {
|
| 264 |
|
|
|
|
| 381 |
TaggedPitchType == "Undefined" ~ "Other",
|
| 382 |
T ~ TaggedPitchType
|
| 383 |
))
|
| 384 |
+
|
| 385 |
+
data <- retag_pitches(data)
|
| 386 |
|
| 387 |
#Main aggregations
|
| 388 |
data <- data %>%
|