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), 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.
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polars_stats-0.0.1.tar.gz.
File metadata
- Download URL: polars_stats-0.0.1.tar.gz
- Upload date:
- Size: 322.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84313e225c80c178c7678c984229410b4517dcead6bd84d393278a2c5b88fd54
|
|
| MD5 |
2821eed6b9bf97d6dc8f64c0fa3ae0ca
|
|
| BLAKE2b-256 |
fdd95844bfcfd37827e27fc9d7dedf9de0b8502fb4a18bd34e0fb999daf2036f
|
Provenance
The following attestation bundles were made for polars_stats-0.0.1.tar.gz:
Publisher:
publish_to_pypi.yml on FBruzzesi/polars-stats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_stats-0.0.1.tar.gz -
Subject digest:
84313e225c80c178c7678c984229410b4517dcead6bd84d393278a2c5b88fd54 - Sigstore transparency entry: 2400655754
- Sigstore integration time:
-
Permalink:
FBruzzesi/polars-stats@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/FBruzzesi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_stats-0.0.1-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: polars_stats-0.0.1-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 4.8 MB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b4f58427648f5b04202ce92d2d38a0244c790a7ab51f064d1e46ccd034b3b5f
|
|
| MD5 |
f325aa4334364489c78b61e7b1470df7
|
|
| BLAKE2b-256 |
1dd8565f93dcfbe212549ae5739d35a28200f038362fdb007bdd7e7616f50896
|
Provenance
The following attestation bundles were made for polars_stats-0.0.1-cp310-abi3-win_amd64.whl:
Publisher:
publish_to_pypi.yml on FBruzzesi/polars-stats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_stats-0.0.1-cp310-abi3-win_amd64.whl -
Subject digest:
1b4f58427648f5b04202ce92d2d38a0244c790a7ab51f064d1e46ccd034b3b5f - Sigstore transparency entry: 2400656576
- Sigstore integration time:
-
Permalink:
FBruzzesi/polars-stats@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/FBruzzesi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_stats-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: polars_stats-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.3 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d990bf5dc31900a15fba3edd26918603a6c83e2816a95c9fc989691f15be38b
|
|
| MD5 |
96cd106451f9736876d90825748d5c12
|
|
| BLAKE2b-256 |
616b3c89cc84c7983060937f3f26c49b28b148b252398b9527c5861e6045c977
|
Provenance
The following attestation bundles were made for polars_stats-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish_to_pypi.yml on FBruzzesi/polars-stats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_stats-0.0.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
5d990bf5dc31900a15fba3edd26918603a6c83e2816a95c9fc989691f15be38b - Sigstore transparency entry: 2400656276
- Sigstore integration time:
-
Permalink:
FBruzzesi/polars-stats@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/FBruzzesi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_stats-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: polars_stats-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.8 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
757396c8581b9238943ac25aae207b0190c014b3cea7fdb6205e789a718b22c8
|
|
| MD5 |
36812d448f22b6c257ce807af2a29a00
|
|
| BLAKE2b-256 |
4da0f9304c59ca827f9a0590e1e78896e15758fda733f0769436e0b71d2ff30e
|
Provenance
The following attestation bundles were made for polars_stats-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
publish_to_pypi.yml on FBruzzesi/polars-stats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_stats-0.0.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
757396c8581b9238943ac25aae207b0190c014b3cea7fdb6205e789a718b22c8 - Sigstore transparency entry: 2400655910
- Sigstore integration time:
-
Permalink:
FBruzzesi/polars-stats@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/FBruzzesi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_stats-0.0.1-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: polars_stats-0.0.1-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.7 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd727277139e9d2df902790dc789de3ec561442a76960265e01631b6943ff081
|
|
| MD5 |
70340a1f8958ab3a74065f75c0865475
|
|
| BLAKE2b-256 |
f74213d3d030f8fbd9d350cd6cb0fce2a3bf87354fa8876008177caf188c88e2
|
Provenance
The following attestation bundles were made for polars_stats-0.0.1-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
publish_to_pypi.yml on FBruzzesi/polars-stats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_stats-0.0.1-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
fd727277139e9d2df902790dc789de3ec561442a76960265e01631b6943ff081 - Sigstore transparency entry: 2400656071
- Sigstore integration time:
-
Permalink:
FBruzzesi/polars-stats@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/FBruzzesi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Trigger Event:
push
-
Statement type:
File details
Details for the file polars_stats-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: polars_stats-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 4.1 MB
- Tags: CPython 3.10+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5853263c8a033b6b67f8c814f5087df88b8e116f13990ca404b11831353c7c04
|
|
| MD5 |
b83ce2f6cb5703043e99e55afcf30470
|
|
| BLAKE2b-256 |
456382bcc879998f4941dcd81cc68ce97f110fff371a24f377cd488f1eb9fb1c
|
Provenance
The following attestation bundles were made for polars_stats-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl:
Publisher:
publish_to_pypi.yml on FBruzzesi/polars-stats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polars_stats-0.0.1-cp310-abi3-macosx_10_12_x86_64.whl -
Subject digest:
5853263c8a033b6b67f8c814f5087df88b8e116f13990ca404b11831353c7c04 - Sigstore transparency entry: 2400656439
- Sigstore integration time:
-
Permalink:
FBruzzesi/polars-stats@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Branch / Tag:
refs/tags/v0.0.1 - Owner: https://github.com/FBruzzesi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@acc3c45cfec3c3d5d5405047c9ba003924d13980 -
Trigger Event:
push
-
Statement type: