CF Range
“Best” depends on the question. A fielder optimized for late-inning substitutions isn’t necessarily the one you sign as a free agent. This project builds a model of CF range and derives two statistics from it, each designed for a different front-office decision.
The model
Each fielder gets a catchability ellipse that scales with hang time. Four per-player parameters define their range.
Starting position \(y_{0,i}\) — the fielder’s actual pre-play depth, estimated from catch/no-catch outcomes rather than assumed fixed. The adjusted displacement is \(\Delta y_\text{adj} = \Delta y - y_{0,i}\).
Asymmetric depth speed — fielders charge better than they retreat. A smooth asymmetry factor \(\gamma_i\) stretches the forward half of the ellipse:
\[w = \sigma\!\left(\frac{-\Delta y_\text{adj}}{5}\right), \quad b_\text{eff} = b_i \exp(\log \gamma_i \cdot w)\]
where \(w \approx 1\) on balls in front of the fielder and \(\approx 0\) behind. The normalized distance and catch probability are then:
\[d_i = \sqrt{\left(\frac{\Delta x}{a_i \tau}\right)^2 + \left(\frac{\Delta y_\text{adj}}{b_{\text{eff},i}\, \tau}\right)^2}, \qquad P(\text{catch}) = \sigma(\beta_0 - \beta_1 d_i)\]
The four parameters \((a_i, b_i, y_{0,i}, \gamma_i)\) — lateral speed, depth speed, starting depth, and charge asymmetry — define each player’s range. Hang time is derived from launch speed and launch angle via \(\tau = 2v_0\sin\theta/g\).
Why this matters. Diagnosing the original two-parameter model on 2021–2024 data found systematic overprediction on charging plays (sectors 225–315°, mean residual −0.062) and significant spatial autocorrelation in 32 of 35 qualified players. Root cause: fielders play considerably deeper than the assumed 310 ft — the v2.0 model estimates a population-average starting depth of ~353 ft. The fixed-origin model conflated range with positioning. Introducing \(y_{0,i}\) reduces the charging zone residual from −0.062 to −0.0002, leaving \((a_i, b_i, \gamma_i)\) as clean speed estimates from the player’s actual location.
Two statistics, two decisions
\((a_i, b_i)\) are the parameters. The decision determines the estimand.
Reliable range weights each player’s range by where balls are actually hit. A play is in \(R_i\) if the full posterior puts \(P(\text{catch}) \geq 0.80\) in at least 80% of samples — high probability and high confidence:
def opportunity_weighted_range(player, all_fly_balls):
# A play is "reliably covered" if ≥80% of posterior samples
# give P(catch) ≥ 0.80
in_range = fraction_above_threshold(player.posterior, fly_ball) >= 0.80
return in_range.mean()Spectacular play probability isolates performance in the thin-coverage zone — balls outside most fielders’ reliable ranges, where only the best CFs make plays:
def spectacular_play_prob(player, fly_balls, spectacular_zone):
hard_plays = fly_balls[spectacular_zone]
# Use full posterior mean — actual expected performance on hard plays
return catch_prob(player.a_mean, player.b_mean, hard_plays).mean()The two stats can rank players differently. A player with elite straight-back range racks up opportunity-weighted range. A player who makes diving plays in the gaps earns spectacular play probability.
| Decision | Statistic |
|---|---|
| Free agent evaluation | Opportunity-weighted reliable range |
| Late-inning substitution | Spectacular play probability |
| Park fit | Shape of \(R_i\), not just size |
Reading the parameters
The model outputs \((a_i, b_i, y_{0,i}, \gamma_i)\); several derived quantities make them interpretable.
Ellipse area at hang time \(\tau\) is \(\pi a b \tau^2\) — the coverage footprint in square feet. At \(\tau = 5\) seconds (a long fly ball), the 2021 median qualified CF covers about 23,300 sq ft. The best cover roughly 29,700 sq ft — 27% more ground, a gap comparable in size to a baseball diamond.
Marginal plays per season. The difference in opportunity-weighted range between two players, multiplied by the ~425 CF fly-ball opportunities per team slot per season, converts the statistic into a concrete defensive volume. In 2021, Harrison Bader’s range advantage over the median CF translates to roughly +34 plays per season; Michael Taylor’s to +26. A 0.03 difference in opportunity-weighted range is approximately 13 additional plays per season.
Shape ratio \(a/b\) characterizes the type of range independent of total size. Most qualified CFs fall between 1.5–2.0, reflecting that lateral movement is generally faster than depth movement. But the extremes are informative: Tyler Naquin (\(a/b \approx 2.2\)) is strongly lateral-dominant — large total ellipse, but well below-median opportunity-weighted range because his coverage shape does not match where CF fly balls actually land. Yonathan Daza (\(a/b \approx 1.3\)) runs the opposite pattern: modest total area but ranked in the top 3 for spectacular play probability, driven by unusual depth range.
Starting depth \(y_{0,i}\) is the player’s inferred pre-play position. A positive offset means the player typically positions deeper than 310 ft; negative means shallower. This absorbs the systematic overprediction on charging plays that afflicted the v1.0 model and makes \((a_i, b_i)\) interpretable as pure speed estimates from the player’s actual location.
Charge asymmetry \(\gamma_i\) is the ratio of effective depth speed going in versus going back. \(\gamma_i > 1\) means the fielder is faster charging than retreating. The population prior is centered at \(\gamma = 1\) — the data drives any departure, and the hierarchical structure prevents sparse-data players from getting extreme estimates.
Bader vs. Bradley (2021) shows how the shape decomposition works in practice. Both players have nearly identical lateral reach (~125 ft at \(\tau = 5\) s). The entire gap between them — Bader’s ellipse is 13.6% larger — comes from depth: Bader reaches 75 ft deep versus Bradley’s 65 ft. Ten feet of additional depth range, compounded over a full season, accounts for roughly +26 plays.
Bayesian pipeline
The full implementation uses Stan with sequential season-to-season updating. Rather than re-estimating from scratch each year, each player’s prior for a new season is their previous season’s posterior, inflated by a factor \(\lambda\) to allow for year-to-year change:
\[\sigma_{\text{prior}} = \lambda \cdot \sigma_{\text{posterior}}, \quad \lambda = 1.25\]
This encodes a sensible belief: a player’s range next year is probably similar to this year’s, but not identical. New players receive the population hyperparameters as their prior. The result:
- Players with few opportunities are pulled toward the population mean (shrinkage)
- Players with many opportunities are barely affected — the data overwhelms the prior
- As data accumulates through a season, credible bands on \((a_i, b_i)\) narrow visibly
Burn-in (2017–2020): fit cf_range_v2.stan once to estimate population hyperparameters \((\mu_a, \sigma_a, \mu_b, \sigma_b, \mu_{y_0}, \sigma_{y_0}, \mu_{\log\gamma}, \sigma_{\log\gamma})\) and logistic shape \((\beta_0, \beta_1)\).
Sequential updates (2021–2024): fit cf_range_update_v2.stan each season, passing all eight per-player prior parameters as data. \(\beta_0, \beta_1\) are fixed at burn-in posterior means — re-estimating them from a single season would be poorly identified.
The Bayesian reliable range \(R_i\) uses the full posterior: a play is included if at least 80% of posterior samples give \(P(\text{catch}) \geq 0.80\). Thin-data players get a smaller \(R_i\) because their wider posterior makes fewer plays reliably covered; as data accumulates through a season, the credible band narrows and \(R_i\) stabilizes.
Part 2: Optimal positioning
Knowing who has the best range answers one question. A different question: given the fielder you have, where should you put him?
For each batter, Statcast gives us a spray distribution of CF fly-ball landing spots. Given a fielder’s four parameters and that distribution, the optimal pre-pitch starting position is:
\[(\hat{x}_0, \hat{y}_0) = \underset{x_0, y_0}{\arg\max} \; \mathbb{E}[\text{coverage} \mid x_0, y_0, f_{\text{batter}}, a_i, b_i, \gamma_i]\]
Because \(y_{0,i}\) is already estimated by the model, the optimizer shifts from the player’s inferred actual baseline rather than an assumed canonical depth. The answer depends on both the fielder’s range shape and the batter — a depth-dominant fielder may shade differently than a lateral-dominant peer facing the same pull hitter. The same framework quantifies the value of shifting: how much coverage does moving 15 ft gain, and for which fielder-batter pairs does positioning matter most?
Further extensions
Age curve on the prior mean. The sequential update currently assumes “next year = this year” as the baseline. A better motion law shifts the prior mean along a population-level age curve — estimated hierarchically from all players simultaneously. A 27-year-old’s prior for next season is slightly better than their current posterior; a 33-year-old’s is slightly worse. The variance inflation λ captures off-season uncertainty on top of the age trend. This is well-suited to a hierarchical model: individual aging curves are unidentifiable from a handful of seasons per player, but the population curve is estimable from the full panel.
Superellipse boundary. The ellipse (\(p=2\)) assumes full directional coupling — lateral and depth movement compete for the same time budget. The superellipse \(d = \bigl(|\Delta x / a\tau|^p + |\Delta y_\text{adj} / b_\text{eff}\tau|^p\bigr)^{1/p}\) relaxes this with a single additional parameter. The empirical test: rerun spatial autocorrelation (Moran’s I) on v2.0 residuals. If the clustering disappears once the positional confound is absorbed, the ellipse is adequate. If a symmetric residual pattern emerges at the 45° diagonals, \(p > 2\) is indicated.
Diagonal asymmetry. A correlation parameter \(\rho\) in the distance metric yields a tilted ellipse whose principal axes rotate away from field coordinates, capturing directional preferences (natural glide paths, handedness) that \((a, b, \gamma)\) cannot express. Worth adding if per-quadrant residual decomposition on high-data players shows systematic diagonal structure after v2.0.
Measurement uncertainty. Statcast landing coordinates carry ~1–2 ft of tracking error. Marginalizing over this with Gaussian quadrature rather than treating each coordinate as exact is a principled correction with negligible runtime cost.
Environmental covariates. Wind is the largest omitted variable. The natural correction is to adjust observed displacements for wind before fitting the model.
Validation
Two external checks ground the model in data beyond what was used to fit it.
OAA correlation. Statcast Outs Above Average (OAA) is an independent tracking-based metric derived from actual fielder routes, not catch/no-catch outcomes. Spearman \(\rho(\text{OWR}, \text{OAA})\) is +0.18 to +0.33 across seasons — consistently positive and mostly significant. An additional check: the directional OAA decomposition (In vs Back zones) provides an independent signal on \(\gamma_i\). Players with higher charge asymmetry in the model show a higher share of their OAA coming from “In” plays: \(\rho(\gamma, \text{OAA In-fraction}) = +0.17\) pooled across seasons (\(p = 0.001\)).
Starting position face validity. Because Statcast does not publish pre-play fielder coordinates in its public API, direct validation of \(y_{0,i}\) against observed positions is not feasible. Three indirect checks: (1) the population mean of \(y_{0,i}\) is +43 ft — the model infers fielders position at ~353 ft on average, deeper than the canonical 310 ft reference, consistent with typical CF fly-ball territory; (2) relative rankings pass a face-validity check — Brenton Doyle ranks among the deepest (Coors Field, thin air), Corbin Carroll and Byron Buxton among the shallowest (aggressive, speed-first positioning); and (3) plays under “Strategic” outfield alignment show higher RMSE than “Standard” plays (0.293 vs 0.265), confirming that the model fits worse precisely where its typical-positioning assumption is least valid.
Charge asymmetry (\(\gamma_i\)) vs directional OAA — players with higher model-estimated charge asymmetry earn a larger share of their defensive value from “In” plays:
Results
v2.0 diagnostic summary. The charging zone residual dropped from −0.062 to −0.0002 (FIXED). Boundary calibration improved from a +0.160 gap to +0.063 (PARTIAL). Year-over-year correlations are stable and improving as the sequential prior chain accumulates (OWR \(\rho\) = 0.69–0.85, \(b\) \(\rho\) = 0.72–0.94 across adjacent seasons). The \(\sigma_\text{transition}\) test finds no signal for a v3.0 state-space model — estimation noise dominates the year-to-year differences at current data volume.
Directional residuals and spatial autocorrelation — the key v1.0 pathologies:
Year-over-year parameter stability across adjacent seasons:
Rankings by opportunity-weighted reliable range, 2021–2024:
Posterior catch probability for the top-ranked CF — dashed contour is the posterior-mean ellipse boundary, semi-transparent ellipses are individual posterior samples:
Coverage across the outfield — how many qualified CFs have reliable range at each location. Dark spots are the spectacular zone:
Within-season evolution for 2024 — credible bands narrow as data accumulates through the year:
Resources
- GitHub — full code, model, and data pipeline