Metadata-Version: 2.4
Name: p9beeswarm
Version: 0.2.0
Summary: Beeswarm, quasirandom, and sina geoms for plotnine
License-Expression: GPL-3.0-or-later
License-File: COPYING.txt
License-File: LICENSE.txt
Requires-Dist: numpy>=1.23
Requires-Dist: pandas>=1.5
Requires-Dist: plotnine>=0.12
Requires-Dist: rpy2>=3.5 ; extra == 'r-test'
Requires-Dist: mypy>=1.13 ; extra == 'test'
Requires-Dist: pandas-stubs>=2.2.2.240807 ; extra == 'test'
Requires-Dist: pytest>=7 ; extra == 'test'
Requires-Dist: pytest-cov>=6 ; extra == 'test'
Requires-Dist: scikit-learn>=1.3 ; extra == 'test'
Requires-Dist: scikit-learn-stubs>=0.0.3 ; extra == 'test'
Requires-Python: >=3.10
Provides-Extra: r-test
Provides-Extra: test
Description-Content-Type: text/markdown

# p9beeswarm

`p9beeswarm` provides beeswarm-style geoms for [plotnine][plotnine], modelled after the R [ggbeeswarm][ggbeeswarm-github] package.
The distribution also exposes the upstream algorithm compatibility modules as
`beeswarm` and `vipor`, mirroring the corresponding R packages.

```python
import plotnine as p9
from plotnine.data import penguins
from p9beeswarm import geom_quasirandom

penguins_plot = (
    p9.ggplot(penguins.dropna(subset=["species", "body_mass_g"]),
              p9.aes("species", "body_mass_g", color="species"))
    + geom_quasirandom(width=0.35)
    + p9.labs(
        title="Penguin body mass by species",
        x="Species",
        y="Body mass (g)",
    )
    + p9.theme_bw()
)
```

Rendered output:

![Penguin body mass beeswarm plot by p9beeswarm][penguins-figure]

The [side-by-side ggbeeswarm examples][examples] recreate the upstream README plots with both the Python and R implementations.
Render them with:

```sh
quarto render docs/ggbeeswarm-examples.qmd
```

The README figure is generated by [docs/update_readme_figure.py][update-readme-figure].
If you change the example above, regenerate the image with:

```sh
uv run python docs/update_readme_figure.py
```


## 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:

|         Package          |              License               |                                     Authors                                     |
| ------------------------ | ---------------------------------- | ------------------------------------------------------------------------------- |
| [beeswarm][beeswarm]     | [Artistic License 2.0][artistic-2] | [Aron Eklund, James Trimble, et al.][beeswarm-authors]                          |
| [vipor][vipor]           | [GPL (>= 2)][gpl-2]                | [Scott Sherrill-Mix, Erik Clarke, et al.][vipor-authors]                        |
| [ggbeeswarm][ggbeeswarm] | [GPL (>= 3)][gpl-3]                | [Erik Clarke, Scott Sherrill-Mix, Charlotte Dawson, et al.][ggbeeswarm-authors] |


## License

`p9beeswarm` is licensed under the [GNU GPLv3 or later][license].


## 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:

```sh
uv run --extra test pytest
```

### Upstream comparison tests

Beyond the core suite, the tests include parity checks against the original R packages.
These tests use [rpy2][rpy2] to call the upstream R implementations and assert that the Python results match numerically:

```sh
uv run --extra test --extra r-test pytest
```

The R dependencies are pinned in [`renv.lock`][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:

```sh
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][mypy].

```sh
uv run --extra test --extra r-test mypy src/ tests/
```

[plotnine]: https://plotnine.org/
[mypy]: https://mypy.readthedocs.io/
[rpy2]: https://rpy2.github.io/
[ggbeeswarm-github]: https://github.com/eclarke/ggbeeswarm
[beeswarm]: https://CRAN.R-project.org/package=beeswarm
[beeswarm-authors]: https://cran.r-project.org/web/packages/beeswarm/DESCRIPTION
[vipor]: https://CRAN.R-project.org/package=vipor
[vipor-authors]: https://cran.r-project.org/web/packages/vipor/DESCRIPTION
[ggbeeswarm]: https://CRAN.R-project.org/package=ggbeeswarm
[ggbeeswarm-authors]: https://cran.r-project.org/web/packages/ggbeeswarm/DESCRIPTION
[artistic-2]: https://opensource.org/license/artistic-2-0/
[gpl-2]: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
[gpl-3]: https://www.gnu.org/licenses/gpl-3.0.html

[penguins-figure]: docs/penguins-beeswarm.png
[examples]: docs/ggbeeswarm-examples.qmd
[update-readme-figure]: docs/update_readme_figure.py
[license]: LICENSE.txt
[renv-lock]: renv.lock
