install.packages("plyr")
You also need the "mgcv" package for creating the model that is used in the calculations.
You can find the code below in blue, and comments in green. Hope it's readable:
#load library
library("plyr")
#initalize -- add necessary variables. Best way is with vectorized ifelse function.
pitcher$babipn = ifelse(pitcher$type=="X" & pitcher$event %in% c("Single", "Double", "Triple"), 1, 0)
pitcher$inplay = ifelse(pitcher$type=="X" & pitcher$event != "Home Run", 1, 0)
pitcher$swing = ifelse(pitcher$type=='X' | pitcher$des %in% c('Foul', 'Foul Tip', 'Foul (Runner Going)',
'Swinging Strike', 'Swinging Strike (Blocked)'), 1, 0)
pitcher$walk = ifelse(pitcher$des %in% c("Ball", "Ball In Dirt", "Intent Ball")
& pitcher$event %in% c("Intent Walk", "Walk") & pitcher$ball==3, 1, 0)
pitcher$strikeout = ifelse(pitcher$des %in% c("Called Strike", "Foul Tip", "Strike",
"Swinging Strike", "Swinging Strike (Blocked)")
& pitcher$event %in% c("Strikeout","Strikeout - DP") & pitcher$strike==2, 1, 0)
pitcher$homerun = ifelse(pitcher$des == "In play, run(s)" & pitcher$event == 'Home Run', 1, 0)
pitcher$full_name = paste(pitcher$first, pitcher$last)
#find number of plate appearances for each umpire, don't know why I named it atbats
#calculate kwERA, FIP, babip, and swing rate
pitcher = ddply(pitcher, .(ump_id), transform, atbats = length(unique(ab_id)))
pitcher = ddply(pitcher, .(ump_id), transform, fip = ((13 * sum(homerun)
+ 3 * sum(walk) - 1.93 * sum(strikeout)) / (.23 * atbats)) + 3.10,
kwERA = 5.24 - (12 * (sum(strikeout) - sum(walk)) / atbats),
babip = sum(babipn) / sum(inplay), swing = mean(swing))
#subset data to only look at pitches where umpire makes a call
#this won't affect the stats we just calculated
pitcher = subset(pitcher, des %in% c("Automatic Ball", "Ball","Ball In Dirt", "Called Strike",
"Intent Ball", "Pitchout", "Strike"))
#add in variable to describe called strike
pitcher$cs = ifelse(pitcher$des == 'Called Strike', 1, 0)
#find number of pitches seen by each umpire
pitcher = ddply(pitcher, .(ump_id), transform, n = length(px))
#only look at umpires with greater than 3000 pitches
pitcher = subset(pitcher, n > 3000)
#define function to calculate strikezone area
csarea = function(x) {require('mgcv')
#create model with mgcv package, use smoothing
model <- gam(cs ~ s(px) + s(pz), data = x, family = binomial(link='logit'))
mygrid <- expand.grid(x=seq(-4, 4, length=50), y=seq(-2, 6, length=50))
#find predicted called strike rates for a bunch of tiny bins
mygrid$z <- as.numeric(predict(model, data.frame(px=mygrid$x, pz=mygrid$y), type='response'))
thresh <- 0.5
#find number of tiny bins where predicted rate is at least .5
nbins=length(mygrid$z[mygrid$z >= thresh])
#multiply number of tiny bins by size of each bin
areas=nbins * 0.02665556
#add in fields to data.frame
x$area <-rep(areas, length(x[,1]))
x$nbins <- rep(nbins, length(x[,1]))
return(x)
}
#call + evaluate csarea() on each subset of umpire data
pitcher=ddply(pitcher, .(ump_id), "csarea")
#create a summary dataset for each umpire, then sort by area
sum.pitch=ddply(pitcher, .(ump_id), summarize, area=mean(area), full_name=full_name[1],
fip=mean(fip), kwERA=mean(kwERA), babip=mean(babip), swing=mean(swing))
sum.pitch=arrange(sum.pitch, area)