Metadata-Version: 2.5
Name: genoplot
Version: 0.1.0a6
Summary: Modular genome neighborhood plots with colormaps
Author-email: Luan Leal <luanleal@usp.br>
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: matplotlib>=3.8
Requires-Dist: numpy>=1.26
Requires-Dist: polars>=1.0
Description-Content-Type: text/markdown

<p align="center">
  <h1 align="center">genoplot</h1>
  <p align="center">Modular genome neighborhood plots with colormaps</p>
  <p align="center">
    <a href="https://pypi.org/project/genoplot/"><img src="https://img.shields.io/pypi/v/genoplot" alt="PyPI version"></a>
    <a href="https://pypi.org/project/genoplot/"><img src="https://img.shields.io/pypi/pyversions/genoplot" alt="Python versions"></a>
    <a href="https://pypi.org/project/genoplot/"><img src="https://img.shields.io/pypi/l/genoplot" alt="License"></a>
    <a href="https://pypi.org/project/genoplot/"><img src="https://img.shields.io/pypi/dm/genoplot" alt="PyPI downloads"></a>
  </p>
</p>

`genoplot` plots the genes around an anchor gene on each plasmid: a backbone
line with direction-aware gene arrows, colored by a numeric association value
(e.g. NPMI). It replaces the original monolithic `plot_npmi_neighborhood`
function with pure coordinate math separated from matplotlib rendering.

![example neighborhood plot](neighborhood.png)

## Features

- **Polars in, figure out** — pass a `pl.DataFrame` of coordinates, get a
  `matplotlib` figure.
- **Circular genomes handled for free** — windows may cross the plasmid
  boundary; genes spanning the replication origin stay contiguous.
- **Collision-free annotations** — gene labels are horizontal, sit above the
  arrows with a leader line back to each gene, and are automatically
  staggered up to `label_rows=3` (default) well-separated rows when they
  collide (remaining overlaps are dropped).
- **Small plasmids stay centered** — when a plasmid's genome is smaller than
  the flank, `center_anchor=True` (default) caps the window to the genome so
  the anchor is drawn at the track's centre instead of pooling its labels
  at the left edge where they get discarded.

## Installation

```bash
pip install genoplot
```

Requires Python >= 3.11. Dependencies: `matplotlib`, `numpy`, `polars`.

## Quick start

```python
import genoplot

coords = genoplot.make_mock_coords()
fig, axes = genoplot.plot_neighborhoods(
    coords,
    ["plasmid_1", "plasmid_4"],
    neighbor_range=10_000,
)
fig.savefig("neighborhoods.png", dpi=200)
```

## Coordinate table schema

One row per gene. Required columns: `plasmid`, `gene_id`, `start`, `end`,
`strand` (the entry `start`/`end` may be any integer range; `strand` is
`1` or `-1`). Optional but useful: label columns (`accession`,
`short_name`) and a numeric value column such as `NPMI_h1`.

```python
import polars as pl
import genoplot

df = pl.DataFrame({
    "plasmid": ["p1"] * 3,
    "gene_id": ["g1", "g2", "g3"],
    "accession": ["WP_00000001.1", "WP_00000002.1", None],
    "short_name": ["RepB", None, "parA"],
    "start": [0, 3400, 5100],
    "end": [1200, 3900, 5800],
    "strand": [1, -1, 1],
    "NPMI_h1": [0.91, None, -0.2],
})
fig, axes = genoplot.plot_neighborhoods(df, ["p1"])
```

## API

- `genoplot.plot_neighborhoods(coords, plasmids, *, neighbor_range=10_000,
  value_col="NPMI_h1", color="viridis", vmin=None, vmax=None,
  label_rows=3, label_gap=10, center_anchor=True, margin_frac=None, ...)` —
  one track per plasmid. Returns `(fig, axes)`; a
  shared colorbar is attached on the right. Plasmids without an anchor gene
  get a placeholder row. `value_col` selects the numeric column used for
  anchor ranking, gene coloring, the colorbar, and the title. `predicate`
  (a DataFrame→DataFrame filter) narrows the anchor candidates before
  ranking and takes precedence over `label_contains` — e.g.
  `predicate=lambda df: df.filter(pl.col("NPMI_h1") > 0.7)`. Anchor
  candidacy uses the `short_name` column by default (`anchor_label=`); when
  that column is missing from the table, `label_col` (the text drawn above
  each gene) is used instead, and a `SchemaError` is raised if neither
  exists. The `color`
  is any matplotlib colormap name (default `viridis`). When `vmin`/`vmax`
  are left as `None` the color scale is derived automatically from the
  smallest and largest `value_col` among the genes visible in the plotted
  windows (falling back to `-1..1` when every visible value is equal or
  missing); pass both to fix the range. The
  `title_template` receives `plasmid`, `value_col` and `value` (the
  anchor's score), plus any *other* column as long as it is constant
  across all rows of the plasmid being plotted:
  `title_template="{plasmid} ({host}) | {value_col}={value:.2f}"`. A
  column that varies within a plasmid raises a `SchemaError`.
  `center_anchor=True` (default) caps the window to a small plasmid's genome
  so the anchor stays centered and its labels aren't dropped at the edge;
  `margin_frac` (device-scaled x-margin) reserves extra horizontal room for
  long edge annotation labels. `label_gap` (pixels) is the minimum
  horizontal separation between labels on the same row — closer labels are
  staggered onto higher rows so neighbouring annotations stay distinct.
- `genoplot.schema.validate_coords(df)` / `check_value_column(df, col)` —
  coordinate validation.
- `genoplot.window.resolve_window()` — pure circular-window coordinate math;
  `genoplot.anchors.select_anchor()` — anchor gene selection.
- `genoplot.render.draw_backbone / draw_gene_arrow / draw_gene_label /
  draw_track / resolve_label_overlaps` — drawing primitives onto a
  caller-provided `Axes`.
- `genoplot.make_mock_coords(n_plasmids=4, seed=2026)` — deterministic
  synthetic data for demos and tests.

See [examples.md](examples.md) for runnable end-to-end examples and
[docs/predicate.md](docs/predicate.md) for the full reference on anchor
selection and the `predicate` argument.

## License

[MIT](LICENSE)

## Changelog

Release history is in [CHANGELOG.md](CHANGELOG.md). Contributions and
development workflow are documented in [DEVELOPMENT.md](DEVELOPMENT.md).