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
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11.2
32 changes: 32 additions & 0 deletions docs/runner_extra_bases_taken.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Statcast Runner Extra Bases Taken

## `runner_extra_bases_taken`

Function to get extra base taken data from each advanced opportunity for a specific runner. Based on Baseball Savant's [Runner Extra Bases Taken](https://baseballsavant.mlb.com/leaderboard/baserunning).

**Examples**

```python
from baseball_stats_python import runner_extra_bases_taken

# Get Corbin Carroll's runner extra bases taken data
runner_extra_bases_taken('682998')

# Get Corbin Carroll's runner extra bases taken data in 2023
runner_extra_bases_taken('682998', season='2023')

# Get Corbin Carroll's runner extra bases taken data in playoffs
runner_extra_bases_taken('682998', game_type=GameType.PLAYOFFS)
```

**Arguments**

| Argument | Data Type | Description |
| -------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| runner_id (Required) | `str` | The MLBAM ID of the runner. |
| 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 [Runner Extra Bases Taken](https://baseballsavant.mlb.com/leaderboard/baserunning) leaderboard. The DataFrame will represent each advanced opportunity for a specific runner which contains data like `fielder_runs`, `runner_runs`, `base_out_text`, etc.
4 changes: 4 additions & 0 deletions src/baseball_stats_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
minor_statcast_search,
)
from .statcast.mlbam_id_search import mlbam_id_search
from .statcast.runner_basestealing import runner_basestealing
from .statcast.runner_extra_bases_taken import runner_extra_bases_taken
from .statcast.statcast_search import (
statcast_batter_search,
statcast_pitcher_search,
Expand All @@ -20,4 +22,6 @@
'minor_statcast_batter_search',
'mlbam_id_search',
'catcher_throwing',
'runner_basestealing',
'runner_extra_bases_taken',
]
59 changes: 59 additions & 0 deletions src/baseball_stats_python/statcast/runner_extra_bases_taken.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/baserunning'


def runner_extra_bases_taken(
runner_id: str,
game_type: str | GameType = GameType.REGULAR_SEASON,
season: str = str(DEFAULT_SEASON),
) -> pd.DataFrame:
"""
Get extra base taken data from each advanced opportunity for a specific runner.
ref: https://baseballsavant.mlb.com/leaderboard/baserunning

Args:
runner_id (str): The MLBAM ID of the runner. (Required)
game_type (str | GameType): The game type to filter by. Default is "Regular".
season (str): The season to filter by. The earliest season available is 2016.
Returns:
pd.DataFrame: A DataFrame containing the baserunning data.
"""

if not runner_id:
raise ValueError('runner_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 = {
'game_type': game_type,
'season_start': season,
'season_end': season,
'n': 0,
}

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

if response.status_code == 200:
result = response.json()
df = pd.DataFrame(result['data'])
return df
else:
raise Exception(
f'Failed to fetch data: {response.status_code} - {response.text}'
)
Loading