You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Simplify float stat conversion using Pydantic coercion #340
PR #241 adds a custom BeforeValidator / OptionalFloat type to convert MLB Stats API string values such as ".287" into Python floats.
Pydantic already performs this coercion for fields typed as float or Optional[float] (float | None), so the custom conversion logic is unnecessary for normal numeric strings.
Before removing the validator entirely, verify whether the MLB Stats API can return non-numeric sentinel values for these fields, such as:
.---
-
""
If those values occur, normalization should handle only those MLB-specific sentinel values and allow Pydantic to perform the normal string-to-float coercion.
We should avoid silently converting every invalid value to None, since that can hide unexpected API data or model errors.
Acceptance criteria
Verify representative MLB responses for numeric float fields and identify any sentinel/non-numeric values.
Replace OptionalFloat with Optional[float] / float | None where standard Pydantic coercion is sufficient.
Remove the generic float_or_none / BeforeValidator conversion if it is no longer needed.
If MLB sentinel values require normalization, handle only the known sentinel values explicitly.
Add or update tests covering numeric strings, None, and any known MLB sentinel values.
The following string-backed stat fields are candidates to replace with standard Optional[float] / float | None, allowing Pydantic to handle numeric-string coercion directly.
SimpleCatchingSplit
avg
obp
slg
ops
caught_stealing_percentage
stolen_base_percentage
strikeout_walk_ratio
SimpleFieldingSplit
caught_stealing_percentage
stolen_base_percentage
fielding
range_factor_per_game
range_factor_per_9_inn
catcher_era
AdvancedHittingSplit
babip
pitches_per_plate_appearance
walks_per_plate_appearance
strikeouts_per_plate_appearance
home_runs_per_plate_appearance
walks_per_strikeout
iso
SimpleHittingSplit
avg
obp
slg
ops
caught_stealing_percentage
stolen_base_percentage
babip
groundouts_to_airouts
at_bats_per_home_run
SimplePitchingSplit
avg
obp
slg
ops
caught_stealing_percentage
stolen_base_percentage
era
whip
strike_percentage
groundouts_to_airouts
win_percentage
pitches_per_inning
strikeout_walk_ratio
strikeouts_per_9_inn
walks_per_9_inn
hits_per_9_inn
runs_scored_per_9
home_runs_per_9
AdvancedPitchingSplit
winning_percentage
runs_scored_per_9
babip
obp
slg
ops
strikeouts_per_9
base_on_balls_per_9
home_runs_per_9
hits_per_9
strikeouts_to_walks
strikeouts_minus_walks_percentage
gidp_percentage
batters_faced_per_game
whiff_percentage
strike_percentage
pitches_per_inning
pitches_per_plate_appearance
walks_per_plate_appearance
strikeouts_per_plate_appearance
home_runs_per_plate_appearance
walks_per_strikeout
iso
flyball_percentage
ExpectedStatistics
avg
slg
woba
wobacon
The baseball-notation innings fields are intentionally excluded from this conversion list:
SimpleFieldingSplit.innings
SimplePitchingSplit.innings_pitched
AdvancedPitchingSplit.innings_pitched_per_game
They should remain outside the scope of this issue because values such as "6.2" represent 6 2/3 innings rather than the mathematical decimal 6.2.
Activity
Mattsface commentedon Sep 3, 2026
Fields to update from PR #241
The following string-backed stat fields are candidates to replace with standard
Optional[float]/float | None, allowing Pydantic to handle numeric-string coercion directly.SimpleCatchingSplitavgobpslgopscaught_stealing_percentagestolen_base_percentagestrikeout_walk_ratioSimpleFieldingSplitcaught_stealing_percentagestolen_base_percentagefieldingrange_factor_per_gamerange_factor_per_9_inncatcher_eraAdvancedHittingSplitbabippitches_per_plate_appearancewalks_per_plate_appearancestrikeouts_per_plate_appearancehome_runs_per_plate_appearancewalks_per_strikeoutisoSimpleHittingSplitavgobpslgopscaught_stealing_percentagestolen_base_percentagebabipgroundouts_to_airoutsat_bats_per_home_runSimplePitchingSplitavgobpslgopscaught_stealing_percentagestolen_base_percentageerawhipstrike_percentagegroundouts_to_airoutswin_percentagepitches_per_inningstrikeout_walk_ratiostrikeouts_per_9_innwalks_per_9_innhits_per_9_innruns_scored_per_9home_runs_per_9AdvancedPitchingSplitwinning_percentageruns_scored_per_9babipobpslgopsstrikeouts_per_9base_on_balls_per_9home_runs_per_9hits_per_9strikeouts_to_walksstrikeouts_minus_walks_percentagegidp_percentagebatters_faced_per_gamewhiff_percentagestrike_percentagepitches_per_inningpitches_per_plate_appearancewalks_per_plate_appearancestrikeouts_per_plate_appearancehome_runs_per_plate_appearancewalks_per_strikeoutisoflyball_percentageExpectedStatisticsavgslgwobawobaconThe baseball-notation innings fields are intentionally excluded from this conversion list:
SimpleFieldingSplit.inningsSimplePitchingSplit.innings_pitchedAdvancedPitchingSplit.innings_pitched_per_gameThey should remain outside the scope of this issue because values such as
"6.2"represent 6 2/3 innings rather than the mathematical decimal 6.2.Normalize MLB float sentinel strings before Pydantic coercion
Convert numeric stat fields from str to float via Pydantic coercion