Expected Swing Value Grader: Methodology
For every pitch a batter faces, there is a correct decision: swing or take. The ESV Grader computes the expected run value of each option and grades the batter's actual choice.
Key findings (2020–2026)
Swing/take decision quality is a repeatable batter skill with year-over-year stability rho of +0.60 (hitters with 750+ pitches in both seasons, averaged over the five full-season pairs 2021→2026), the strongest validation numbers across all modules. Among hitters with 750+ pitches, the best single seasons are roughly +5 to +6 runs per 100 pitches above league average and the worst roughly −4 to −5; most fall between −2.3 and +2.3.
What we measure
Baseball Savant grades execution: did the batter make contact on an expected swing? We grade the decision: given this pitch, in this count, in this base-out situation, was swinging or taking the higher expected-value action?
A batter who whiffs on a breaking ball in the dirt grades the same as one who takes it, as long as both were the right decision given the pitch. The ESV grade is independent of whether the batter made contact. It evaluates only the swing/take choice.
The ESV formula
Expected Swing Value (ESV) is the difference in expected run value between swinging and taking:
Positive ESV means swinging is the higher-EV choice; negative ESV means taking is.
E(swing):
+ (1 − P(contact)) × whiff_delta
- P(contact): Batter's contact rate on swings against this pitch bin, from earlier seasons only (see below). Falls back to the league average for the bin when the batter saw fewer than 50 pitches in it in prior seasons.
- avg_contact_delta_rv: Batter's average run-value delta on contact against this pitch bin in earlier seasons, from Statcast's
delta_run_expfield (same fallback rule). - whiff_delta: The RE24 change from adding a strike to the count (count_rv_after_strike − count_rv_current). A whiff advances the count to a worse state for the batter.
E(take):
- P(ball) / P(called_strike): Umpire call rates for this pitch profile (zone × pitcher handedness), computed from all historical takes, not the specific batter's choices.
- ball_delta: RE24 change from adding a ball (count_rv_after_ball − count_rv_current).
- cs_delta: RE24 change from a called strike (count_rv_after_strike − count_rv_current).
Terminal count states are handled explicitly: on ball 3, the walk outcome is computed as the RE24 of the new base state (batter to 1B, runners advance if forced). On strike 2, the strikeout outcome uses RE24 with outs + 1.
Pitch binning
Each pitch is placed in a bin defined by four observable pre-pitch factors. Batter contact history and the league-average fallback are computed at the bin level:
- Pitch type (Statcast's own label, not grouped): each Statcast pitch type is its own bin dimension. 18 types appear in 2020–2026 (FF, SI, FC, FS, SL, ST, SV, CU, KC, CS, CH, FO, SC, KN, EP, FA, PO, UN). All are graded; none are excluded.
- Location zone (13): Statcast's standard zone grid as recorded: zones 1–9 inside the strike zone and zones 11–14 outside it. Zones are defined from the catcher's view, so the same zone is inside for one batter side and outside for the other.
- Velocity tier (3): Slow / Medium / Hard. Thresholds depend on which family the pitch type belongs to:
- Fastball family (FF, SI, FC, FS): <92 / 92–96 / >96 mph
- Breaking family (SL, ST, SV, CU, KC, CS): <78 / 78–83 / >83 mph
- Offspeed family (CH, FO, SC, KN): <83 / 83–87 / >87 mph
- Everything else (EP, FA, PO, UN): <80 / 80–90 / >90 mph
- Pitcher handedness (2): Left / Right.
Up to 18 × 13 × 3 × 2 = 1,404 bins are possible; 1,042 actually occur in 2020–2026 data. Splitting pitch type this finely is a main reason most pitch decisions fall back to league-average contact rates (see below): a batter needs 50 prior pitches in one exact type × zone × velocity × pitcher-hand combination before their own history is used.
Count-state run expectancy (288-cell table)
The ESV model requires a run-expectancy value for every combination of count state (balls, strikes) and base-out situation. This is a 288-cell table: 12 count states (0–0 through 3–2) × 24 base-out states (8 base configurations × 3 outs values).
How it is computed: For each pitch in the full 2020–2026 Statcast dataset, we record the current count state, base-out state, and the maximum batting-team score achieved for the rest of the inning. The count_rv for a given cell is the average expected runs from that point to the end of the inning, computed from all pitches seen in that exact (balls, strikes, on_1b, on_2b, on_3b, outs) state.
This table is a one-time computation: it is saved as data/reference/count_re24.csv and reused for all subsequent pipeline runs. The minimum cell size across all 288 cells is >149 observations (mean >10,000). It is distinct from the 24-state RE24 table used by the other modules.
Grading logic
Each pitch decision is assigned one of four grades:
- GOOD_SWING: Batter swung and ESV > 0 (swinging was the higher-EV option).
- BAD_SWING: Batter swung and ESV < 0 (taking would have been better).
- GOOD_TAKE: Batter took and ESV < 0 (taking was the higher-EV option).
- BAD_TAKE: Batter took and ESV > 0 (swinging would have been better).
There is no tolerance band around zero: a pitch where swinging is better by a tiny margin is graded the same way as one where it is better by a lot. The run-value weighting below is what keeps near-zero decisions from mattering much. A pitch is NEUTRAL only if ESV is exactly 0, which essentially never happens; NEUTRAL pitches count toward n_pitches and are scored as not correct in correct_rate.
Run value per pitch = ±|ESV| × leverage_weight, where leverage_weight = RE24(base-out state) / RE_MEAN (RE_MEAN = 0.477, the average run expectancy across all base-out states). Correct decisions contribute positive run value; incorrect decisions contribute negative.
Aggregation and leaderboards
We aggregate by batter × season. The primary metric is runs added above league average per 100 pitches (RAA/100): the leverage-weighted run value added, normalized to 100 pitches and expressed as a deviation from the pitch-weighted league mean for that season.
- Minimum threshold: 200 pitches seen in the season to appear on the leaderboard.
- Low sample flag: Fewer than 500 pitches; estimates carry higher uncertainty.
- 2020 flag (60g): The 60-game season produces partial-season samples.
- 2026 flag: 2026 entries reflect the complete regular season.
Why league-average centering? The raw metric produces a structural positive mean of ~+4.2 runs per 100 pitches because the correct-decision rate (68.1%) is above 50% and the |ESV| distribution is asymmetric. Subtracting the pitch-weighted league mean centers the distribution at zero: a score of 0 means exactly league-average decision quality, not random swing/take behavior.
Validation: year-over-year stability
The primary validation for the ESV module is year-over-year stability: does a batter's RAA/100 in year Y predict their RAA/100 in year Y+1? A metric measuring a real, repeatable skill should show a positive correlation; noise produces near-zero correlation.
We restrict to batters with ≥ 750 pitches in both seasons (full-time regulars), the same population used on the predictive-validity page. Spearman correlation:
| Season pair | n batters | Spearman ρ | Notes |
|---|---|---|---|
| 2020 → 2021 | 127 | +0.514 | Context only: 60-game 2020 season; few hitters reach 750 pitches |
| 2021 → 2022 | 273 | +0.622 | |
| 2022 → 2023 | 265 | +0.579 | |
| 2023 → 2024 | 283 | +0.624 | Best pair |
| 2024 → 2025 | 270 | +0.598 | |
| 2025 → 2026 | 263 | +0.576 | |
| Mean (5 full-season pairs, 2021→2026) | 1,354 | +0.600 | Headline figure used across the site |
The 2020→2021 pair is the weakest because the 60-game 2020 season produces noisier individual estimates; fewer pitches per batter means higher sampling error. The five full-season pairs are consistently in the +0.58 to +0.62 range, confirming that swing/take decision quality is a genuine, repeatable batter skill that persists across seasons. These are the strongest YoY validation numbers across all modules in this system.
These correlations are computed on grades that use only earlier seasons' contact history (see below), so no season's grade draws on data from the season it is being compared with.
Does decision quality predict the future?Repeatable isn't the same as predictive. We tested whether RAA/100 forecasts next-season production. It mostly doesn't. Parts of this study are currently being re-verified.Fallback rate and batter-specific history
Batter-specific P(contact) and avg_contact_delta_rv are only used when the batter has enough history in the exact bin. For a pitch in season N, we look only at seasons before N and require at least 50 pitches seen in that (batter, pitch_bin) combination. Otherwise we fall back to the league-average contact rate and contact run-value delta for the bin, computed from all MLB pitches up to and including season N. No grade uses information from a later season.
Across 2020–2026, about 88% of pitch decisions use the league-average fallback (86% for hitters with 750+ pitches in the season). The rate falls as history accumulates:
| Season | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | 2026 |
|---|---|---|---|---|---|---|---|
| Fallback rate | 100% | 99% | 94% | 88% | 83% | 79% | 78% |
The data starts in 2020, so 2020 grades use no batter-specific history at all and 2021 grades almost none. Because bins are fine-grained (exact pitch type × zone × velocity tier × pitcher hand), even a veteran rarely reaches 50 prior pitches in a given bin outside their most common fastball locations.
Practical implication for interpretation:
- ESV mainly grades pitch selection against league-average expectations. For most pitches, E(swing) reflects how the league as a whole fares swinging at that pitch type, location and velocity, not this batter's own bat-to-ball ability.
- Personalization increases in later seasons and for regulars with long track records. The leaderboard's fallback column shows each batter-season's rate directly.
- E(take) never uses batter history. It comes from umpire call rates and count run expectancy, so it is equally reliable for every batter.
Correction and robustness check (October 2026): earlier versions of this grader pooled each batter's contact history across all of 2020–2026, so a grade could draw on seasons after the one being graded. We rebuilt the history to use earlier seasons only. That raised the fallback rate from about 66% to about 88%, yet the grades barely changed: batter-season RAA/100 rankings correlate 0.98 between the two versions, and year-over-year stability (750+ pitches, 2021–2026) went from +0.61 to +0.60. The decision grade does not depend heavily on batter-specific contact history.
Data sources
- Pitch-level data: MLB Statcast via pybaseball (
statcast(), 2020–2026). Fields used:pitch_type,zone,release_speed,p_throws,balls,strikes,on_1b/on_2b/on_3b,outs_when_up,description,delta_run_exp. - Count-state run expectancy: 288-cell table computed from 2020–2026 Statcast data (saved to
data/reference/count_re24.csv). - Umpire call rates: Computed from all taken pitches in the 2020–2026 dataset, grouped by (zone, pitcher handedness). Not pitch-type specific; umpire behavior is primarily zone-dependent.
- Batter contact history: Grouped by (batter, pitch_type, zone, vel_tier, p_throws) and built separately for each graded season from earlier seasons only. Pitches seen counts every pitch in the bin; P(contact) uses swings (swing-and-miss, foul, or in-play); contact run value uses balls put in play.
- League-average fallback: Same bins, all MLB batters, built for each season from that season and earlier.
Known limitations
- ▲~88% fallback to league-average contact rates. Most pitch decisions have fewer than 50 prior-season pitches in their bin, so E(swing) usually reflects the league's results on that pitch rather than the batter's own. 2020 (100%) and 2021 (99%) grades are effectively not personalized.
- ▲League-average fallback mixes left- and right-handed batters. A batter's own history is already side-specific: one-sided hitters always bat from the same side, and a switch hitter's side is set by pitcher hand, which is a bin dimension. But the league-average table used for most pitches pools both batter sides. Because zones are defined from the catcher's view, the same zone is inside for one side and away for the other. We tested splitting the league-average fallback table by batter handedness. The effect is real but small: it changes about 2% of pitch grades and narrows a roughly 0.2 RAA/100 gap between mostly-left-handed and mostly-right-handed hitters by 20–45%, without changing year-over-year stability. It is not being shipped on its own; it is planned for a future pipeline update, bundled with a minimum-swing floor for the split cells.
- ▲Run-expectancy and umpire tables use all seasons. The count-state run-expectancy table and umpire call rates are league-wide environment tables built from all of 2020–2026, including seasons after the one graded. They contain no batter-specific information.
- ▲Umpire rates are aggregate, not pitch-type-specific. P(ball) and P(called_strike) vary by zone and pitcher hand but not by pitch type within a zone. A 97 mph fastball in zone 2 and an 88 mph changeup in zone 2 receive the same umpire rates.
- ▲E(swing) uses average contact delta_rv, not predictive. The
avg_contact_delta_rvis the batter's historical average on contact against that bin, not an expected value conditioned on hitting the ball well. It reflects past outcomes, not a forward-looking contact quality estimate. - ▲No pitch sequence effects. Each pitch is graded independently. Whether the previous pitch was a called strike or a ball out of the zone affects a batter's real decision calculus but is not captured here.
- ▲2020 entries carry higher uncertainty due to the 60-game season (~200–500 pitches per batter vs. 600–1,200 in full seasons). The 2020→2021 YoY rho (+0.514 among the 127 hitters with 750+ pitches in both seasons) is lower than all other pairs for this reason.
- ▲Grades decision quality, not swing mechanics. A batter who correctly identifies a fastball to swing at but pops it up receives a GOOD_SWING. The ESV module is not a hitting tool. It grades the swing/take decision, not the execution.