polars-baseball 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- polars_baseball-0.1.0/LICENSE +21 -0
- polars_baseball-0.1.0/MANIFEST.in +4 -0
- polars_baseball-0.1.0/PKG-INFO +255 -0
- polars_baseball-0.1.0/README.md +210 -0
- polars_baseball-0.1.0/polars_baseball/__init__.py +108 -0
- polars_baseball-0.1.0/polars_baseball/_cache.py +442 -0
- polars_baseball-0.1.0/polars_baseball/_client.py +216 -0
- polars_baseball-0.1.0/polars_baseball/_config.py +58 -0
- polars_baseball-0.1.0/polars_baseball/_encoding.py +11 -0
- polars_baseball-0.1.0/polars_baseball/_json_types.py +3 -0
- polars_baseball-0.1.0/polars_baseball/_json_utils.py +20 -0
- polars_baseball-0.1.0/polars_baseball/_schema_utils.py +27 -0
- polars_baseball-0.1.0/polars_baseball/_schemas/__init__.py +1 -0
- polars_baseball-0.1.0/polars_baseball/_schemas/mlb.py +331 -0
- polars_baseball-0.1.0/polars_baseball/_schemas/retrosheet.py +202 -0
- polars_baseball-0.1.0/polars_baseball/_season.py +112 -0
- polars_baseball-0.1.0/polars_baseball/_validation.py +35 -0
- polars_baseball-0.1.0/polars_baseball/apis/_leaderboard_registry.py +179 -0
- polars_baseball-0.1.0/polars_baseball/apis/bref.py +82 -0
- polars_baseball-0.1.0/polars_baseball/apis/fangraphs.py +250 -0
- polars_baseball-0.1.0/polars_baseball/apis/lahman.py +210 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/__init__.py +42 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/_contracts.py +294 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/draft.py +80 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/game.py +323 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/people.py +124 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/roster.py +81 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/schedule.py +144 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/stats.py +310 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/taxonomy.py +176 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/team_lookup.py +37 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/transactions.py +92 -0
- polars_baseball-0.1.0/polars_baseball/apis/mlb/venues.py +80 -0
- polars_baseball-0.1.0/polars_baseball/apis/playerid.py +281 -0
- polars_baseball-0.1.0/polars_baseball/apis/retrosheet.py +260 -0
- polars_baseball-0.1.0/polars_baseball/apis/savant_fielding_running.py +205 -0
- polars_baseball-0.1.0/polars_baseball/apis/savant_gamefeed.py +87 -0
- polars_baseball-0.1.0/polars_baseball/apis/savant_leaderboards.py +382 -0
- polars_baseball-0.1.0/polars_baseball/apis/standings.py +121 -0
- polars_baseball-0.1.0/polars_baseball/apis/statcast.py +250 -0
- polars_baseball-0.1.0/polars_baseball/apis/teamid.py +43 -0
- polars_baseball-0.1.0/polars_baseball/apis/top_prospects.py +161 -0
- polars_baseball-0.1.0/polars_baseball/context.py +85 -0
- polars_baseball-0.1.0/polars_baseball/data/fangraphs_teams.csv +2936 -0
- polars_baseball-0.1.0/polars_baseball/data/statcast_valid_dates.json +21 -0
- polars_baseball-0.1.0/polars_baseball/enums/__init__.py +13 -0
- polars_baseball-0.1.0/polars_baseball/enums/enum_base.py +45 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/__init__.py +58 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/batting_data_enum.py +394 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/fangraphs_stats_base.py +55 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/fangraphs_stats_category.py +12 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/fielding_data_enum.py +100 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/league.py +19 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/month.py +15 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/pitching_data_enum.py +463 -0
- polars_baseball-0.1.0/polars_baseball/enums/fangraphs/positions.py +20 -0
- polars_baseball-0.1.0/polars_baseball/enums/pitch.py +44 -0
- polars_baseball-0.1.0/polars_baseball/enums/player.py +15 -0
- polars_baseball-0.1.0/polars_baseball/enums/position.py +72 -0
- polars_baseball-0.1.0/polars_baseball/enums/savant.py +14 -0
- polars_baseball-0.1.0/polars_baseball/exceptions.py +54 -0
- polars_baseball-0.1.0/polars_baseball/gateways/__init__.py +13 -0
- polars_baseball-0.1.0/polars_baseball/gateways/bref.py +130 -0
- polars_baseball-0.1.0/polars_baseball/gateways/compiled.py +202 -0
- polars_baseball-0.1.0/polars_baseball/gateways/mlb.py +43 -0
- polars_baseball-0.1.0/polars_baseball/gateways/savant.py +160 -0
- polars_baseball-0.1.0/polars_baseball/parsers/__init__.py +46 -0
- polars_baseball-0.1.0/polars_baseball/parsers/_strategy.py +136 -0
- polars_baseball-0.1.0/polars_baseball/parsers/base.py +10 -0
- polars_baseball-0.1.0/polars_baseball/parsers/bref.py +348 -0
- polars_baseball-0.1.0/polars_baseball/parsers/bref_schema.py +190 -0
- polars_baseball-0.1.0/polars_baseball/parsers/bref_standard_strategy.py +134 -0
- polars_baseball-0.1.0/polars_baseball/parsers/fangraphs.py +79 -0
- polars_baseball-0.1.0/polars_baseball/parsers/fangraphs_next_data_strategy.py +43 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/__init__.py +84 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/draft.py +32 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/game.py +147 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/html.py +84 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/people.py +58 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/roster.py +21 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/schedule.py +68 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/stats.py +107 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/taxonomy.py +67 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/transactions.py +36 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/types.py +300 -0
- polars_baseball-0.1.0/polars_baseball/parsers/mlb/venues.py +13 -0
- polars_baseball-0.1.0/polars_baseball/parsers/pipeline.py +135 -0
- polars_baseball-0.1.0/polars_baseball/parsers/savant.py +35 -0
- polars_baseball-0.1.0/polars_baseball/parsers/savant_gamefeed.py +222 -0
- polars_baseball-0.1.0/polars_baseball/parsers/savant_leaderboard_strategy.py +111 -0
- polars_baseball-0.1.0/polars_baseball/parsers/savant_schema.py +20 -0
- polars_baseball-0.1.0/polars_baseball.egg-info/PKG-INFO +255 -0
- polars_baseball-0.1.0/polars_baseball.egg-info/SOURCES.txt +96 -0
- polars_baseball-0.1.0/polars_baseball.egg-info/dependency_links.txt +1 -0
- polars_baseball-0.1.0/polars_baseball.egg-info/requires.txt +23 -0
- polars_baseball-0.1.0/polars_baseball.egg-info/top_level.txt +1 -0
- polars_baseball-0.1.0/pyproject.toml +80 -0
- polars_baseball-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polars-baseball
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Retrieve baseball data in Python
|
|
5
|
+
Author: Nick
|
|
6
|
+
Maintainer: Nick
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/nicko4o/polars-baseball
|
|
9
|
+
Project-URL: Repository, https://github.com/nicko4o/polars-baseball
|
|
10
|
+
Project-URL: Issues, https://github.com/nicko4o/polars-baseball/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/nicko4o/polars-baseball/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: baseball,sabermetrics,data,statistics,statcast,web scraping
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: lxml>=4.2.1
|
|
24
|
+
Requires-Dist: pyarrow>=1.0.1
|
|
25
|
+
Requires-Dist: tqdm>=4.50.0
|
|
26
|
+
Requires-Dist: curl_cffi>=0.10.0
|
|
27
|
+
Requires-Dist: polars>=1.0.0
|
|
28
|
+
Requires-Dist: httpx
|
|
29
|
+
Requires-Dist: typing_extensions>=4.0.0
|
|
30
|
+
Provides-Extra: plot
|
|
31
|
+
Requires-Dist: hvplot>=0.9.0; extra == "plot"
|
|
32
|
+
Requires-Dist: altair>=5.0.0; extra == "plot"
|
|
33
|
+
Provides-Extra: test
|
|
34
|
+
Requires-Dist: pytest<8.4,>=7.4; extra == "test"
|
|
35
|
+
Requires-Dist: mypy<1.15,>=1.8; extra == "test"
|
|
36
|
+
Requires-Dist: pytest-cov>=2.10.1; extra == "test"
|
|
37
|
+
Requires-Dist: pytest-xdist>=2.1.0; extra == "test"
|
|
38
|
+
Requires-Dist: types-requests>=2.18.1; extra == "test"
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == "test"
|
|
40
|
+
Requires-Dist: types-tqdm; extra == "test"
|
|
41
|
+
Requires-Dist: lxml-stubs; extra == "test"
|
|
42
|
+
Requires-Dist: types-setuptools; extra == "test"
|
|
43
|
+
Requires-Dist: pytest-asyncio; extra == "test"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# polars-baseball
|
|
47
|
+
|
|
48
|
+
Languages: [English](README.md) | [Traditional Chinese](README.zh-TW.md)
|
|
49
|
+
|
|
50
|
+
`polars-baseball` is a modern asynchronous Python library for retrieving baseball data. It is built around
|
|
51
|
+
Polars, where most public data APIs return `polars.DataFrame` objects (with documented exceptions such as `standings()`
|
|
52
|
+
returning other shapes), and supports Statcast, Baseball Reference, FanGraphs, Lahman,
|
|
53
|
+
Retrosheet, MLB Stats API, and player ID workflows.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Key Features
|
|
58
|
+
|
|
59
|
+
- **Polars Core**: Most public APIs return native `polars.DataFrame` objects (with `standings()` returning `list[polars.DataFrame]`) for filtering, aggregation, and export.
|
|
60
|
+
- **Async-First Engine**: Data-fetching APIs are asynchronous and should be called with `await` or `asyncio.run()`.
|
|
61
|
+
- **Flexible Concurrency**: Support for custom context configuration to isolate resources in multi-threaded/loop environments.
|
|
62
|
+
- **Automatic Cache**: Built-in file caching reduces repeated network requests for large workflows.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install polars-baseball
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For local development:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/nicko4o/polars-baseball
|
|
76
|
+
cd polars-baseball
|
|
77
|
+
uv sync --all-extras
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
To run the visualization examples, install the optional example dependencies:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pip install "polars-baseball[plot]"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
### 1. Statcast Queries
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import asyncio
|
|
94
|
+
|
|
95
|
+
import polars_baseball as pb
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def main() -> None:
|
|
99
|
+
df = await pb.statcast(start_dt="2024-05-06", end_dt="2024-05-06")
|
|
100
|
+
print(df.head(5))
|
|
101
|
+
|
|
102
|
+
darvish_df = await pb.statcast_pitcher(
|
|
103
|
+
start_dt="2024-05-06",
|
|
104
|
+
end_dt="2024-05-06",
|
|
105
|
+
player_id=450314,
|
|
106
|
+
)
|
|
107
|
+
print(darvish_df.head(5))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
asyncio.run(main())
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2. Aggregate with Polars
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import asyncio
|
|
118
|
+
|
|
119
|
+
import polars as pl
|
|
120
|
+
import polars_baseball as pb
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
async def main() -> None:
|
|
124
|
+
darvish_df = await pb.statcast_pitcher(
|
|
125
|
+
start_dt="2024-05-06",
|
|
126
|
+
end_dt="2024-05-06",
|
|
127
|
+
player_id=450314,
|
|
128
|
+
)
|
|
129
|
+
summary = (
|
|
130
|
+
darvish_df
|
|
131
|
+
.group_by("pitch_type")
|
|
132
|
+
.agg(pl.col("release_speed").mean().alias("mean_speed"))
|
|
133
|
+
)
|
|
134
|
+
print(summary)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
asyncio.run(main())
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 3. Top Prospects
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
import asyncio
|
|
145
|
+
|
|
146
|
+
import polars_baseball as pb
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async def main() -> None:
|
|
150
|
+
prospects = await pb.top_prospects(team_name="mets")
|
|
151
|
+
print(prospects.head(5))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
if __name__ == "__main__":
|
|
155
|
+
asyncio.run(main())
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 4. Interactive Data Visualization
|
|
159
|
+
|
|
160
|
+
`polars-baseball` does not provide a plotting API. This example passes the returned `polars.DataFrame` to hvPlot.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import asyncio
|
|
164
|
+
|
|
165
|
+
import hvplot.polars # noqa: F401
|
|
166
|
+
import polars_baseball as pb
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
async def main() -> None:
|
|
170
|
+
df = await pb.statcast(start_dt="2024-05-06", end_dt="2024-05-06")
|
|
171
|
+
chart = (
|
|
172
|
+
df
|
|
173
|
+
.filter(df["hc_x"].is_not_null() & df["hc_y"].is_not_null())
|
|
174
|
+
.plot.scatter(
|
|
175
|
+
x="hc_x",
|
|
176
|
+
y="hc_y",
|
|
177
|
+
by="events",
|
|
178
|
+
invert_y=True,
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
print(chart)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
asyncio.run(main())
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Web Services & Concurrency (FastAPI, Gunicorn, Celery)
|
|
189
|
+
|
|
190
|
+
By default, calling package functions without a `context` parameter will fallback to an implicit package-level global singleton `BaseballContext`. **This global default context is not guaranteed to be thread-safe or loop-safe in long-running concurrent environments.**
|
|
191
|
+
|
|
192
|
+
When deploying `polars-baseball` inside concurrent web services (such as FastAPI, Gunicorn, or Celery workers), you **must** explicitly manage the lifespan of `BaseballContext` and pass it to all API calls.
|
|
193
|
+
|
|
194
|
+
### FastAPI lifespan Example
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from contextlib import asynccontextmanager
|
|
198
|
+
from fastapi import FastAPI
|
|
199
|
+
import polars_baseball as pb
|
|
200
|
+
|
|
201
|
+
@asynccontextmanager
|
|
202
|
+
async def lifespan(app: FastAPI):
|
|
203
|
+
# Initialize a dedicated context bound to the app's event loop
|
|
204
|
+
app.state.pb_context = pb.BaseballContext()
|
|
205
|
+
try:
|
|
206
|
+
yield
|
|
207
|
+
finally:
|
|
208
|
+
# Properly clean up HTTP connections
|
|
209
|
+
await app.state.pb_context.http.close()
|
|
210
|
+
|
|
211
|
+
app = FastAPI(lifespan=lifespan)
|
|
212
|
+
|
|
213
|
+
@app.get("/statcast")
|
|
214
|
+
async def get_statcast():
|
|
215
|
+
df = await pb.statcast(
|
|
216
|
+
start_dt="2026-06-01",
|
|
217
|
+
end_dt="2026-06-02",
|
|
218
|
+
context=app.state.pb_context,
|
|
219
|
+
)
|
|
220
|
+
return df.to_dicts()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## API Namespace Policy
|
|
226
|
+
|
|
227
|
+
The package root (`import polars_baseball as pb`) exposes only the stable, commonly used public API. Provider-specific and advanced functions remain available from `polars_baseball.apis.*`.
|
|
228
|
+
|
|
229
|
+
Modules prefixed with `_`, including `_schemas`, are internal implementation details and are not part of the compatibility contract.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Documentation
|
|
234
|
+
|
|
235
|
+
- [English documentation](docs/)
|
|
236
|
+
- [Traditional Chinese documentation](docs/zh-tw/)
|
|
237
|
+
- [Caching Guide](docs/caching.md)
|
|
238
|
+
- [Data Visualization Guide](docs/plotting.md)
|
|
239
|
+
- [Statcast API](docs/statcast.md)
|
|
240
|
+
- [Player ID Lookup](docs/playerid_lookup.md)
|
|
241
|
+
- [MLB Stats API](docs/mlb_api.md)
|
|
242
|
+
- [Savant Gamefeed API](docs/savant_gamefeed.md)
|
|
243
|
+
- [Prospect Rankings](docs/prospect_rankings.md)
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Contributing
|
|
248
|
+
|
|
249
|
+
See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the development workflow and architecture notes.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Author
|
|
254
|
+
|
|
255
|
+
Created and maintained by Nick.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# polars-baseball
|
|
2
|
+
|
|
3
|
+
Languages: [English](README.md) | [Traditional Chinese](README.zh-TW.md)
|
|
4
|
+
|
|
5
|
+
`polars-baseball` is a modern asynchronous Python library for retrieving baseball data. It is built around
|
|
6
|
+
Polars, where most public data APIs return `polars.DataFrame` objects (with documented exceptions such as `standings()`
|
|
7
|
+
returning other shapes), and supports Statcast, Baseball Reference, FanGraphs, Lahman,
|
|
8
|
+
Retrosheet, MLB Stats API, and player ID workflows.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Key Features
|
|
13
|
+
|
|
14
|
+
- **Polars Core**: Most public APIs return native `polars.DataFrame` objects (with `standings()` returning `list[polars.DataFrame]`) for filtering, aggregation, and export.
|
|
15
|
+
- **Async-First Engine**: Data-fetching APIs are asynchronous and should be called with `await` or `asyncio.run()`.
|
|
16
|
+
- **Flexible Concurrency**: Support for custom context configuration to isolate resources in multi-threaded/loop environments.
|
|
17
|
+
- **Automatic Cache**: Built-in file caching reduces repeated network requests for large workflows.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install polars-baseball
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For local development:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/nicko4o/polars-baseball
|
|
31
|
+
cd polars-baseball
|
|
32
|
+
uv sync --all-extras
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To run the visualization examples, install the optional example dependencies:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install "polars-baseball[plot]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
### 1. Statcast Queries
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import asyncio
|
|
49
|
+
|
|
50
|
+
import polars_baseball as pb
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def main() -> None:
|
|
54
|
+
df = await pb.statcast(start_dt="2024-05-06", end_dt="2024-05-06")
|
|
55
|
+
print(df.head(5))
|
|
56
|
+
|
|
57
|
+
darvish_df = await pb.statcast_pitcher(
|
|
58
|
+
start_dt="2024-05-06",
|
|
59
|
+
end_dt="2024-05-06",
|
|
60
|
+
player_id=450314,
|
|
61
|
+
)
|
|
62
|
+
print(darvish_df.head(5))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
asyncio.run(main())
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Aggregate with Polars
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import asyncio
|
|
73
|
+
|
|
74
|
+
import polars as pl
|
|
75
|
+
import polars_baseball as pb
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def main() -> None:
|
|
79
|
+
darvish_df = await pb.statcast_pitcher(
|
|
80
|
+
start_dt="2024-05-06",
|
|
81
|
+
end_dt="2024-05-06",
|
|
82
|
+
player_id=450314,
|
|
83
|
+
)
|
|
84
|
+
summary = (
|
|
85
|
+
darvish_df
|
|
86
|
+
.group_by("pitch_type")
|
|
87
|
+
.agg(pl.col("release_speed").mean().alias("mean_speed"))
|
|
88
|
+
)
|
|
89
|
+
print(summary)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
asyncio.run(main())
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 3. Top Prospects
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import asyncio
|
|
100
|
+
|
|
101
|
+
import polars_baseball as pb
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def main() -> None:
|
|
105
|
+
prospects = await pb.top_prospects(team_name="mets")
|
|
106
|
+
print(prospects.head(5))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
asyncio.run(main())
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 4. Interactive Data Visualization
|
|
114
|
+
|
|
115
|
+
`polars-baseball` does not provide a plotting API. This example passes the returned `polars.DataFrame` to hvPlot.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import asyncio
|
|
119
|
+
|
|
120
|
+
import hvplot.polars # noqa: F401
|
|
121
|
+
import polars_baseball as pb
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
async def main() -> None:
|
|
125
|
+
df = await pb.statcast(start_dt="2024-05-06", end_dt="2024-05-06")
|
|
126
|
+
chart = (
|
|
127
|
+
df
|
|
128
|
+
.filter(df["hc_x"].is_not_null() & df["hc_y"].is_not_null())
|
|
129
|
+
.plot.scatter(
|
|
130
|
+
x="hc_x",
|
|
131
|
+
y="hc_y",
|
|
132
|
+
by="events",
|
|
133
|
+
invert_y=True,
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
print(chart)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
asyncio.run(main())
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Web Services & Concurrency (FastAPI, Gunicorn, Celery)
|
|
144
|
+
|
|
145
|
+
By default, calling package functions without a `context` parameter will fallback to an implicit package-level global singleton `BaseballContext`. **This global default context is not guaranteed to be thread-safe or loop-safe in long-running concurrent environments.**
|
|
146
|
+
|
|
147
|
+
When deploying `polars-baseball` inside concurrent web services (such as FastAPI, Gunicorn, or Celery workers), you **must** explicitly manage the lifespan of `BaseballContext` and pass it to all API calls.
|
|
148
|
+
|
|
149
|
+
### FastAPI lifespan Example
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from contextlib import asynccontextmanager
|
|
153
|
+
from fastapi import FastAPI
|
|
154
|
+
import polars_baseball as pb
|
|
155
|
+
|
|
156
|
+
@asynccontextmanager
|
|
157
|
+
async def lifespan(app: FastAPI):
|
|
158
|
+
# Initialize a dedicated context bound to the app's event loop
|
|
159
|
+
app.state.pb_context = pb.BaseballContext()
|
|
160
|
+
try:
|
|
161
|
+
yield
|
|
162
|
+
finally:
|
|
163
|
+
# Properly clean up HTTP connections
|
|
164
|
+
await app.state.pb_context.http.close()
|
|
165
|
+
|
|
166
|
+
app = FastAPI(lifespan=lifespan)
|
|
167
|
+
|
|
168
|
+
@app.get("/statcast")
|
|
169
|
+
async def get_statcast():
|
|
170
|
+
df = await pb.statcast(
|
|
171
|
+
start_dt="2026-06-01",
|
|
172
|
+
end_dt="2026-06-02",
|
|
173
|
+
context=app.state.pb_context,
|
|
174
|
+
)
|
|
175
|
+
return df.to_dicts()
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## API Namespace Policy
|
|
181
|
+
|
|
182
|
+
The package root (`import polars_baseball as pb`) exposes only the stable, commonly used public API. Provider-specific and advanced functions remain available from `polars_baseball.apis.*`.
|
|
183
|
+
|
|
184
|
+
Modules prefixed with `_`, including `_schemas`, are internal implementation details and are not part of the compatibility contract.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Documentation
|
|
189
|
+
|
|
190
|
+
- [English documentation](docs/)
|
|
191
|
+
- [Traditional Chinese documentation](docs/zh-tw/)
|
|
192
|
+
- [Caching Guide](docs/caching.md)
|
|
193
|
+
- [Data Visualization Guide](docs/plotting.md)
|
|
194
|
+
- [Statcast API](docs/statcast.md)
|
|
195
|
+
- [Player ID Lookup](docs/playerid_lookup.md)
|
|
196
|
+
- [MLB Stats API](docs/mlb_api.md)
|
|
197
|
+
- [Savant Gamefeed API](docs/savant_gamefeed.md)
|
|
198
|
+
- [Prospect Rankings](docs/prospect_rankings.md)
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Contributing
|
|
203
|
+
|
|
204
|
+
See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the development workflow and architecture notes.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Author
|
|
209
|
+
|
|
210
|
+
Created and maintained by Nick.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import logging as _logging
|
|
2
|
+
|
|
3
|
+
from polars_baseball._cache import configure_cache
|
|
4
|
+
from polars_baseball.apis.fangraphs import FanGraphsRequest, fg_data
|
|
5
|
+
from polars_baseball.apis.mlb import (
|
|
6
|
+
mlb_divisions,
|
|
7
|
+
mlb_draft,
|
|
8
|
+
mlb_game_boxscore,
|
|
9
|
+
mlb_game_boxscore_stats,
|
|
10
|
+
mlb_game_feed_live,
|
|
11
|
+
mlb_game_linescore,
|
|
12
|
+
mlb_game_play_by_play,
|
|
13
|
+
mlb_game_win_probability,
|
|
14
|
+
mlb_leagues,
|
|
15
|
+
mlb_people,
|
|
16
|
+
mlb_people_awards,
|
|
17
|
+
mlb_pitch_arsenal,
|
|
18
|
+
mlb_player_stats,
|
|
19
|
+
mlb_postseason_schedule,
|
|
20
|
+
mlb_roster,
|
|
21
|
+
mlb_schedule,
|
|
22
|
+
mlb_stat_leaders,
|
|
23
|
+
mlb_team_stats,
|
|
24
|
+
mlb_teams,
|
|
25
|
+
mlb_transactions,
|
|
26
|
+
mlb_venues,
|
|
27
|
+
)
|
|
28
|
+
from polars_baseball.apis.playerid import playerid_lookup
|
|
29
|
+
from polars_baseball.apis.savant_fielding_running import (
|
|
30
|
+
statcast_arm_strength,
|
|
31
|
+
statcast_baserunning_run_value,
|
|
32
|
+
statcast_catcher_stance,
|
|
33
|
+
statcast_catcher_throwing,
|
|
34
|
+
)
|
|
35
|
+
from polars_baseball.apis.savant_gamefeed import (
|
|
36
|
+
savant_gamefeed_exit_velocity,
|
|
37
|
+
savant_gamefeed_exit_velocity_many,
|
|
38
|
+
savant_gamefeed_pitch_data,
|
|
39
|
+
savant_gamefeed_pitch_data_many,
|
|
40
|
+
)
|
|
41
|
+
from polars_baseball.apis.standings import standings
|
|
42
|
+
from polars_baseball.apis.statcast import statcast, statcast_batter, statcast_pitcher, statcast_single_game
|
|
43
|
+
from polars_baseball.apis.top_prospects import prospect_rankings, top_prospects
|
|
44
|
+
from polars_baseball.context import BaseballContext
|
|
45
|
+
from polars_baseball.context import cleanup as _cleanup
|
|
46
|
+
from polars_baseball.enums import ArsenalType, KeyType
|
|
47
|
+
|
|
48
|
+
__version__ = "0.1.0"
|
|
49
|
+
|
|
50
|
+
_logging.getLogger("polars_baseball").addHandler(_logging.NullHandler())
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def cleanup() -> None:
|
|
54
|
+
"""Close default HTTP resources held by the package-level context."""
|
|
55
|
+
await _cleanup()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"ArsenalType",
|
|
60
|
+
"BaseballContext",
|
|
61
|
+
"FanGraphsRequest",
|
|
62
|
+
"KeyType",
|
|
63
|
+
"cleanup",
|
|
64
|
+
"configure_cache",
|
|
65
|
+
"fg_data",
|
|
66
|
+
"mlb_divisions",
|
|
67
|
+
"mlb_draft",
|
|
68
|
+
"mlb_game_boxscore",
|
|
69
|
+
"mlb_game_boxscore_stats",
|
|
70
|
+
"mlb_game_feed_live",
|
|
71
|
+
"mlb_game_linescore",
|
|
72
|
+
"mlb_game_play_by_play",
|
|
73
|
+
"mlb_game_win_probability",
|
|
74
|
+
"mlb_leagues",
|
|
75
|
+
"mlb_people",
|
|
76
|
+
"mlb_people_awards",
|
|
77
|
+
"mlb_pitch_arsenal",
|
|
78
|
+
"mlb_player_stats",
|
|
79
|
+
"mlb_postseason_schedule",
|
|
80
|
+
"mlb_roster",
|
|
81
|
+
"mlb_schedule",
|
|
82
|
+
"mlb_stat_leaders",
|
|
83
|
+
"mlb_team_stats",
|
|
84
|
+
"mlb_teams",
|
|
85
|
+
"mlb_transactions",
|
|
86
|
+
"mlb_venues",
|
|
87
|
+
"playerid_lookup",
|
|
88
|
+
"savant_gamefeed_exit_velocity",
|
|
89
|
+
"savant_gamefeed_exit_velocity_many",
|
|
90
|
+
"savant_gamefeed_pitch_data",
|
|
91
|
+
"savant_gamefeed_pitch_data_many",
|
|
92
|
+
"statcast_arm_strength",
|
|
93
|
+
"statcast_baserunning_run_value",
|
|
94
|
+
"statcast_catcher_stance",
|
|
95
|
+
"statcast_catcher_throwing",
|
|
96
|
+
"standings",
|
|
97
|
+
"statcast",
|
|
98
|
+
"statcast_batter",
|
|
99
|
+
"statcast_pitcher",
|
|
100
|
+
"statcast_single_game",
|
|
101
|
+
"top_prospects",
|
|
102
|
+
"prospect_rankings",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
for _implementation_namespace in ("apis", "context", "enums", "exceptions", "gateways", "parsers"):
|
|
106
|
+
globals().pop(_implementation_namespace, None)
|
|
107
|
+
|
|
108
|
+
del _implementation_namespace
|