Skip to main content

saberkit

Live sabermetrics for marimo notebooks, computed in Rust: the "plus" family of baseball statistics (OPS+, sOPS+, tOPS+, ERA+, wRC+), the FanGraphs "minus" family (ERA-, FIP-, xFIP-), and Baseball-Savant-style league percentile rankings — the 1–100 "bubbles".

Fetch a season, compute everything, and drive it with sliders:

import marimo as mo
import saberkit

season = mo.ui.dropdown(options=list(saberkit.compute.SEASONS), value=2024)
park_factor = mo.ui.slider(start=85, stop=115, value=100)

table = saberkit.compute.batting_table(
    saberkit.ingest.load_batting(season.value),
    league="season",
    season=season.value,
    park_factor=float(park_factor.value),
)

Every rate stat, OPS+, wRC+, and the percentile ranks recompute reactively as the controls move. Two ready-to-run notebooks ship in notebooks/ — a Savant-style percentile explorer and a pitcher scout for ERA-/FIP-:

pip install "saberkit[marimo,data]"   # data adds pybaseball for live fetching
marimo edit notebooks/percentile_explorer.py   # runs offline without `data` too

Also a fast batch library

Pass Arrow arrays and you get Arrow back through the PyCapsule interface — polars.Series, pyarrow.Array, and pandas ArrowDtype columns all pass with no copy and no conversion. Pass plain numbers and you get a number:

>>> round(saberkit.obp(h=150, bb=60, hbp=5, ab=500, sf=5), 4)
0.3772

league = saberkit.LeagueContext.for_season(2024)
rating = saberkit.wrc_plus(0.390, ctx=league, park_factor=102)

saberkit has zero required runtime dependencies — not even pyarrow. The notebook layer (ingest, compute) imports polars lazily; scalar use needs nothing installed at all.

Install

pip install saberkit              # core library, no dependencies
pip install "saberkit[marimo]"    # adds marimo + altair + polars for notebooks
pip install "saberkit[data]"      # adds pybaseball helpers for fetching data

Requires Python 3.11+.

Design

Undefined is not an error. A player with zero at-bats has no batting average. Every statistic returns None — an Arrow null in batch mode — rather than raising, returning NaN, or returning infinity. Nulls propagate elementwise, and the table layer extends the rule to whole populations: filter a dashboard until nobody qualifies and the percentile columns come back all-null instead of crashing your cell.

Misconfiguration is an error. A null player row is normal; a NaN league constant silently poisons every result it touches. Bad constants and mismatched array lengths raise saberkit.SaberError (a ValueError subclass).

League baselines are inputs, not guesses. compute.batting_table makes you say which population defines "league average": bundled season constants (league="season"), the data on screen (league="sample"), or your own LeagueContext. Whether pitchers' hitting counts is a methodological choice, and it stays with you.

Park factors are on the 100-scale everywhere (100 = neutral), matching how Baseball-Reference and FanGraphs publish them.

Layout

The statistical core is a separate crate that depends on nothing but thiserror — no Python, no Arrow, no I/O:

Crate / package Role
crates/saberkit-core The statistics. Pure Rust, testable with cargo test, no interpreter needed. Usable standalone from Rust.
crates/saberkit-py The only crate that knows Python exists. Arrow conversion and PyO3 bindings; contains no statistics.
python/saberkit/ingest.py Fetch-and-reshape: publisher column names → canonical polars frames. No formulas.
python/saberkit/compute.py One-call tables: wires columns into the Rust-backed functions and reattaches results. No formulas.
notebooks/ Shipped marimo apps built on the above.

Development

cargo test -p saberkit-core                 # statistics, no Python involved
cargo test -p saberkit-py --no-default-features  # binding layer (cast, nulls, chunks)
cargo clippy --workspace -- -D warnings
maturin develop -E dev                      # build + install into the active venv
pytest                                      # incl. headless runs of every notebook
SABERKIT_OFFLINE=1 marimo edit notebooks/pitcher_explorer.py   # notebooks without network

Maintainers should follow RELEASING.md for versioning, artifact verification, trusted-publisher setup, and tagging.

What's implemented

Group Statistics
Rate stats obp slg avg ops iso babip woba fip xfip era total_bases singles
Plus family ops_plus sops_plus tops_plus era_plus wraa wrc wrc_plus
Minus family era_minus fip_minus xfip_minus
Percentiles LeagueDistribution percentile_ranks batter_qualifier pitcher_qualifier
Innings ip_to_outs outs_to_innings
League context LeagueContext.from_totals LeagueContext.for_season (2010–2025)
Tables compute.batting_table compute.pitching_table

Accuracy

Statistics that depend only on their inputs — OBP, SLG, wOBA, FIP, sOPS+, tOPS+ — reproduce published figures exactly. Those involving park factors (OPS+, ERA+, and the minus family) approximate the publishers' internal pipelines, which use regressed multi-year park factors; expect agreement to within a point or two. Each function's docstring says which it is.

Innings pitched

Baseball records innings in a notation where the digit after the decimal counts outs: 190.1 is 190⅓ and 190.2 is 190⅔. Reading those as decimals understates the denominator by about 0.7% — enough to shift a FIP in the third decimal and quietly break comparisons against published figures. saberkit converts through outs internally and rejects impossible values like 190.4.

Season constants and provenance

LeagueContext.for_season(year) supplies completed-season MLB constants for 2010–2025. They are saberkit constants derived from Retrosheet regular-season play-by-play—not copies of FanGraphs' Guts! table. The population is all MLB batters, including pitcher batting where it occurred. The generator measures each season's RE24 matrix, derives linear event weights relative to outs, scales wOBA to league OBP, and computes the FIP constant from league totals.

The derivation is reproducible with scripts/generate_season_constants.py. Every season row records a SHA-256 digest of its source event files and the generated module records the exact Chadwick Bureau Retrosheet revision. Raw Retrosheet data is not packaged. See NOTICE for attribution.

Because the methodology and population are explicit, these constants can differ slightly from publisher-specific tables. In-progress seasons are never bundled; requesting an unsupported year raises SaberError.

Data sources

saberkit.data wraps pybaseball's FanGraphs scrapers behind the optional data extra; saberkit.ingest reshapes their output — and any polars/ pandas/pyarrow frame you already have — into canonical lowercase columns. Scraped sites change without notice, so treat fetching as convenience, not contract, and pin snapshots for anything reproducible. The shipped notebooks fall back to committed fixtures when offline (SABERKIT_OFFLINE=1 forces it).

Not yet included

  • Published wheels. CI builds candidate wheels for Linux x86_64/aarch64, macOS Intel/Apple Silicon, and Windows x86_64, but no PyPI release has been published yet.
  • Park factors. You supply them. saberkit does not compute or bundle any.

License and data attribution

MIT OR Apache-2.0

Bundled season constants are derived from Retrosheet data under its attribution terms; see NOTICE.

Metadata

Release files for saberkit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for saberkit 0.1.0
File Size Uploaded
saberkit-0.1.0.tar.gz 71.2 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for saberkit 0.1.0
File
saberkit-0.1.0-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
saberkit-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details
saberkit-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
saberkit-0.1.0-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details
saberkit-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl CPython 3.11 abi3 macOS 10.12+ x86-64 Details

Total release size: 15.6 MB

Release files / saberkit-0.1.0.tar.gz

Download URL saberkit-0.1.0.tar.gz
Size 71.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ea7d4e3ed9185e7b91382aa6ca2d57b9c1328ba75026c9bef42b631245b94e95
BLAKE2b-256 checksum
How to use checksums
eeb0f019d9151b4a02f055fa64cae612164f1fe541b94bdc23f684665da66ed9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / saberkit-0.1.0-cp311-abi3-win_amd64.whl

Download URL saberkit-0.1.0-cp311-abi3-win_amd64.whl
Size 3.3 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
7d149357c62faadf8b0ee9fd93ff07a4dfe89b2e801cf75dec61888114c88eae
BLAKE2b-256 checksum
How to use checksums
cfded1addf8075aa1026b1fae3200b8c73b4cd11c6d82615d548896ff6f0329b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / saberkit-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL saberkit-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.3 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
864fd294840ec2e507343ec36460aa24976d4798cc93758c24eaf75864ac0f5d
BLAKE2b-256 checksum
How to use checksums
16fd7eceb3bad311fbd8e00c370407844ffe4120a2973641cc87171017a94460
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / saberkit-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL saberkit-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 3.1 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
c605a007fd914f584e308ed8615358e5cd30ee444cdc8f89cf37abea27d91e9c
BLAKE2b-256 checksum
How to use checksums
275458c9f9532bc6eb465240ddd7352988b42f07789f4348265ab9b46f784686
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / saberkit-0.1.0-cp311-abi3-macosx_11_0_arm64.whl

Download URL saberkit-0.1.0-cp311-abi3-macosx_11_0_arm64.whl
Size 2.9 MB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
31d57eafa780c313cf29628683dab929af42fc2b36eabd0d7b6509c49962ee26
BLAKE2b-256 checksum
How to use checksums
5577c399e3300a45a59dfdd0b45e4c424d7f05224fbbf18bf9df0e3f8592ca66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / saberkit-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl

Download URL saberkit-0.1.0-cp311-abi3-macosx_10_12_x86_64.whl
Size 3.1 MB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
3730b78479542dc8cf15f6c7605bc806ad15685408a1ed21dbc72afb5f855f16
BLAKE2b-256 checksum
How to use checksums
90c1be1d7cdfa270511a2450fd5637ae879bfae3fdb0979070c99dacf5a023fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page