Skip to content

Commit

Permalink
Feat: Update "OOA Behind Pitcher" function (#299)
Browse files Browse the repository at this point in the history
* Update "OOA Behind Pitcher" function

* Update docs and argument descriptions for view opt
  • Loading branch information
zach-hopkins authored Dec 13, 2022
1 parent c574f72 commit 4c207bf
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 3 deletions.
8 changes: 7 additions & 1 deletion docs/statcast_fielding.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
**Note:** Statcast data is liable to change unexpectedly due to the large number of observations. Please keep that in mind when pulling data.

# Statcast Fielding Outs Above Average
`statcast_outs_above_average(year: int, pos: Union[int, str], min_att: Union[int, str] = "q")`
`statcast_outs_above_average(year: int, pos: Union[int, str], min_att: Union[int, str] = "q", view: str = "Fielder")`

This function retrieves outs above average (OAA) for the given year, position, and attempts. OAA is a Statcast metric based on the "cumulative effect of all individual plays a fielder has been credited or debited with, making it a range-based metric of fielding skill that accounts for the number of plays made and the difficulty of them".

## Arguments
`year:` The year for which you wish to retrieve batted ball against data. Format: YYYY.
`pos:` The position you are interested in. Valid positions include "all", "IF", "OF", and position names, numbers, or abbreviations. Position numbers may be entered as integers or strings, e.g. 6 or "6" for shortstops. Pitchers and catchers are not included.
`min_att:` The minimum number of fielding attempts for the player to be included in the result. Statcast's default is players, which is 1 fielding attempt per game played for 2B, SS, 3B, and OF and 1 fielding attempt per every other game played for 1B.
`view:` The perspective by which the OAA numbers should be returned. Statcast default is fielders, which returns typical OOA statistics for all eligible fielders. Valid views include "Fielder" (default) "Pitcher" (OOA of defense behind pitcher), "Fielding_Team", "Batter" (OOA of Defense when player is at-bat), and "Batting_Team". The argument "min_att" is ignored on team based views.

## Examples of Valid Queries
```python
Expand All @@ -23,8 +24,13 @@ data = statcast_outs_above_average(2019, pos = "cf")

# Shortstops who qualified in 2019
data = statcast_outs_above_average(2019, pos = 6)

# Infielders with at least 100 fielding attempts in 2019
data = statcast_outs_above_average(2019, pos = "IF", min_att = 100)

# Infield defense stats behind particular pitchers in 2021
data = statcast_outs_above_average(2021, pos = "IF", view = "Pitcher")

```

# Statcast Fielding Outfield Directional OAA
Expand Down
22 changes: 20 additions & 2 deletions pybaseball/statcast_fielding.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,31 @@
from . import cache
from .utils import norm_positions, sanitize_statcast_columns

"""Scrapes outs above average from baseball savant for a given year and position
Args:
year (int): Season to pull
pos (Union[int, str]): Numerical position (e.g. 3 for 1B, 4 for 2B). Catchers not supported
min_att (Union[int, str], optional): Integer number of attempts required or "q" for qualified.
Defaults to "q".
view (str, optional): Perspective of defensive metrics. String argument supports "Fielder", "Pitcher", "Fielding_Team", "Batter", and "Batting_Team"
Defaults to "Fielder"
Raises:
ValueError: Failure if catcher is passed
Returns:
pd.DataFrame: Dataframe of defensive OAA for the given year and position for players who have met
the given threshold
"""

@cache.df_cache()
def statcast_outs_above_average(year: int, pos: Union[int, str], min_att: Union[int, str] = "q") -> pd.DataFrame:
def statcast_outs_above_average(year: int, pos: Union[int, str], min_att: Union[int, str] = "q", view: str = "Fielder") -> pd.DataFrame:
pos = norm_positions(pos)
# catcher is not included in this leaderboard
if pos == "2":
raise ValueError("This particular leaderboard does not include catchers!")
url = f"https://baseballsavant.mlb.com/leaderboard/outs_above_average?type=Fielder&year={year}&team=&range=year&min={min_att}&pos={pos}&roles=&viz=show&csv=true"
url = f"https://baseballsavant.mlb.com/leaderboard/outs_above_average?type={view}&year={year}&team=&range=year&min={min_att}&pos={pos}&roles=&viz=show&csv=true"
res = requests.get(url, timeout=None).content
data = pd.read_csv(io.StringIO(res.decode('utf-8')))
data = sanitize_statcast_columns(data)
Expand Down
15 changes: 15 additions & 0 deletions tests/integration/pybaseball/test_statcast_fielding.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ def test_statcast_outs_above_average() -> None:
assert len(result.columns) == 17
assert len(result) > 0

def test_statcast_outs_above_average_view() -> None:
min_att = 50
pos = "of"
view = "Pitcher"
result: pd.DataFrame = statcast_outs_above_average(2019, pos, min_att, view)

assert result is not None
assert not result.empty

assert len(result.columns) == 17
assert len(result) > 0

def test_statcast_outfield_directional_oaa() -> None:
min_opp = 50
result: pd.DataFrame = statcast_outfield_directional_oaa(2019, min_opp)
Expand Down Expand Up @@ -76,3 +88,6 @@ def test_statcast_catcher_framing() -> None:
assert len(result) > 0
assert len(result.loc[result.n_called_pitches < min_called_p]) == 0


#test_statcast_outs_above_average_view()
test_statcast_outs_above_average()

0 comments on commit 4c207bf

Please sign in to comment.