Skip to content

Commit e2794d4

Browse files
committed
added plotFielding, MAJOR speed up to fielding model
1 parent 2853723 commit e2794d4

11 files changed

+171
-121
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: openWAR
22
Type: Package
33
Title: Machinery for analyzing baseball data and computing WAR
4-
Version: 0.2.2.9000
4+
Version: 0.2.2.9001
55
Date: 2013-07-31
66
Authors@R: c(
77
person("Ben", "Baumer", email = "[email protected]", role = c("aut", "cre")),

NAMESPACE

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Generated by roxygen2 (4.1.1): do not edit by hand
22

3+
S3method(getModelFieldingCollective,GameDayPlays)
4+
S3method(getModelFieldingCollective,default)
35
S3method(getModelRunExpectancy,GameDayPlays)
46
S3method(getModelRunExpectancy,default)
57
S3method(getModels,GameDayPlays)
@@ -16,6 +18,12 @@ S3method(plot,GameDayPlays)
1618
S3method(plot,do.openWARPlayers)
1719
S3method(plot,gameday)
1820
S3method(plot,openWARPlayers)
21+
S3method(plotFielding,bkde2D)
22+
S3method(plotFielding,default)
23+
S3method(plotFielding,formula)
24+
S3method(plotFielding,glm)
25+
S3method(plotFielding,lm)
26+
S3method(predict,bkde2D)
1927
S3method(shakeWAR,GameDayPlays)
2028
S3method(shakeWAR,list)
2129
S3method(shakeWAR,openWARPlays)
@@ -43,7 +51,9 @@ export(makeWAR)
4351
export(makeWARBaserunning)
4452
export(makeWARre24)
4553
export(panel.baseball)
54+
export(panel.fielding)
4655
export(panel.war)
56+
export(plotFielding)
4757
export(readData.gameday)
4858
export(shakeWAR)
4959
export(tabulate)

R/getModels.R

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#' @title getModels
2-
#' @aliases getModels.GameDayPlays
32
#'
4-
#' @description Retrieve various models trained on GameDayPlays data
3+
#' @description Retrieve various models trained on \code{\link{GameDayPlays}} data
54
#'
65
#' @details This function will retrieve various models based on the MLBAM data
76
#' set and the openWAR framework. Currently this only returns the Run Expectancy Model.
87
#'
9-
#' @param data a GameDayPlays dataset
8+
#' @param data a \code{\link{GameDayPlays}} dataset
109
#' @param ... currently ignored
1110
#'
12-
#' @return A list of model objects
11+
#' @return A \code{\link{list}} of model objects
1312
#'
14-
#' @export getModels
13+
#' @export
1514
#' @examples
1615
#'
1716
#' data(May)
@@ -35,13 +34,12 @@ getModels.GameDayPlays = function(data, ...) {
3534
}
3635

3736
#' @title getModelRunExpectancy
38-
#' @aliases getModelRunExpectancy
3937
#'
4038
#' @description Build the Run Expectancy Model
4139
#'
42-
#' @details This function will build the Run Expectancy Model used in \code{openWAR}.
40+
#' @details This function will build the Run Expectancy Model used in \code{\link{openWAR}}.
4341
#'
44-
#' @param data a GameDayPlays dataset
42+
#' @param data a \code{\link{GameDayPlays}} dataset
4543
#' @param mod.re an existing Run Expectancy Model
4644
#' @param verbose print messages to screen during operation?
4745
#' @param drop.incomplete a LOGICAL indicating whether incomplete innings (e.g. walk-off innings)
@@ -197,7 +195,7 @@ getModelFieldingRF = function(data) {
197195
#' @details Computes a 2D kernel smoothed estimate of the probability that *any* of the 9 fielders
198196
#' will make a play on a ball in play
199197
#'
200-
#' @param data An MLBAM data.frame
198+
#' @param data A \code{\link{GameDayPlays}} object
201199
#'
202200
#' @return a vector representing the probability that each ball in play will be fielded
203201
#'
@@ -206,36 +204,56 @@ getModelFieldingRF = function(data) {
206204
#' @importFrom KernSmooth bkde2D
207205
#' @importFrom Hmisc whichClosest
208206
#'
207+
#' @examples
208+
#'
209+
#' fmod <- getModelFieldingCollective(May)
210+
#' plotFielding(fmod)
209211
#'
210212

211-
getModelFieldingCollective = function(data) {
213+
getModelFieldingCollective = function(data) { UseMethod("getModelFieldingCollective"); }
214+
215+
#' @export
216+
#' @rdname getModelFieldingCollective
217+
#' @method getModelFieldingCollective GameDayPlays
218+
219+
getModelFieldingCollective.GameDayPlays = function(data) {
220+
getModelFieldingCollective(filter_(data, ~isBIP == TRUE))
221+
}
222+
223+
#' @export
224+
#' @rdname getModelFieldingCollective
225+
#' @method getModelFieldingCollective default
226+
#'
227+
getModelFieldingCollective.default = function(data) {
212228
message("....Computing the collective fielding model...")
213229
data = dplyr::mutate_(data, wasFielded = ~!is.na(fielderId))
214230
outs = dplyr::select_(dplyr::filter_(data, ~wasFielded == TRUE), ~our.x, ~our.y)
215231
hits = dplyr::select_(dplyr::filter_(data, ~wasFielded == FALSE), ~our.x, ~our.y)
216232
# Find 2D kernel density estimates for hits and outs Make sure to specify the range, so that they over estimated over the
217233
# same grid
218234
grid = list(range(data$our.x, na.rm = TRUE), range(data$our.y, na.rm = TRUE))
219-
fit.out <- KernSmooth::bkde2D(outs, bandwidth = c(10, 10), range.x = grid)
220-
fit.hit <- KernSmooth::bkde2D(hits, bandwidth = c(10, 10), range.x = grid)
221-
222-
field.smooth = data.frame(cbind(expand.grid(fit.out$x1, fit.out$x2), isOut = as.vector(fit.out$fhat)), isHit = as.vector(fit.hit$fhat))
223-
names(field.smooth)[1:2] = c("x", "y")
224-
# Plot the surfaces wireframe(isOut ~ x + y, data=field.smooth, scales = list(arrows = FALSE), drape = TRUE, colorkey =
225-
# TRUE) wireframe(isHit ~ x + y, data=field.smooth, scales = list(arrows = FALSE), drape = TRUE, colorkey = TRUE)
235+
fit.out <- KernSmooth::bkde2D(as.matrix(outs), bandwidth = c(10, 10), range.x = grid)
236+
fit.hit <- KernSmooth::bkde2D(as.matrix(hits), bandwidth = c(10, 10), range.x = grid)
237+
class(fit.out) <- union("bkde2D", class(fit.out))
238+
class(fit.hit) <- union("bkde2D", class(fit.hit))
226239

227-
# Make sure to add a small amount to avoid division by zero
228-
field.smooth = dplyr::mutate_(field.smooth, wasFielded = ~(isOut/(isOut + isHit + 1e-08)))
229-
# summary(field.smooth) fieldingplot(wasFielded ~ x + y, data=field.smooth, label = 'cum_resp', write.pdf=TRUE)
230-
231-
fit.all = function(x, y) {
232-
x.idx = Hmisc::whichClosest(field.smooth$x, x)
233-
y.idx = Hmisc::whichClosest(field.smooth$y, y)
234-
match = dplyr::filter_(field.smooth, ~x == field.smooth$x[x.idx] & y == field.smooth$y[y.idx])
235-
return(match$wasFielded)
236-
}
237-
238-
message("....Applying the collective fielding model...")
239-
resp.field = mapply(fit.all, data$our.x, data$our.y)
240-
return(resp.field)
241-
}
240+
fmod <- fit.out
241+
fmod$fhat <- fit.out$fhat / (fit.out$fhat + fit.hit$fhat + 1e-08)
242+
return(fmod)
243+
}
244+
245+
#' @export
246+
247+
predict.bkde2D <- function(object, ...) {
248+
dots <- list(...)
249+
newdata <- dots$newdata
250+
if (ncol(newdata) < 2) {
251+
stop("newdata must have at least two columns")
252+
}
253+
# find the indices that match closest
254+
x.idx = Hmisc::whichClosest(object$x1, as.numeric(as.data.frame(newdata)[,1]))
255+
y.idx = Hmisc::whichClosest(object$x2, as.numeric(as.data.frame(newdata)[,2]))
256+
zHat <- apply(data.frame(x.idx, y.idx), MARGIN = 1, function(x) { object$fhat[x[1], x[2]]; })
257+
return(zHat)
258+
}
259+

R/makeWAR.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ makeWARFielding = function(data, ...) {
233233
message("...Estimating Fielding Runs Above Average...")
234234

235235
# Compute the collective responsibility of all fielders
236-
p.hat = getModelFieldingCollective(data[, c("fielderId", "our.x", "our.y")])
236+
fmod = getModelFieldingCollective(data[, c("fielderId", "our.x", "our.y")])
237+
message("....Applying the collective fielding model...")
238+
p.hat <- predict(fmod, newdata = select_(data, ~our.x, ~our.y))
237239
# Step 2a: Define \delta.field for the defense, collectively
238240
delta.field = data$delta * p.hat
239241

R/fieldingplot.R renamed to R/plotFielding.R

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#' @title fieldingplot
1+
#' @title plotFielding
22
#'
33
#' @description Creates a plot of the fielding model for an individual player
44
#'
@@ -8,7 +8,8 @@
88
#' paper. However, this function is not exported, since it is not really accessible
99
#' to end users. We hope to add this functionality in a future release.
1010
#'
11-
#' @param x a model object, currently \code{\link{lm}} and \code{\link{glm}} are
11+
#' @param x a model object, currently \code{\link{lm}}, \code{\link{glm}}, and
12+
#' \code{\link{bkde2D}} are
1213
#' supported. For the default method this is an x-coordinate to be passed to
1314
#' \code{\link{contourplot}}.
1415
#' @param y y-coordinate for default method passed to \code{\link{contourplot}}.
@@ -23,6 +24,7 @@
2324
#' @importFrom RColorBrewer brewer.pal
2425
#' @importFrom mosaic lhs
2526
#'
27+
#' @export
2628
#' @return A contourplot object
2729
#'
2830
#' @examples
@@ -31,21 +33,23 @@
3133
#' library(dplyr)
3234
#' BIP <- filter(Mayplus, isBIP == TRUE)
3335
#' \dontrun{
34-
#' fielding = makeWARFielding(BIP)
36+
#' fielding <- makeWARFielding(BIP)
3537
#' }
3638
#'
3739

38-
fieldingplot = function(x, data, ...) UseMethod("fieldingplot")
40+
plotFielding = function(x, ...) UseMethod("plotFielding")
3941

40-
#' @rdname fieldingplot
42+
#' @export
43+
#' @rdname plotFielding
4144

42-
fieldingplot.glm = function(x, data, ...) {
43-
fieldingplot.lm(x, data, ...)
45+
plotFielding.glm = function(x, ...) {
46+
NextMethod()
4447
}
4548

46-
#' @rdname fieldingplot
49+
#' @export
50+
#' @rdname plotFielding
4751

48-
fieldingplot.lm = function(x, data, ...) {
52+
plotFielding.lm = function(x, ...) {
4953
model = x
5054
# make sure that the model object passed has a predict() method
5155
if (sum(paste("predict.", class(model), sep = "") %in% methods(predict)) == 0) {
@@ -59,12 +63,13 @@ fieldingplot.lm = function(x, data, ...) {
5963

6064
label = gsub("[^A-Z]", "", paste(lhs(terms(model)), collapse = ""))
6165

62-
fieldingplot.formula(z.hat ~ our.x + our.y, data = my.grid, label = label, ...)
66+
plotFielding.formula(z.hat ~ our.x + our.y, data = my.grid, label = label, ...)
6367
}
6468

65-
#' @rdname fieldingplot
69+
#' @export
70+
#' @rdname plotFielding
6671

67-
fieldingplot.formula = function(x, data, label = "label", write.pdf = FALSE, ...) {
72+
plotFielding.formula = function(x, label = "label", write.pdf = FALSE, ...) {
6873

6974
if (write.pdf) {
7075
filename = paste("fielding_", label, ".pdf", sep = "")
@@ -81,13 +86,38 @@ fieldingplot.formula = function(x, data, label = "label", write.pdf = FALSE, ...
8186
}
8287
}
8388

84-
#' @rdname fieldingplot
89+
#' @export
90+
#' @rdname plotFielding
8591

86-
fieldingplot.default = function(x, y, z, label = "label", write.pdf = FALSE, ...) {
92+
plotFielding.default = function(x, y, z, label = "label", write.pdf = FALSE, ...) {
8793
stop("No available methods")
8894
}
8995

90-
#' @rdname fieldingplot
96+
97+
#' @export
98+
#' @rdname plotFielding
99+
#' @method plotFielding bkde2D
100+
#'
101+
#' @examples
102+
#' fmod <- getModelFieldingCollective(May)
103+
#' plotFielding(fmod)
104+
#'
105+
106+
plotFielding.bkde2D <- function(x, ...) {
107+
contourplot(x = x$fhat, row.values = x$x1, column.values = x$x2,
108+
panel = panel.fielding, region = TRUE, alpha.regions = 0.5,
109+
col.regions = colorRampPalette(RColorBrewer::brewer.pal(9, "Blues"))(100),
110+
cuts = 10, contour = TRUE,
111+
# , xlim = c(-350, 350), ylim = c(0, 550)
112+
xlab = "Horizontal Distance from Home Plate (ft.)",
113+
ylab = "Vertical Distance from Home Plate (ft.)",
114+
...
115+
)
116+
}
117+
118+
119+
#' @rdname plotFielding
120+
#' @export
91121

92122
panel.fielding = function(x, y, z, ...) {
93123
panel.baseball()

man/fieldingplot.Rd

Lines changed: 0 additions & 66 deletions
This file was deleted.

man/getModelFieldingCollective.Rd

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22
% Please edit documentation in R/getModels.R
33
\name{getModelFieldingCollective}
44
\alias{getModelFieldingCollective}
5+
\alias{getModelFieldingCollective.GameDayPlays}
6+
\alias{getModelFieldingCollective.default}
57
\title{getModelFieldingCollective}
68
\usage{
79
getModelFieldingCollective(data)
10+
11+
\method{getModelFieldingCollective}{GameDayPlays}(data)
12+
13+
\method{getModelFieldingCollective}{default}(data)
814
}
915
\arguments{
10-
\item{data}{An MLBAM data.frame}
16+
\item{data}{A \code{\link{GameDayPlays}} object}
1117
}
1218
\value{
1319
a vector representing the probability that each ball in play will be fielded
@@ -21,4 +27,8 @@ Determine the responsibility of the fielders, collectively
2127
Computes a 2D kernel smoothed estimate of the probability that *any* of the 9 fielders
2228
will make a play on a ball in play
2329
}
30+
\examples{
31+
fmod <- getModelFieldingCollective(May)
32+
plotFielding(fmod)
33+
}
2434

0 commit comments

Comments
 (0)