xBA / xBA
Batter
Expected batting average: Savant's per-batted-ball estimate divided by at-bats, excluding at-bats whose batted ball carries no estimate. See runbook section 22.
Agreement with Savant
Checked nightly. Agreement tolerance: ±0.
Pitcher
Expected batting average against: Savant's per-batted-ball estimate divided by at-bats against, excluding at-bats whose batted ball carries no estimate. See runbook section 22.
Agreement with Savant
Checked nightly. Agreement tolerance: ±0.
Shown for: batter and pitcher.
Formula
COALESCE(sum(xba_num), 0) / NULLIF(sum(ab) - sum((ab = 1 AND bip = 1 AND xba_num IS NULL)::int), 0) — Savant's per-batted-ball estimate averaged over at-bats, excluding at-bats whose batted ball carries no estimate.
Inputs
pitches.estimated_ba_using_speedangleplate_appearances.xba_numplate_appearances.abplate_appearances.bip
Era caveats
Untracked contact leaves the denominator
Savant excludes batted balls it could not track rather than imputing them, and matching that is what closed the residual. The exclusion reaches the three expected stats by two different routes: xBA and xSLG subtract at aggregation time — at-bats minus at-bats whose batted ball carries no estimate — while xwOBA never counts them at all, because a plate appearance with no estimate never enters xwoba_den when its fact row is built. (runbook §10)
Three tracking systems across the seasons shown
Rows carry a tracking_era of trackman, hawkeye or hawkeye_bat (migration 001). The systems differ in what they measure and how precisely, so long spans cross a measurement boundary as well as a rule one.
Version history
- 2026-08-14 — Added with the expected-stats slice: estimated_ba_using_speedangle, typed out of the extras JSONB catch-all by migration 010 rather than refetched from Savant, since the bytes were already stored. (runbook §22)