Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/catcher_throwing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Statcast Catcher Throwing

## `catcher_throwing`

Function to get catcher throwing data from each stolen base attempt for a specific catcher. Based on Baseball Savant's [Catcher Throwing](https://baseballsavant.mlb.com/leaderboard/catcher-throwing).

**Examples**

```python
from baseball_stats_python import catcher_throwing

# Get Will Smith's catcher throwing data
catcher_throwing('669257')

# Get Will Smith's catcher throwing data in 2023
catcher_throwing('669257', season='2023')

# Get Will Smith's catcher throwing data in playoffs
catcher_throwing('669257', game_type=GameType.PLAYOFFS)
```

**Arguments**

| Argument | Data Type | Description |
| --------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| catcher_id (Required) | `str` | The MLBAM ID of the catcher. |
| game_type | `str` or `GameType` | The game type to filter by. Can be `R` for regular season, `PO` for playoffs, or `All` for all games. Check enum [GameType](../enums/statcast_leaderboard.py) |
| season | `str` | The season to filter by. The earliest season available is 2016. |

**Return**

A DataFrame with columns that related to the [Catcher Throwing](https://baseballsavant.mlb.com/leaderboard/catcher-throwing) leaderboard. The DataFrame will represent each stolen base attempt for a specific catcher which contains data like `pop_time`, `throw_type`, `r_primary_lead`, etc.
4 changes: 4 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
)
from src.baseball_stats_python.enums.minor import MinorGameType
from src.baseball_stats_python.enums.statcast import GameType, MlbTeam, Month
from src.baseball_stats_python.statcast.catcher_throwing import catcher_throwing


def example():
Expand Down Expand Up @@ -35,3 +36,6 @@ def mlbam_id_example():
# example()
# minor_example()
# mlbam_id_example()

df = catcher_throwing('669257', game_type=123)
print(df)
2 changes: 2 additions & 0 deletions src/baseball_stats_python/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .statcast.catcher_throwing import catcher_throwing
from .statcast.minor_statcast_search import (
minor_statcast_batter_search,
minor_statcast_pitcher_search,
Expand All @@ -18,4 +19,5 @@
'minor_statcast_pitcher_search',
'minor_statcast_batter_search',
'mlbam_id_search',
'catcher_throwing',
]
1 change: 1 addition & 0 deletions src/baseball_stats_python/constants/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEFAULT_SEASON = 2024
Empty file.
7 changes: 7 additions & 0 deletions src/baseball_stats_python/enums/statcast_leaderboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .enum_base import EnumBase


class GameType(EnumBase):
REGULAR_SEASON = 'R'
PLAYOFFS = 'PO'
ALL = 'All'
59 changes: 59 additions & 0 deletions src/baseball_stats_python/statcast/catcher_throwing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pandas as pd
import requests

from ..constants import DEFAULT_SEASON
from ..enums.statcast_leaderboard import GameType

session = requests.Session()

API_URL = 'https://baseballsavant.mlb.com/leaderboard/services/catcher-throwing'


def catcher_throwing(
catcher_id: str,
game_type: str | GameType = GameType.REGULAR_SEASON,
season: str = str(DEFAULT_SEASON),
) -> pd.DataFrame:
"""
Get catcher throwing data from each stolen base attempt for a specific catcher.
ref: https://baseballsavant.mlb.com/leaderboard/catcher-throwing

Args:
catcher_id (str): The MLBAM ID of the catcher. (Required)
game_type (str): The game type to filter by.
n (str): The number of results to return.
season (str): The season to filter by. The earliest season available is 2016.

Returns:
pd.DataFrame: A DataFrame containing the catcher throwing data.
"""

if not catcher_id:
raise ValueError('catcher_id is required')

if not isinstance(game_type, str) and not isinstance(game_type, GameType):
raise ValueError(f'Invalid type for game_type: {type(game_type)}')

if not GameType.has_value(game_type):
raise ValueError(f'Invalid game type: {game_type}')

if int(season) < 2016:
raise ValueError(
f'Invalid season: {season}, The earliest season available is 2016'
)

params = {
'gameType': game_type,
'season': season,
'n': 0,
}

response = session.get(f'{API_URL}/{catcher_id}', params=params)

if response.status_code == 200:
result = response.json()
return pd.DataFrame(result['data'])
else:
raise Exception(
f'Failed to fetch data: {response.status_code} - {response.text}'
)
2 changes: 1 addition & 1 deletion src/baseball_stats_python/statcast/mlbam_id_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

def mlbam_id_search(player_name: str, debug: bool = False) -> pd.DataFrame:
"""
Search for MLBAM ID(s) by player name.
Search for MLBAM ID(s) by player's name.

Args:
player_name (str): The player name to search for. (Required)
Expand Down
8 changes: 4 additions & 4 deletions src/baseball_stats_python/utils/statcast.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from ..constants import DEFAULT_SEASON
from ..enums.statcast import GameType, MlbTeam, Month

CURRENT_SEASON = 2024
START_SEASON = 2008
STATCAST_START_SEASON = 2015

ALL_SEASONS = [str(year) for year in range(START_SEASON, CURRENT_SEASON + 1)]
ALL_SEASONS = [str(year) for year in range(START_SEASON, DEFAULT_SEASON + 1)]
STATCAST_SEASONS = [
str(year) for year in range(STATCAST_START_SEASON, CURRENT_SEASON + 1)
str(year) for year in range(STATCAST_START_SEASON, DEFAULT_SEASON + 1)
]


Expand All @@ -20,7 +20,7 @@ def get_season_param_str(season: str | list[str]) -> str:
return '|'.join(season)

if season == '':
return str(CURRENT_SEASON)
return str(DEFAULT_SEASON)
if season == 'all':
return '|'.join(ALL_SEASONS)
if season == 'statcast':
Expand Down
25 changes: 25 additions & 0 deletions tests/statcast/test_catcher_throwing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from baseball_stats_python.statcast.catcher_throwing import catcher_throwing


def test_catcher_throwing_invalid():
with pytest.raises(ValueError) as e:
catcher_throwing('')
assert str(e.value) == 'catcher_id is required'

with pytest.raises(ValueError) as e:
catcher_throwing('669257', 'invalid')
assert str(e.value) == 'Invalid game type: invalid'

with pytest.raises(ValueError) as e:
catcher_throwing('669257', 123)
assert str(e.value) == f'Invalid type for game_type: {int}'

with pytest.raises(ValueError) as e:
catcher_throwing('669257', season='2015')
assert str(e.value) == 'Invalid season: 2015, The earliest season available is 2016'

with pytest.raises(ValueError) as e:
catcher_throwing('669257', game_type='RRR')
assert str(e.value) == 'Invalid game type: RRR'
4 changes: 2 additions & 2 deletions tests/utils/test_statcast.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import pytest

from baseball_stats_python.constants import DEFAULT_SEASON
from baseball_stats_python.enums.statcast import GameType, MlbTeam, Month
from baseball_stats_python.utils.statcast import (
CURRENT_SEASON,
get_game_type_param_str,
get_month_param_str,
get_season_param_str,
Expand All @@ -15,7 +15,7 @@
def test_get_season_param_str():
assert get_season_param_str('2024') == '2024'
assert get_season_param_str(['2024', '2023']) == '2024|2023'
assert get_season_param_str('') == str(CURRENT_SEASON)
assert get_season_param_str('') == str(DEFAULT_SEASON)


def test_get_season_param_str_invalid():
Expand Down
Loading