Metadata-Version: 2.4
Name: polars-baseball
Version: 0.6.1
Summary: Retrieve baseball data in Python
Author-email: Nick <nick009965@gmail.com>
Maintainer-email: Nick <nick009965@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/nicko4o/polars-baseball
Project-URL: Repository, https://github.com/nicko4o/polars-baseball
Project-URL: Issues, https://github.com/nicko4o/polars-baseball/issues
Project-URL: Changelog, https://github.com/nicko4o/polars-baseball/blob/main/CHANGELOG.md
Keywords: baseball,mlb,statcast,baseball savant,fangraphs,retrosheet,sabermetrics,sports analytics,baseball analytics,python baseball,polars,pybaseball alternative
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: lxml>=4.2.1
Requires-Dist: pyarrow>=1.0.1
Requires-Dist: tqdm>=4.50.0
Requires-Dist: curl_cffi>=0.10.0
Requires-Dist: polars>=1.0.0
Requires-Dist: httpx
Requires-Dist: typing_extensions>=4.0.0
Provides-Extra: plot
Requires-Dist: hvplot>=0.9.0; extra == "plot"
Requires-Dist: altair>=5.0.0; extra == "plot"
Provides-Extra: test
Requires-Dist: pytest<8.4,>=7.4; extra == "test"
Requires-Dist: mypy<1.15,>=1.8; extra == "test"
Requires-Dist: pytest-cov>=2.10.1; extra == "test"
Requires-Dist: pytest-xdist>=2.1.0; extra == "test"
Requires-Dist: types-requests>=2.18.1; extra == "test"
Requires-Dist: ruff>=0.1.0; extra == "test"
Requires-Dist: types-tqdm; extra == "test"
Requires-Dist: lxml-stubs; extra == "test"
Requires-Dist: types-setuptools; extra == "test"
Requires-Dist: pytest-asyncio; extra == "test"
Dynamic: license-file

# polars-baseball

[![PyPI version](https://img.shields.io/pypi/v/polars-baseball.svg)](https://pypi.org/project/polars-baseball/)
[![Python versions](https://img.shields.io/pypi/pyversions/polars-baseball.svg)](https://pypi.org/project/polars-baseball/)
[![CI](https://github.com/nicko4o/polars-baseball/actions/workflows/pytest.yml/badge.svg)](https://github.com/nicko4o/polars-baseball/actions)
[![Codecov](https://img.shields.io/codecov/c/github/nicko4o/polars-baseball)](https://codecov.io/gh/nicko4o/polars-baseball)
[![License](https://img.shields.io/pypi/l/polars-baseball.svg)](https://github.com/nicko4o/polars-baseball/blob/main/LICENSE)
[![Downloads](https://img.shields.io/pypi/dm/polars-baseball.svg)](https://pypi.org/project/polars-baseball/)

Languages: [English](README.md) | [Traditional Chinese](README.zh-TW.md)

`polars-baseball` is **The unified Polars-native baseball data SDK**: a typed, async-first Python
library for retrieving MLB and baseball analytics data from Statcast, Baseball Savant, FanGraphs,
Baseball Reference, Lahman, Retrosheet, and the MLB Stats API.

If you searched for `python baseball data`, `python statcast`, `fangraphs python`,
`baseball savant api`, `pybaseball alternative`, or `polars dataframe baseball`, this project is
built for the workflow where data should land directly in `polars.DataFrame` instead of going
through pandas first.

## Why use polars-baseball instead of pybaseball?

`pybaseball` is useful and established. `polars-baseball` is aimed at a different execution model:
async data ingestion, native Polars output, and one consistent entry point across multiple baseball
data providers.

| Feature | pybaseball | polars-baseball |
| --- | --- | --- |
| Polars native | No | Yes |
| Async data fetching | No | Yes |
| Statcast / Baseball Savant | Yes | Yes |
| FanGraphs | Yes | Yes |
| MLB Stats API | Limited | Yes |
| Lahman / Retrosheet workflows | Partial | Yes |
| Built-in cache | Partial | Yes |
| Typed public API | Partial | Yes |

Typical pandas-first workflow:

```text
pybaseball -> pandas -> convert to Polars -> analysis
```

`polars-baseball` workflow:

```text
polars-baseball -> Polars -> analysis
```

## Key Features

- **Polars-native data**: Public data-fetching APIs return `polars.DataFrame` unless an API
  reference explicitly documents a non-tabular contract.
- **Async-first engine**: Data-fetching APIs are `async def` and can be composed with your own
  async workflows.
- **Multiple providers**: Statcast, Baseball Savant, FanGraphs, Baseball Reference, Lahman,
  Retrosheet, MLB Stats API, and player ID workflows.
- **Built-in cache**: Repeated network requests are cached as Parquet files for large workflows.
- **Service-ready context**: `BaseballContext` lets long-running apps control HTTP and cache
  resources explicitly.

## Installation

```bash
pip install polars-baseball
```

For local development:

```bash
git clone https://github.com/nicko4o/polars-baseball
cd polars-baseball
uv sync --all-extras
```

To run visualization examples:

```bash
pip install "polars-baseball[plot]"
```

## Quick Start

### Statcast pitch-level data

```python
import asyncio

import polars_baseball as pb


async def main() -> None:
    df = await pb.statcast(start_date="2024-05-06", end_date="2024-05-06")
    print(df.head(5))


if __name__ == "__main__":
    asyncio.run(main())
```

### Aggregate directly with Polars

```python
import asyncio

import polars as pl
import polars_baseball as pb


async def main() -> None:
    df = await pb.statcast_pitcher(
        start_date="2024-05-06",
        end_date="2024-05-06",
        player_id=506433,
    )
    summary = df.group_by("pitch_type").agg(
        pl.col("release_speed").mean().alias("mean_speed"),
        pl.len().alias("pitch_count"),
    )
    print(summary.sort("pitch_count", descending=True))


if __name__ == "__main__":
    asyncio.run(main())
```

### FanGraphs leaderboard

```python
import asyncio

import polars_baseball as pb


async def main() -> None:
    df = await pb.fangraphs.batting(
        start_season=2024,
        end_season=2024,
        qual=100,
        max_results=20,
    )
    print(df.head(10))


if __name__ == "__main__":
    asyncio.run(main())
```

## Examples

Runnable examples live in [`examples/`](examples/):

- [`examples/statcast_pitch_mix.py`](examples/statcast_pitch_mix.py): Statcast pitch mix with Polars.
- [`examples/fangraphs_leaderboard.py`](examples/fangraphs_leaderboard.py): FanGraphs batting leaderboard.
- [`examples/mlb_schedule.py`](examples/mlb_schedule.py): MLB Stats API schedule query.
- [`examples/benchmark_statcast.py`](examples/benchmark_statcast.py): Conservative Statcast timing and memory benchmark.

## Benchmarking

Do not trust performance claims without a reproducible command. Start with:

```bash
python examples/benchmark_statcast.py --start-date 2024-04-01 --end-date 2024-04-07
```

The script reports row count, column count, wall time, and Python allocation peak measured by
`tracemalloc`. Use the same date range, cache state, Python version, and machine when comparing
against pandas-first workflows.

## Web Services & Concurrency

Calling package functions without `context` uses the implicit package-level `BaseballContext`.
That default context is convenient for scripts, but long-running concurrent services should manage
their own context and pass it into every API call.

```python
from contextlib import asynccontextmanager

from fastapi import FastAPI

import polars_baseball as pb


@asynccontextmanager
async def lifespan(app: FastAPI):
    async with pb.BaseballContext() as context:
        app.state.pb_context = context
        yield


app = FastAPI(lifespan=lifespan)


@app.get("/statcast")
async def get_statcast() -> dict[str, int]:
    df = await pb.statcast(
        start_date="2026-06-01",
        end_date="2026-06-02",
        context=app.state.pb_context,
    )
    return {"rows": df.height}
```

## API Namespace Policy

The package root (`import polars_baseball as pb`) exposes core convenience APIs and provider
namespaces. Use `pb.fangraphs`, `pb.savant`, and `pb.mlb` for provider-specific workflows.
Lahman, Retrosheet, Baseball Reference, and player ID workflows remain available from the package root.

Modules prefixed with `_`, including `_schemas`, are internal implementation details and are not part
of the compatibility contract.

## Documentation

- [Documentation](docs/index.md)
- [API Use-Case Index](docs/api_index.md): choose the right API by task.
- [Traditional Chinese documentation](docs/zh-tw/)

## Showcase

Projects using `polars-baseball`:

- MLB dashboard workflows
- Chinese baseball website data jobs
- Threads bot baseball data pipelines

## Contributing

See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for development workflow and architecture notes.

## Author

Created and maintained by Nick.
