p9beeswarm
p9beeswarm provides beeswarm-style geoms for plotnine, modelled after the R ggbeeswarm package.
The distribution also exposes the upstream algorithm compatibility modules as
beeswarm and vipor, mirroring the corresponding R packages.
Upstream R tools
p9beeswarm is heavily inspired by the following upstream R packages.
The Python implementations and compatibility tests follow their algorithms and plotting behavior:
Installation
Install p9beeswarm from PyPI with:
python -m pip install p9beeswarm
Usage
import plotnine as p9
from plotnine.data import penguins
from p9beeswarm import geom_quasirandom
penguins_plot = (
p9.ggplot(
penguins,
p9.aes(x="species", y="body_mass_g", color="species"),
)
+ geom_quasirandom()
+ p9.labs(
title="Penguin body mass by species",
x="Species",
y="Body mass (g)",
)
+ p9.theme_bw()
)
Rendered output:
The side-by-side ggbeeswarm examples recreate the upstream README plots with both the Python and R implementations. Render them with:
quarto render docs/ggbeeswarm-examples.qmd
The README figure is generated by docs/update_readme_figure.py. If you change the example above, regenerate the image with:
uv run python docs/update_readme_figure.py
Implementations and reproducibility
The table below records the Python implementation and its upstream R implementation.
“100% reproducible” means that repeated builds with the same data and parameters do not draw random numbers.
Methods marked as random can still be made reproducible in Python by passing a fixed random_state; without one, their point positions may change between builds.
| Plotnine API / method | Upstream R implementation | 100% reproducible? | Notes |
|---|---|---|---|
geom_quasirandom(method="quasirandom") |
ggbeeswarm::geom_quasirandom → vipor::offsetX / vipor::offsetSingleGroup |
Yes | Uses the deterministic van der Corput sequence. This is the default quasirandom method. |
geom_quasirandom(method="maxout|minout|smiley|frowney") |
ggbeeswarm::geom_quasirandom → vipor::offsetX / vipor::offsetSingleGroup |
Yes | Density and alternating extreme placement do not use random draws. |
geom_quasirandom(method="pseudorandom") |
ggbeeswarm::geom_quasirandom → vipor::offsetX / vipor::offsetSingleGroup |
No (unless seeded) | Explicitly generates random offsets; pass random_state for repeatable Python plots. |
geom_quasirandom(method="tukey|tukeyDense") |
ggbeeswarm::geom_quasirandom → vipor::offsetX / vipor::tukeyTexture |
No (unless seeded) | Tukey texture generates random permutations and jitter; pass random_state for repeatable Python plots. |
geom_beeswarm(method="swarm") |
ggbeeswarm::geom_beeswarm → beeswarm::swarmx |
Yes* | The default priority="ascending" and corral="none" are deterministic. priority="random" or corral="random" introduces randomness. |
geom_beeswarm(method="compactswarm") |
ggbeeswarm::geom_beeswarm → beeswarm::swarmx(compact=TRUE) |
Yes* | Same priority and corral caveat as swarm. |
geom_beeswarm(method="center|square|hex") |
ggbeeswarm::geom_beeswarm internal grid placement (center, square, or hex) |
Yes* | Grid placement is deterministic; corral="random" is the exception. |
*The beeswarm rows are deterministic with their default options. Any method that uses a random priority or corral is not 100% reproducible unless a seed is supplied.
License
p9beeswarm is licensed under the GNU GPLv3 or later.
Testing
The package is managed with [uv].
The core test suite covers the beeswarm and vipor compatibility modules,
along with the plotnine position/geom integration:
uv run --no-default-groups --group test pytest
Upstream comparison tests
Beyond the core suite, the tests include parity checks against the original R packages. These tests use rpy2 to call the upstream R implementations and assert that the Python results match numerically:
uv run --no-default-groups --group test --group r-test pytest
The R dependencies are pinned in renv.lock and restored by the GitHub Actions workflow with r-lib/actions/setup-renv.
To update the pinned versions, run these commands from the repository root:
Rscript -e 'if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv", repos = "https://cloud.r-project.org")'
Rscript -e 'renv::restore(prompt = FALSE)'
Rscript -e 'renv::update(c("beeswarm", "vipor", "ggbeeswarm", "ggplot2"), prompt = FALSE)'
Rscript -e 'renv::snapshot(prompt = FALSE)'
Review the resulting renv.lock diff, run the upstream comparison tests, and commit the updated lockfile along with any dependency changes.
Type checking
Type-check using mypy.
uv run --no-default-groups --group test --group r-test mypy src/ tests/
Metadata
Release files for p9beeswarm 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| p9beeswarm-0.3.0.tar.gz | 30.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| p9beeswarm-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 63.1 kB
Release files / p9beeswarm-0.3.0.tar.gz
| Download URL | p9beeswarm-0.3.0.tar.gz |
|---|---|
| Size | 30.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ff94384f3a1683c01093c762614407929389d4995a321d755216889cf535c22e
|
|
BLAKE2b-256 checksum How to use checksums |
cd1a354ec3a1c2ac4720a7d2186e79d4b6285c2f40e49b050311433a9d89cffe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / p9beeswarm-0.3.0-py3-none-any.whl
| Download URL | p9beeswarm-0.3.0-py3-none-any.whl |
|---|---|
| Size | 32.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
53bd9898aa9116ea2a70567cad566f6d9b1860da96d631dce99aef7d02e848d9
|
|
BLAKE2b-256 checksum How to use checksums |
e98049d0830294dbe466cf9e5b860e434de416144cc609425afccdcbf4b56e76
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|