HyannisHarborHawks commited on
Commit
b79a5e4
·
verified ·
1 Parent(s): cf5d39c

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +286 -101
app.R CHANGED
@@ -32,59 +32,226 @@ hf_target_for <- function(source) {
32
  }
33
 
34
  # ============================================================
35
- # Model + run-value files (must be shipped in the Space)
36
  # ============================================================
37
- stuffplus_recipe <- readRDS("stuffplus_recipe.rds")
38
- stuffplus_model <- xgb.load("stuffplus_xgb.json")
39
-
40
- rv <- tryCatch(
41
- read.csv("non_context_run_values.csv", stringsAsFactors = FALSE),
42
- error = function(e) {
43
- message("non_context_run_values.csv not found - run values will be unavailable")
44
- NULL
 
 
 
 
 
 
 
 
 
45
  }
46
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  # ============================================================
49
- # Data pipeline (lifted verbatim from the report app)
50
  # ============================================================
51
- clean_college_data <- function(data, teams = NA){
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  data <- data %>%
54
- mutate(PlayResult = ifelse(PlayResult %in% c("HomeRun", "homerun"), "Homerun", PlayResult),
55
- Batter = sub("(.*),\\s*(.*)", "\\2 \\1", Batter),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  Pitcher = sub("(.*),\\s*(.*)", "\\2 \\1", Pitcher),
57
  Catcher = sub("(.*),\\s*(.*)", "\\2 \\1", Catcher))
58
-
 
59
  col <- colnames(data)
60
- if ("Top/Bottom" %in% col){
61
- data <- data %>% rename(`Top.Bottom` = `Top/Bottom`)
 
 
62
  }
63
-
 
64
  numeric_columns <- c("PitchNo", "PAofInning", "PitchofPA", "PitcherId", "BatterId", "Inning", "Outs", "Balls",
65
- "Strikes", "OutsOnPlay", "RunsScored", "RelSpeed", "VertRelAngle", "HorzRelAngle", "SpinRate",
66
- "SpinAxis", "RelHeight", "RelSide", "Extension", "VertBreak", "InducedVertBreak", "HorzBreak",
67
- "PlateLocHeight", "PlateLocSide", "ZoneSpeed", "VertApprAngle", "HorzApprAngle", "ZoneTime",
68
  "ExitSpeed", "Angle", "Direction", "HitSpinRate", "Distance", "Bearing", "HangTime",
69
- "LastTrackedDistance", "pfxx", "pfxz", "x0", "y0", "z0", "vx0", "vz0", "vy0", "ax0", "ay0",
70
  "az0", "EffectiveVelo", "MaxHeight", "SpeedDrop", "ContactPositionX", "ContactPositionY",
71
  "ContactPositionZ", "HomeTeamForeignID", "AwayTeamForeignID", "CatcherId", "ThrowSpeed",
72
- "PopTime", "ExchangeTime", "TimeToBase")
73
-
74
- data <- data %>%
75
- mutate(across(any_of(numeric_columns), as.numeric),
76
- PlateLocHeight = if ("PlateLocHeight" %in% names(.)) 12 * PlateLocHeight else PlateLocHeight,
77
- PlateLocSide = if ("PlateLocSide" %in% names(.)) 12 * PlateLocSide else PlateLocSide)
78
-
79
- data <- data %>%
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  mutate(TaggedPitchType = case_when(
81
- TaggedPitchType == "FourSeamFastBall" ~ "Fastball",
82
- TaggedPitchType %in% c("TwoSeamFastBall", "OneSeamFastBall") ~ "Sinker",
83
- TaggedPitchType == "ChangeUp" ~ "Changeup",
84
- TaggedPitchType == "Undefined" ~ "Other",
85
  T ~ TaggedPitchType
86
  ))
87
-
 
88
  data <- data %>%
89
  mutate(
90
  is_csw = case_when(
@@ -92,7 +259,7 @@ clean_college_data <- function(data, teams = NA){
92
  TRUE ~ 0
93
  ),
94
  is_swing = case_when(
95
- PitchCall %in% c("StrikeSwinging", "FoulBallNotFieldable", "InPlay",
96
  "FoulBallFieldable", "FoulBall") ~ 1,
97
  TRUE ~ 0
98
  ),
@@ -182,98 +349,116 @@ clean_college_data <- function(data, teams = NA){
182
  ),
183
  is_k = case_when(
184
  is_at_bat == 1 & KorBB == "Strikeout" ~ 1,
185
- is_at_bat == 1 & KorBB != "Strikeout" ~ 0,
186
  PlayResult %in% c("StrikeoutSwinging", "StrikeoutLooking") ~ 1,
187
  TRUE ~ NA_real_
188
- ),
189
  is_put_away = case_when(
190
  Strikes == 2 & KorBB == "Strikeout" ~ 1,
191
  Strikes == 2 & KorBB != "Strikeout" ~ 0,
192
  Strikes == 2 & PlayResult %in% c("StrikeoutSwinging", "StrikeoutLooking") ~ 1,
193
  TRUE ~ NA_real_
194
- ),
195
  OutsOnPlay = ifelse(KorBB == "Strikeout" | PlayResult %in% c("StrikeoutSwinging", "StrikeoutLooking"), OutsOnPlay + 1, OutsOnPlay)
196
  )
197
-
198
- data <- data %>%
199
  mutate(event_type = case_when(
200
- PitchCall %in% c("StrikeSwinging", "StrkeSwinging") ~ "Whiff",
201
- PitchCall %in% c("StriekC", "StrikeCalled") ~ "Called Strike",
202
  PitchCall %in% c("FoulBallFieldable", "FoulBall", "FoulBallNotFieldable",
203
- "FouldBallNotFieldable") ~ "Foul Ball",
204
- PitchCall %in% c("BallCalled", "BallinDirt", "BallIntentional", "BalIntentional") ~ "Ball",
205
- PitchCall == "HitByPitch" ~ "HBP",
206
  PitchCall == "InPlay" & PlayResult %in% c("Out", "FieldersChoice",
207
- "Error", "error",
208
- "Sacrifice") ~ "Field Out",
209
- PitchCall == "InPlay" & PlayResult == "Single" ~ "Single",
210
  PitchCall == "InPlay" & PlayResult == "Double" ~ "Double",
211
  PitchCall == "InPlay" & PlayResult == "Triple" ~ "Triple",
212
- PitchCall == "InPlay" & PlayResult == "Homerun" ~ "Home Run",
213
  T ~ NA
214
  ))
215
-
216
- if (!is.null(rv)) data <- data %>% left_join(rv, by = "event_type")
217
-
 
 
 
 
218
  data <- data %>%
219
  dplyr::select(
220
- -any_of(c("PitchLastMeasuredX", "PitchLastMeasuredY", "PitchLastMeasuredZ",
221
- "HitSpinAxis",
222
- "PitchReleaseConfidence", "PitchLocationConfidence", "PitchMovementConfidence",
223
- "HitLaunchConfidence", "HitLandingConfidence",
224
- "CatcherThrowCatchConfidence", "CatcherThrowReleaseConfidence", "CatcherThrowLocationConfidence",
225
- "PositionAt110X", "PositionAt110Y", "PositionAt110Z")),
 
 
226
  -starts_with("PitchTrajectory"),
227
  -starts_with("HitTrajectory")
228
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
- return(data)
231
- }
232
-
233
- predict_stuffplus <- function(data) {
234
-
235
- predict_data <- data %>%
236
- mutate(RelSide = case_when(
237
- PitcherThrows == "Right" ~ RelSide,
238
- PitcherThrows == "Left" ~ -RelSide,
239
- PitcherThrows %in% c("Both", "Undefined") & RelSide > 0 ~ RelSide,
240
- PitcherThrows %in% c("Both", "Undefined") & RelSide < 0 ~ -RelSide),
241
- ax0 = case_when(
242
- PitcherThrows == "Right" ~ ax0,
243
- PitcherThrows == "Left" ~ -ax0,
244
- PitcherThrows %in% c("Both", "Undefined") & ax0 > 0 ~ ax0,
245
- PitcherThrows %in% c("Both", "Undefined") & ax0 < 0 ~ -ax0),
246
- PlateLocHeight = PlateLocHeight*12,
247
- PlateLocSide = PlateLocSide*12,
248
- ax0 = -ax0) %>%
249
- group_by(Pitcher, GameID) %>%
250
- mutate(
251
- primary_pitch = case_when(
252
- any(TaggedPitchType == "Fastball") ~ "Fastball",
253
- any(TaggedPitchType == "Sinker") ~ "Sinker",
254
- TRUE ~ names(sort(table(TaggedPitchType), decreasing = TRUE))[1]
255
- )
256
- ) %>%
257
- group_by(Pitcher, GameID, primary_pitch) %>%
258
- mutate(
259
- primary_az0 = mean(az0[TaggedPitchType == primary_pitch], na.rm = TRUE),
260
- primary_velo = mean(RelSpeed[TaggedPitchType == primary_pitch], na.rm = TRUE)
261
- ) %>%
262
- ungroup() %>%
263
- mutate(az0_diff = az0 - primary_az0,
264
- velo_diff = RelSpeed - primary_velo)
265
-
266
- df_processed <- bake(stuffplus_recipe, new_data = predict_data)
267
- df_matrix <- as.matrix(df_processed)
268
- raw_stuff <- predict(stuffplus_model, df_matrix)
269
-
270
- data$raw_stuff <- raw_stuff
271
  data <- data %>%
272
- mutate(stuff_plus = ((raw_stuff - 0.004424894) / 0.01010482) * 10 + 100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
274
  return(data)
 
 
275
  }
276
 
 
277
  # ============================================================
278
  # CSS
279
  # ============================================================
 
32
  }
33
 
34
  # ============================================================
35
+ # READ IN PRIVATE DATASETS AND MODELS FOR CLEANING -- All in a private dataset on the main (HyannisHarborHawksCCBL) Huggingface
36
  # ============================================================
37
+
38
+ download_private_parquet <- function(repo_id, filename, max_retries = 3) {
39
+ url <- paste0("https://huggingface.co/datasets/", repo_id, "/resolve/main/", filename)
40
+ api_key <- Sys.getenv("HF_READ_TOKEN")
41
+ if (api_key == "") stop("API key is not set.")
42
+ for (attempt in 1:max_retries) {
43
+ tryCatch({
44
+ response <- GET(url, add_headers(Authorization = paste("Bearer", api_key)), timeout(60))
45
+ if (status_code(response) == 200) {
46
+ tmp <- tempfile(fileext = ".parquet")
47
+ writeBin(content(response, "raw"), tmp)
48
+ return(read_parquet(tmp))
49
+ } else warning(paste("Attempt", attempt, "failed with status:", status_code(response)))
50
+ }, error = function(e) {
51
+ warning(paste("Attempt", attempt, "failed:", e$message))
52
+ if (attempt < max_retries) Sys.sleep(2)
53
+ })
54
  }
55
+ stop(paste("Failed to download dataset after", max_retries, "attempts"))
56
+ }
57
+
58
+ download_private_csv <- function(repo_id, filename, max_retries = 3) {
59
+ url <- paste0("https://huggingface.co/datasets/", repo_id, "/resolve/main/", filename)
60
+ api_key <- Sys.getenv("HF_READ_TOKEN")
61
+ if (api_key == "") stop("API key is not set.")
62
+ for (attempt in 1:max_retries) {
63
+ tryCatch({
64
+ response <- GET(url, add_headers(Authorization = paste("Bearer", api_key)), timeout(60))
65
+ if (status_code(response) == 200) {
66
+ tmp <- tempfile(fileext = ".csv")
67
+ writeBin(content(response, "raw"), tmp)
68
+ return(readr::read_csv(tmp, show_col_types = FALSE))
69
+ } else warning(paste("Attempt", attempt, "failed with status:", status_code(response)))
70
+ }, error = function(e) {
71
+ warning(paste("Attempt", attempt, "failed:", e$message))
72
+ if (attempt < max_retries) Sys.sleep(2)
73
+ })
74
+ }
75
+ stop(paste("Failed to download dataset after", max_retries, "attempts"))
76
+ }
77
+
78
+ download_private_xgb <- function(repo_id, filename, max_retries = 3) {
79
+ url <- paste0("https://huggingface.co/datasets/", repo_id, "/resolve/main/", filename)
80
+ api_key <- Sys.getenv("HF_READ_TOKEN")
81
+ if (api_key == "") stop("API key is not set.")
82
+ for (attempt in 1:max_retries) {
83
+ tryCatch({
84
+ response <- GET(url, add_headers(Authorization = paste("Bearer", api_key)), timeout(60))
85
+ if (status_code(response) == 200) {
86
+ tmp <- tempfile(fileext = ".json")
87
+ writeBin(content(response, "raw"), tmp)
88
+ return(xgb.load(tmp))
89
+ } else warning(paste("Attempt", attempt, "failed with status:", status_code(response)))
90
+ }, error = function(e) {
91
+ warning(paste("Attempt", attempt, "failed:", e$message))
92
+ if (attempt < max_retries) Sys.sleep(2)
93
+ })
94
+ }
95
+ stop(paste("Failed to download dataset after", max_retries, "attempts"))
96
+ }
97
+
98
+ download_private_rds <- function(repo_id, filename, max_retries = 3) {
99
+ url <- paste0("https://huggingface.co/datasets/", repo_id, "/resolve/main/", filename)
100
+ api_key <- Sys.getenv("HF_READ_TOKEN")
101
+ if (api_key == "") stop("API key is not set.")
102
+ for (attempt in 1:max_retries) {
103
+ tryCatch({
104
+ response <- GET(url, add_headers(Authorization = paste("Bearer", api_key)), timeout(60))
105
+ if (status_code(response) == 200) {
106
+ tmp <- tempfile(fileext = ".rds")
107
+ writeBin(content(response, "raw"), tmp)
108
+ return(readRDS(tmp))
109
+ } else warning(paste("Attempt", attempt, "failed:", e$message))
110
+ }, error = function(e) {
111
+ warning(paste("Attempt", attempt, "failed:", e$message))
112
+ if (attempt < max_retries) Sys.sleep(2)
113
+ })
114
+ }
115
+ stop(paste("Failed to download dataset after", max_retries, "attempts"))
116
+ }
117
+
118
+ #Stuff Model (XGBoost)
119
+ stuffplus_recipe <- download_private_rds("HyannisHarborHawksCCBL/CleanDataObjects", "stuffplus_recipe.rds")
120
+ stuffplus_model <- download_private_xgb("HyannisHarborHawksCCBL/CleanDataObjects", "stuffplus_xgb.json")
121
+
122
+ #xwOBA model (KNN)
123
+ xwoba_model <- unbundle(download_private_rds("HyannisHarborHawksCCBL/CleanDataObjects", "bundle_college_xwoba_knn.rds"))
124
+
125
+ #Data to join on (non-context run values, conferences, GUTS!, longer team names)
126
+ rv <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "non_context_run_values.csv")
127
+ team_leagues <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_team_leagues.csv")
128
+ college_guts <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_GUTS.csv")
129
+ college_join <- download_private_csv("HyannisHarborHawksCCBL/CleanDataObjects", "college_join.csv")
130
+
131
+ #Team logo
132
+ logo_b64 <- base64enc::base64encode("HHLogo.png")
133
+ logo_uri <- paste0("data:image/png;base64,", logo_b64)
134
 
135
  # ============================================================
136
+ #Clean data
137
  # ============================================================
 
138
 
139
+ predict_stuffplus <- function(data) {
140
+
141
+ predict_data <- data %>%
142
+ mutate(RelSide = case_when(
143
+ PitcherThrows == "Right" ~ RelSide,
144
+ PitcherThrows == "Left" ~ -RelSide,
145
+ PitcherThrows %in% c("Both", "Undefined") & RelSide > 0 ~ RelSide,
146
+ PitcherThrows %in% c("Both", "Undefined") & RelSide < 0 ~ -RelSide),
147
+ ax0 = case_when(
148
+ PitcherThrows == "Right" ~ ax0,
149
+ PitcherThrows == "Left" ~ -ax0,
150
+ PitcherThrows %in% c("Both", "Undefined") & ax0 > 0 ~ ax0,
151
+ PitcherThrows %in% c("Both", "Undefined") & ax0 < 0 ~ -ax0),
152
+ ax0 = -ax0) %>%
153
+ group_by(Pitcher, GameID) %>%
154
+ mutate(
155
+ primary_pitch = case_when(
156
+ any(TaggedPitchType == "Fastball") ~ "Fastball",
157
+ any(TaggedPitchType == "Sinker") ~ "Sinker",
158
+ TRUE ~ names(sort(table(TaggedPitchType), decreasing = TRUE))[1]
159
+ )
160
+ ) %>%
161
+ ungroup() %>%
162
+ group_by(Pitcher, GameID, primary_pitch) %>%
163
+ mutate(
164
+ primary_az0 = mean(az0[TaggedPitchType == primary_pitch], na.rm = TRUE),
165
+ primary_velo = mean(RelSpeed[TaggedPitchType == primary_pitch], na.rm = TRUE)
166
+ ) %>%
167
+ ungroup() %>%
168
+ mutate(az0_diff = az0 - primary_az0,
169
+ velo_diff = RelSpeed - primary_velo)
170
+
171
+
172
+ df_processed <- bake(stuffplus_recipe, new_data = predict_data)
173
+
174
+ df_matrix <- as.matrix(df_processed)
175
+
176
+ raw_stuff <- predict(stuffplus_model, df_matrix)
177
+
178
+ data$raw_stuff <- raw_stuff
179
+
180
+
181
  data <- data %>%
182
+ mutate(stuff_plus = ((raw_stuff - 0.004424894) / 0.01010482) * 10 + 100)
183
+
184
+ return(data)
185
+
186
+ }
187
+
188
+ #===================================================================================================
189
+ #Clean College Data Function (includes predicting stuffplus)
190
+
191
+
192
+ clean_college_data <- function(data, teams = NA) {
193
+
194
+
195
+ data <- data %>%
196
+ mutate(PlayResult = ifelse(PlayResult %in% c("HomeRun", "homerun"), "Homerun", PlayResult),
197
+ Batter = sub("(.*),\\s*(.*)", "\\2 \\1", Batter),
198
  Pitcher = sub("(.*),\\s*(.*)", "\\2 \\1", Pitcher),
199
  Catcher = sub("(.*),\\s*(.*)", "\\2 \\1", Catcher))
200
+
201
+
202
  col <- colnames(data)
203
+
204
+ if ("Top/Bottom" %in% col){
205
+ data <- data %>%
206
+ rename(`Top.Bottom` = `Top/Bottom`)
207
  }
208
+
209
+ #Fix column types
210
  numeric_columns <- c("PitchNo", "PAofInning", "PitchofPA", "PitcherId", "BatterId", "Inning", "Outs", "Balls",
211
+ "Strikes", "OutsOnPlay", "RunsScored", "RelSpeed", "VertRelAngle", "HorzRelAngle", "SpinRate",
212
+ "SpinAxis", "RelHeight", "RelSide", "Extension", "VertBreak", "InducedVertBreak", "HorzBreak",
213
+ "PlateLocHeight", "PlateLocSide", "ZoneSpeed", "VertApprAngle", "HorzApprAngle", "ZoneTime",
214
  "ExitSpeed", "Angle", "Direction", "HitSpinRate", "Distance", "Bearing", "HangTime",
215
+ "LastTrackedDistance", "pfxx", "pfxz", "x0", "y0", "z0", "vx0", "vz0", "vy0", "ax0", "ay0",
216
  "az0", "EffectiveVelo", "MaxHeight", "SpeedDrop", "ContactPositionX", "ContactPositionY",
217
  "ContactPositionZ", "HomeTeamForeignID", "AwayTeamForeignID", "CatcherId", "ThrowSpeed",
218
+ "PopTime", "ExchangeTime", "TimeToBase", "OutsOnPlay")
219
+ data <- data %>%
220
+ mutate(across(any_of(numeric_columns), ~suppressWarnings(as.numeric(.x))))
221
+
222
+
223
+ #Correct for last, first
224
+ data <- data %>%
225
+ mutate(
226
+ Batter = ifelse(str_detect(Batter, ","),
227
+ str_trim(sub("^([^,]*),\\s*(.*)$", "\\2 \\1", Batter)),
228
+ Batter),
229
+ Pitcher = ifelse(str_detect(Pitcher, ","),
230
+ str_trim(sub("^([^,]*),\\s*(.*)$", "\\2 \\1", Pitcher)),
231
+ Pitcher)
232
+ )
233
+
234
+ #Fix plate locations where unit is feet and not inches
235
+ data <- data %>%
236
+ group_by(GameID) %>%
237
+ mutate(
238
+ PlateLocHeight = PlateLocHeight * if (isTRUE(sd(PlateLocHeight, na.rm = TRUE) < 5)) 12 else 1,
239
+ PlateLocSide = PlateLocSide * if (isTRUE(sd(PlateLocSide, na.rm = TRUE) < 5)) 12 else 1
240
+ ) %>%
241
+ ungroup()
242
+
243
+
244
+ #fix pitch tagging
245
+ data <- data %>%
246
  mutate(TaggedPitchType = case_when(
247
+ TaggedPitchType == "FourSeamFastBall" ~ "Fastball",
248
+ TaggedPitchType %in% c("TwoSeamFastBall", "OneSeamFastBall") ~ "Sinker",
249
+ TaggedPitchType == "ChangeUp" ~ "Changeup",
250
+ TaggedPitchType == "Undefined" ~ "Other",
251
  T ~ TaggedPitchType
252
  ))
253
+
254
+ #Main aggregations
255
  data <- data %>%
256
  mutate(
257
  is_csw = case_when(
 
259
  TRUE ~ 0
260
  ),
261
  is_swing = case_when(
262
+ PitchCall %in% c("StrikeSwinging", "FoulBallNotFieldable", "InPlay",
263
  "FoulBallFieldable", "FoulBall") ~ 1,
264
  TRUE ~ 0
265
  ),
 
349
  ),
350
  is_k = case_when(
351
  is_at_bat == 1 & KorBB == "Strikeout" ~ 1,
352
+ is_at_bat == 1 & KorBB != "Strikeout" ~ 0,
353
  PlayResult %in% c("StrikeoutSwinging", "StrikeoutLooking") ~ 1,
354
  TRUE ~ NA_real_
355
+ ),
356
  is_put_away = case_when(
357
  Strikes == 2 & KorBB == "Strikeout" ~ 1,
358
  Strikes == 2 & KorBB != "Strikeout" ~ 0,
359
  Strikes == 2 & PlayResult %in% c("StrikeoutSwinging", "StrikeoutLooking") ~ 1,
360
  TRUE ~ NA_real_
361
+ ),
362
  OutsOnPlay = ifelse(KorBB == "Strikeout" | PlayResult %in% c("StrikeoutSwinging", "StrikeoutLooking"), OutsOnPlay + 1, OutsOnPlay)
363
  )
364
+
365
+ data <- data %>%
366
  mutate(event_type = case_when(
367
+ PitchCall %in% c("StrikeSwinging", "StrkeSwinging") ~ "Whiff",
368
+ PitchCall %in% c("StriekC", "StrikeCalled") ~ "Called Strike",
369
  PitchCall %in% c("FoulBallFieldable", "FoulBall", "FoulBallNotFieldable",
370
+ "FouldBallNotFieldable") ~ "Foul Ball",
371
+ PitchCall %in% c("BallCalled", "BallinDirt", "BallIntentional", "BalIntentional") ~ "Ball",
372
+ PitchCall == "HitByPitch" ~ "HBP",
373
  PitchCall == "InPlay" & PlayResult %in% c("Out", "FieldersChoice",
374
+ "Error", "error",
375
+ "Sacrifice") ~ "Field Out",
376
+ PitchCall == "InPlay" & PlayResult == "Single" ~ "Single",
377
  PitchCall == "InPlay" & PlayResult == "Double" ~ "Double",
378
  PitchCall == "InPlay" & PlayResult == "Triple" ~ "Triple",
379
+ PitchCall == "InPlay" & PlayResult == "Homerun" ~ "Home Run",
380
  T ~ NA
381
  ))
382
+
383
+
384
+ if (!"mean_DRE" %in% names(data)) {
385
+ data <- data %>%
386
+ left_join(rv, by = "event_type")
387
+ }
388
+
389
  data <- data %>%
390
  dplyr::select(
391
+ -any_of(c(
392
+ "PitchLastMeasuredX", "PitchLastMeasuredY", "PitchLastMeasuredZ",
393
+ "HitSpinAxis",
394
+ "PitchReleaseConfidence", "PitchLocationConfidence", "PitchMovementConfidence",
395
+ "HitLaunchConfidence", "HitLandingConfidence",
396
+ "CatcherThrowCatchConfidence", "CatcherThrowReleaseConfidence", "CatcherThrowLocationConfidence",
397
+ "PositionAt110X", "PositionAt110Y", "PositionAt110Z"
398
+ )),
399
  -starts_with("PitchTrajectory"),
400
  -starts_with("HitTrajectory")
401
  )
402
+
403
+ data <- data %>%
404
+ dplyr::select(-any_of(c("HomeLeague", "AwayLeague", "PitcherLeague", "BatterLeague"))) %>%
405
+ left_join(team_leagues, by = "HomeTeam") %>%
406
+ left_join(team_leagues %>% rename(AwayLeague = HomeLeague, AwayTeam = HomeTeam), by = "AwayTeam") %>%
407
+ mutate(PitcherLeague = ifelse(`Top.Bottom` == "Top", HomeLeague, AwayLeague),
408
+ BatterLeague = ifelse(`Top.Bottom` == "Top", AwayLeague, HomeLeague),
409
+ HomeLeague = replace_na(HomeLeague, "Other"),
410
+ AwayLeague = replace_na(AwayLeague, "Other"),
411
+ PitcherLeague = replace_na(PitcherLeague, "Other"),
412
+ BatterLeague = replace_na(BatterLeague, "Other")
413
+ )
414
+
415
 
416
+
417
+ if (!"stuff_plus" %in% names(data)) {
418
+ data <- predict_stuffplus(data)
419
+ }
420
+
421
+ data <- data %>%
422
+ filter(!(Level %in% c("D2", "JUCO", "NAIA", "D3")))
423
+
424
+ #predict xwoba
425
+ data <- data %>% mutate(row_id = row_number())
426
+
427
+ prob_cols <- c("prob_0", "prob_1", "prob_2", "prob_3", "prob_4")
428
+
429
+ complete <- data %>%
430
+ filter(!is.na(ExitSpeed) & !is.na(Angle)) %>%
431
+ dplyr::select(-any_of(prob_cols))
432
+
433
+ xwoba_preds <- predict(xwoba_model, complete, type = "prob")
434
+ colnames(xwoba_preds) <- prob_cols
435
+
436
+ complete <- complete %>% bind_cols(xwoba_preds)
437
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  data <- data %>%
439
+ dplyr::select(-any_of(prob_cols)) %>%
440
+ left_join(complete %>% dplyr::select(row_id, any_of(prob_cols)), by = "row_id") %>%
441
+ dplyr::select(-row_id)
442
+
443
+ data <- data %>%
444
+ dplyr::select(-any_of(c("w1B", "w2B", "w3B", "wHR"))) %>%
445
+ cross_join(college_guts %>% dplyr::select(w1B, w2B, w3B, wHR)) %>%
446
+ mutate(xwOBA = w1B*prob_1 + w2B*prob_2 + w3B*prob_3 + wHR*prob_4 + 0*prob_0,
447
+ xwOBA = case_when(
448
+ KorBB == "Strikeout" ~ 0,
449
+ KorBB == "Walk" ~ .82,
450
+ event_type == "HBP" ~ .85,
451
+ TRUE ~ xwOBA
452
+ ))
453
+
454
+ message(nrow(data), " rows cleaned.")
455
 
456
  return(data)
457
+
458
+
459
  }
460
 
461
+
462
  # ============================================================
463
  # CSS
464
  # ============================================================