mound 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.
Files changed (43) hide show
  1. mound-0.1.0/.github/workflows/ci.yml +29 -0
  2. mound-0.1.0/.gitignore +17 -0
  3. mound-0.1.0/CHANGELOG.md +15 -0
  4. mound-0.1.0/LICENSE +21 -0
  5. mound-0.1.0/PKG-INFO +193 -0
  6. mound-0.1.0/PLANNING.md +243 -0
  7. mound-0.1.0/README.md +158 -0
  8. mound-0.1.0/ROADMAP.md +31 -0
  9. mound-0.1.0/docs/images/roki_splitter_zone.png +0 -0
  10. mound-0.1.0/examples/roki_sasaki_end_to_end.py +72 -0
  11. mound-0.1.0/mound/__init__.py +14 -0
  12. mound-0.1.0/mound/analysis.py +72 -0
  13. mound-0.1.0/mound/cli.py +182 -0
  14. mound-0.1.0/mound/config.py +28 -0
  15. mound-0.1.0/mound/export.py +96 -0
  16. mound-0.1.0/mound/http.py +41 -0
  17. mound-0.1.0/mound/models.py +192 -0
  18. mound-0.1.0/mound/pitches.py +267 -0
  19. mound-0.1.0/mound/players.py +115 -0
  20. mound-0.1.0/mound/savant.py +45 -0
  21. mound-0.1.0/mound/statsapi.py +90 -0
  22. mound-0.1.0/mound/viz.py +320 -0
  23. mound-0.1.0/mound/zone.py +41 -0
  24. mound-0.1.0/pyproject.toml +55 -0
  25. mound-0.1.0/scripts/publish.sh +70 -0
  26. mound-0.1.0/tests/__init__.py +0 -0
  27. mound-0.1.0/tests/conftest.py +74 -0
  28. mound-0.1.0/tests/fixtures/game_log_2025.json +41 -0
  29. mound-0.1.0/tests/fixtures/game_log_empty.json +10 -0
  30. mound-0.1.0/tests/fixtures/gf_game_1001.json +169 -0
  31. mound-0.1.0/tests/fixtures/gf_game_1002.json +169 -0
  32. mound-0.1.0/tests/fixtures/gf_game_1003.json +201 -0
  33. mound-0.1.0/tests/fixtures/people_808963.json +15 -0
  34. mound-0.1.0/tests/fixtures/people_search_none.json +4 -0
  35. mound-0.1.0/tests/fixtures/people_search_sasaki.json +24 -0
  36. mound-0.1.0/tests/fixtures/people_search_single.json +15 -0
  37. mound-0.1.0/tests/fixtures/people_search_two_pitchers.json +23 -0
  38. mound-0.1.0/tests/test_analysis.py +108 -0
  39. mound-0.1.0/tests/test_export.py +101 -0
  40. mound-0.1.0/tests/test_pitches.py +99 -0
  41. mound-0.1.0/tests/test_players.py +84 -0
  42. mound-0.1.0/tests/test_savant.py +55 -0
  43. mound-0.1.0/tests/test_statsapi.py +48 -0
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+
22
+ - name: Install mound
23
+ run: pip install -e ".[dev,parquet]"
24
+
25
+ - name: Lint
26
+ run: ruff check .
27
+
28
+ - name: Test
29
+ run: pytest
mound-0.1.0/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+ .coverage
13
+ htmlcov/
14
+ examples/output/
15
+ zone.png
16
+ .DS_Store
17
+ .env
@@ -0,0 +1,15 @@
1
+ ## Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ Format based on Keep a Changelog.
6
+
7
+ ## [0.1.0] - 2026-08-08
8
+
9
+ ### Added
10
+
11
+ - Initial prototype: resolve a pitcher by name or MLB ID, retrieve Statcast pitch-level data (filterable by game, date range, last-N-starts or pitch type), calculate pitch mix and strike rate, plot pitch locations against the strike zone, and export to CSV/JSON/Parquet.
12
+ - `Pitcher`/`PitchCollection` Python API and a `mound` CLI (`search`, `pitches`, `mix`, `results`, `zone`) sharing the same underlying implementation.
13
+ - Data sourced directly from the MLB Stats API and Baseball Savant's `/gf` endpoint, no `pybaseball` dependency.
14
+ - Pytest suite covering player resolution, game-log/pitch parsing, filtering, analysis and export, run against mocked HTTP fixtures.
15
+ - README, ROADMAP and an end-to-end example using Roki Sasaki's splitter.
mound-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matt Stiles
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.
mound-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: mound
3
+ Version: 0.1.0
4
+ Summary: A CLI and Python toolkit for acquiring, analyzing and visualizing MLB pitch-level data.
5
+ Project-URL: Homepage, https://github.com/stiles/mound
6
+ Project-URL: Repository, https://github.com/stiles/mound
7
+ Project-URL: Issues, https://github.com/stiles/mound/issues
8
+ Author: Matt Stiles
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: baseball,cli,mlb,pitching,sports,statcast
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: End Users/Desktop
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Topic :: Games/Entertainment
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: matplotlib>=3.8
23
+ Requires-Dist: pandas>=2.0
24
+ Requires-Dist: requests>=2.31
25
+ Requires-Dist: typer>=0.12
26
+ Provides-Extra: dev
27
+ Requires-Dist: build>=1.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Requires-Dist: responses>=0.25; extra == 'dev'
30
+ Requires-Dist: ruff>=0.6; extra == 'dev'
31
+ Requires-Dist: twine>=5.0; extra == 'dev'
32
+ Provides-Extra: parquet
33
+ Requires-Dist: pyarrow>=14.0; extra == 'parquet'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Mound
37
+
38
+ A CLI and Python toolkit for retrieving, analyzing and visualizing MLB pitch-level data — without needing to know MLB player IDs or the underlying API structures.
39
+
40
+ ```
41
+ > How many splitters did Roki Sasaki throw against the Diamondbacks last night?
42
+ > How often has he thrown it relative to his other pitches over his last four starts?
43
+ > What does its location look like over that period?
44
+ ```
45
+
46
+ Mound answers questions like these with a few CLI commands or a few lines of Python.
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ git clone https://github.com/stiles/mound.git
52
+ cd mound
53
+ pip install -e .
54
+
55
+ # Parquet export support:
56
+ pip install -e ".[parquet]"
57
+ ```
58
+
59
+ Requires Python 3.10+.
60
+
61
+ ## Quickstart
62
+
63
+ ### CLI
64
+
65
+ ```bash
66
+ # Find a player and their MLB ID
67
+ mound search "Roki Sasaki"
68
+
69
+ # Retrieve pitches from his last 4 starts
70
+ mound pitches "Roki Sasaki" --last 4
71
+
72
+ # Isolate one pitch type
73
+ mound pitches "Roki Sasaki" --last 4 --pitch splitter
74
+
75
+ # Pitch mix and results by pitch type
76
+ mound mix "Roki Sasaki" --last 4
77
+ mound results "Roki Sasaki" --last 4 --pitch splitter
78
+
79
+ # Plot pitch locations against the strike zone
80
+ mound zone "Roki Sasaki" --pitch splitter --last 4 --out splitter_zone.png
81
+
82
+ # Export the underlying data
83
+ mound pitches "Roki Sasaki" --last 4 --export roki_last4.csv
84
+ ```
85
+
86
+ Run `mound --help` or `mound <command> --help` for the full option list.
87
+
88
+ ### Python
89
+
90
+ ```python
91
+ from mound import Pitcher
92
+
93
+ roki = Pitcher("Roki Sasaki")
94
+
95
+ pitches = roki.pitches(last=4)
96
+ splitters = pitches.filter(pitch_type="splitter")
97
+
98
+ splitters.pitch_mix()
99
+ splitters.strike_rate()
100
+ splitters.plot_zone(out="splitter_zone.png")
101
+
102
+ pitches.to_csv("roki_last4.csv")
103
+ ```
104
+
105
+ `Pitcher.pitches()` and `PitchCollection.filter()` both accept:
106
+
107
+ | Argument | Meaning |
108
+ |---|---|
109
+ | `last` | most recent N appearances |
110
+ | `since` / `until` | date range (`"YYYY-MM-DD"` or `date`), inclusive |
111
+ | `game` | one or more MLB `game_pk` values |
112
+ | `pitch_type` | a pitch name, alias, or Statcast code (see below) |
113
+
114
+ Filtering a `PitchCollection` always returns another `PitchCollection`, so any combination of `.filter()`, `.pitch_mix()`, `.strike_rate()`, `.plot_zone()` and export methods composes freely.
115
+
116
+ ## Plots
117
+
118
+ `plot_zone()` renders a headline, a dek (pitch count, strike rate, date range) and a source line around the strike-zone chart itself, rather than relying on axis titles or a boxed legend:
119
+
120
+ ![Roki Sasaki splitter locations](docs/images/roki_splitter_zone.png)
121
+
122
+ All three are auto-generated but overridable:
123
+
124
+ ```python
125
+ splitters.plot_zone(
126
+ title="Sasaki leans on the splitter",
127
+ subtitle="134 pitches since the All-Star break",
128
+ source="Source: Baseball Savant",
129
+ kind="heatmap", # or "scatter" (default)
130
+ out="splitter_zone.png",
131
+ )
132
+ ```
133
+
134
+ Pass `subtitle=""` or `source=""` to omit either. Passing your own `ax` (e.g. for a multi-panel figure) skips the dek/source and falls back to a plain left-aligned title, so `plot_zone()` behaves as a well-mannered subplot.
135
+
136
+ ## Pitch types
137
+
138
+ Statcast tags every pitch with a short code. Mound normalizes these into human-readable names and accepts common aliases when filtering, so `pitch_type="four-seam"`, `"fastball"` and `"FF"` are all equivalent.
139
+
140
+ | Code | Name | Common aliases |
141
+ |---|---|---|
142
+ | `FF` | four-seam fastball | fastball, four-seam |
143
+ | `FT` | two-seam fastball | two-seam |
144
+ | `SI` | sinker | |
145
+ | `FC` | cutter | cut fastball |
146
+ | `SL` | slider | |
147
+ | `ST` | sweeper | sweeping slider |
148
+ | `SV` | slurve | |
149
+ | `CU` | curveball | curve |
150
+ | `KC` | knuckle curve | |
151
+ | `CH` | changeup | change-up |
152
+ | `FS` | splitter | split-finger |
153
+ | `FO` | forkball | |
154
+ | `SC` | screwball | |
155
+ | `KN` | knuckleball | knuckler |
156
+ | `EP` | eephus | |
157
+
158
+ **Note on Roki Sasaki's signature pitch:** Statcast classifies it inconsistently start-to-start — sometimes as a splitter (`FS`), sometimes as a forkball (`FO`), depending on its movement profile in a given game. If a `pitch_type="splitter"` query looks incomplete, check `pitch_type="forkball"` too, or filter using both.
159
+
160
+ ## Data sources
161
+
162
+ Mound calls two unofficial, public MLB data services directly:
163
+
164
+ - **[MLB Stats API](https://statsapi.mlb.com)** — player search/lookup and game logs, used to resolve a pitcher's identity and discover which games to pull.
165
+ - **[Baseball Savant](https://baseballsavant.mlb.com)** — the `/gf` game-feed endpoint, used for pitch-by-pitch Statcast data (location, velocity, pitch type, count, outcome).
166
+
167
+ Both are unofficial and undocumented; endpoints or response shapes could change without notice. Mound sends a descriptive `User-Agent` and retries transient failures, but does not currently cache responses, so repeated queries re-fetch data from these services.
168
+
169
+ ## Development
170
+
171
+ ```bash
172
+ pip install -e ".[dev]"
173
+ pytest
174
+ ruff check .
175
+ ```
176
+
177
+ Tests run entirely against mocked HTTP fixtures in `tests/fixtures/` (via the `responses` library) and don't require network access.
178
+
179
+ ## Known limitations
180
+
181
+ - No caching yet — every call re-fetches from the MLB Stats API / Baseball Savant.
182
+ - Pitch classification comes from Statcast's own model and can be inconsistent for pitches with unusual movement (see the Roki Sasaki note above).
183
+ - Only pitchers are supported as the primary retrieval unit; there's no batter-vs-pitcher matchup view yet (see [ROADMAP.md](ROADMAP.md)).
184
+ - Historical data availability depends on Statcast/Savant coverage, which is generally reliable from 2015 onward.
185
+ - All requests are synchronous and unthrottled beyond basic retry/backoff; heavy bulk retrieval (e.g. a full season) will be slow.
186
+
187
+ ## Roadmap
188
+
189
+ See [ROADMAP.md](ROADMAP.md) for planned enhancements beyond this prototype.
190
+
191
+ ## Changelog
192
+
193
+ See [CHANGELOG.md](CHANGELOG.md).