polars-stats
polars-stats is a Polars expression plugin that exposes
scipy.stats-style probability distributions natively inside
Polars expressions:
-
Lazy-native: every method returns a
pl.Expr, so a distribution composes inside aLazyFramequery under the optimiser, with no materialisation. -
Column-valued parameters: any distribution parameter can be a scalar or a Polars expression. A single instance describes a different distribution per row:
import polars as pl import polars_stats as ps norm = ps.Normal(mu=pl.col("mu"), sigma=pl.col("sigma")) norm.cdf(pl.col("x"))
-
Polars null and error semantics: a
nullinput gives anullresult, and an invalid parameter raises aComputeErrorrather than silently returningNaN. -
Reproducible sampling: every draw is keyed on
(seed, row index), so a seeded column repeats across runs, chunkings, thread counts, and both engines.
scipy already does the per-row maths: stats.norm(loc=mu_array, scale=sigma_array).sf(x_array) broadcasts parameter
arrays and scores every element against its own distribution, vectorised, with no Python loop. The difference is where
the result lands. scipy returns a NumPy array, so a LazyFrame has to collect() first, pushdown stops at that
boundary, and realigning the result through later joins and filters is your problem. Here it stays a pl.Expr the
planner can see. Why polars-stats has the full comparison.
The math runs in Rust on top of the statrs crate; the Python layer is a thin, typed surface
of distribution classes.
Why
Statistical work in Polars today means falling back to .to_pandas() / .to_numpy(), then reaching for one of:
scipy.stats, which exits the lazy engine and materialises everything,- Python UDFs via
map_elements, which are slow and hold the GIL, - hand-rolled per-distribution expressions, which are ad hoc and error-prone.
The row-varying, vectorised, lazy-native case is what polars-stats targets.
Quick example
Anomaly scoring, where each row carries its own baseline:
import polars as pl
import polars_stats as ps
readings = pl.LazyFrame(
{
"value": [9.8, 101.0, 12.1, 250.0],
"mu": [10.0, 100.0, 10.0, 100.0],
"sigma": [0.5, 2.0, 0.5, 2.0],
}
)
norm = ps.Normal(mu="mu", sigma="sigma")
anomalies = readings.with_columns(upper_tail=norm.sf("value")).filter(pl.col("upper_tail") < 0.01).collect()
print(anomalies)
shape: (2, 4)
┌───────┬───────┬───────┬────────────┐
│ value ┆ mu ┆ sigma ┆ upper_tail │
│ --- ┆ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ f64 ┆ f64 │
╞═══════╪═══════╪═══════╪════════════╡
│ 12.1 ┆ 10.0 ┆ 0.5 ┆ 0.000013 │
│ 250.0 ┆ 100.0 ┆ 2.0 ┆ 0.0 │
└───────┴───────┴───────┴────────────┘
Each row is scored against its own Normal(mu, sigma), in one vectorised pass, without leaving the lazy engine.
Numerical accuracy
The maths runs on statrs, and make audit sweeps every method against
an mpmath oracle at 50 digits, including inputs many decades past where
scipy itself saturates. For tail work on Normal, LogNormal and the closed-form distributions
(Uniform, Exponential, Bernoulli, Geometric, DiscreteUniform), use log_cdf / log_sf rather than the
linear pair, and isf(q) rather than ppf(1 - q). Beta and Binomial inherit several documented statrs-side
limits in this release: there the log methods underflow with the linear ones, and the extreme lower
tail of ppf misbehaves. Every known limit is listed with a regime and a magnitude in
Numerical accuracy.
Installation
pip install polars-stats
Runtime needs polars>=1.15 and Python >=3.10.
Wheels are abi3, so one wheel per platform serves every Python from 3.10 up: manylinux x86_64 and
aarch64, macOS x86_64 and arm64, Windows x64, plus an sdist. Alpine (musl) and Windows on ARM have
no wheel today even though polars publishes one for both; there pip falls back to the sdist and needs a
Rust toolchain. Open an issue if you need either target and it gets added.
Documentation
Full docs at fbruzzesi.github.io/polars-stats: the API reference with the distribution catalogue and method surface, and the architecture and design notes.
A note on the Rust code
I am not a Rust expert, and a good part of the Rust layer was written with AI assistance.
What I vouch for is the behaviour, which is pinned by an extensive test suite: parity against scipy.stats on every
method, property-based invariants, and bit-identity between the constant-parameter fast paths and the general
per-row paths.
Treat the Rust idioms with the appropriate skepticism: if you spot something that should be written differently, an issue or PR is very welcome.
Related projects
polars-stats is not the first take on statistics inside Polars expressions. Three projects cover neighbouring
ground, and if your need matches their scope they may serve you well:
polars-randomgenerates random columns as native Polars expressions (uniform, normal, binomial, integers), with column-valued parameters, per-call seeds, and a globalset_random_seed. It registers.randomnamespaces onExpr,DataFrame, andLazyFrame, which reads very naturally when sampling is the whole job. Its focus is sampling;polars-statstreats sampling as one method of a full distribution object, next topdf/cdf/sf/ppf, their numerically stable log variants, and closed-form moments.polars_rngexposes one sampling expression per distribution (prng.normal(mu=pl.col("x"), sigma=3)), also as a Rust plugin over the samestatrscrate, also with column-valued parameters. Its sampling catalogue is wider than whatpolars-statsships today (Poisson, Gamma, Weibull, Laplace, plus categorical and integer draws), so for pure simulation it may be the better fit. The differences are scope and reproducibility: it is sampling only, with nopdf/cdf/ppfor moments, and it draws from a thread-local RNG with noseedargument, wherepolars-statskeys every draw on(seed, row index)so a seeded column repeats across runs, chunkings, and engines.polars_normal_statscovers the Normal distribution through three focused expressions,normal_cdf/normal_ppf/normal_pdf, each evaluated at a column of points. Itsmeanandstdtravel as plugin kwargs, so they are scalars: the common case, handled in three functions and nothing more.polars-statsgeneralises the same idea to a catalogue of distributions behind one scipy-like class API, passes parameters as plugin inputs so they can be columns, and adds survival functions,log_cdf/log_sf, and reproducible sampling.
License
This project is licensed under the MIT license.
Metadata
Release files for polars-stats 0.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| polars_stats-0.0.3.tar.gz | 374.0 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| polars_stats-0.0.3-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| polars_stats-0.0.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| polars_stats-0.0.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| polars_stats-0.0.3-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| polars_stats-0.0.3-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 31.5 MB
Release files / polars_stats-0.0.3.tar.gz
| Download URL | polars_stats-0.0.3.tar.gz |
|---|---|
| Size | 374.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3123d9e6fdb4d7fa47fd3636fabb00cef42678751d99884fc109146949c1d866
|
|
BLAKE2b-256 checksum How to use checksums |
d024e3ac7d1effb8efbc91a585eb4619cff1acafd9965ba27a35d4e2e6979bb4
|
| 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 Sep 14, 2026.
Transparency logRelease files / polars_stats-0.0.3-cp310-abi3-win_amd64.whl
| Download URL | polars_stats-0.0.3-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 7.4 MB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
1430e0d366ebd7f120e0b0d4aa9139c0e0bf29259ef7e6b0cbbabcd8313c19ae
|
|
BLAKE2b-256 checksum How to use checksums |
4dbfd881e68ba08e18bae3b31ca2c1ed55b5753bfb569485ef223f984dae5ed8
|
| 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 Sep 14, 2026.
Transparency logRelease files / polars_stats-0.0.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | polars_stats-0.0.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 6.4 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
3c630769b9e41472761186060d0985bb4525b549a36da8af353a4a9edfd85e26
|
|
BLAKE2b-256 checksum How to use checksums |
5f7cf495c8ee7e1a2143de74e3de17269ddf6e0ca5b685a47c1ddd1ad79b0cbd
|
| 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 Sep 14, 2026.
Transparency logRelease files / polars_stats-0.0.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | polars_stats-0.0.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 5.6 MB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
177bba5b6597456e14095d1af36adaf98f3b40c12a2a51e948cb97594c7e12eb
|
|
BLAKE2b-256 checksum How to use checksums |
ebe238e3f982830b435304a1666bddce5bf169e81cbc7b489f374157fcbed2cd
|
| 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 Sep 14, 2026.
Transparency logRelease files / polars_stats-0.0.3-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | polars_stats-0.0.3-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 5.5 MB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
8fe7de545dbc7b430ed67f710da5f829ad9fa4a3e48130bb10686dd6e9546467
|
|
BLAKE2b-256 checksum How to use checksums |
2aa34bdfe638c69234d010221f4aefce6b21c3a9093d82614d1a9fd7d34fb1fe
|
| 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 Sep 14, 2026.
Transparency logRelease files / polars_stats-0.0.3-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | polars_stats-0.0.3-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 6.3 MB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
7323b6bbdb4d0803f057af6b6be0a48a0579341be408e9d108ccdc08ea2fc75f
|
|
BLAKE2b-256 checksum How to use checksums |
ee6091f9e00073b33b00eaa09afe6b9563793f9fdfcb6fe2c91c55b0141f9bb6
|
| 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 Sep 14, 2026.
Transparency log