Metadata-Version: 2.5
Name: genoplot
Version: 0.1.0a2
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

# genoplot

Modular genome neighborhood plots with colormaps.

`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.

- **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.

## 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)
```

## Installation

The project uses [pixi](https://pixi.sh):

```bash
pixi install
pixi run test   # run tests
pixi run lint   # ruff check src tests scripts
pixi run demo   # regenerate demo_output/*.png
```

For a plain pip install: `pip install -e .` (requires python >=3.11, needs
`polars`, `matplotlib`, `numpy`).

## Publishing to PyPI

The package is configured for hatchling builds (wheel ships only
`src/genoplot`). Build and validate the artifacts:

```bash
pixi run build          # builds sdist + wheel into dist/
pixi run twine-check    # validates metadata / README rendering
```

Upload (needs a PyPI API token):

```bash
TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-... pixi run python -m twine upload dist/*
```

## Use it from another pixi project

**From PyPI** (after the release is published):

```bash
cd /path/to/your/project
pixi add python                                  # pixi.toml manifests only
pixi add --pypi "genoplot==0.1.0a1"               # pin pre-releases explicitly
pixi run python -c "import genoplot; print(genoplot.__version__)"
```

`pixi add --pypi genoplot` works for both `pixi.toml` and `pyproject.toml`
manifests (pyproject-based projects infer the interpreter from
`requires-python`, so the `pixi add python` line is only needed for
`pixi.toml`).

**From local source / during development** (no PyPI access needed):

```bash
cd /path/to/your/project
pixi add python                                  # pixi.toml manifests only
pixi add --pypi --editable "genoplot @ file:///home/thefire888/Work/ISPB2026"
```

The editable install points at your checkout, so local edits are picked up.
Pixi builds the package with uv on `pixi install`, which needs network
access (to fetch `hatchling` and the runtime deps `polars`/`matplotlib`/
`numpy`).

## 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],
})
genoplot.plot_neighborhoods(df, ["p1"])
```

## API

### High level

- `genoplot.plot_neighborhoods(coords, plasmids, *, neighbor_range=10_000,
  value_col="NPMI_h1", color="managua", vmin=-1.0, vmax=1.0, ...)` —
  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. pass
  `predicate=lambda df: df.filter(pl.col("NPMI_h1") > 0.7)` for a minimum
  anchor threshold. Gene labels are drawn above each arrow and
  automatically staggered up to `label_rows=3` rows when they collide;
  labels that still overlap are dropped and off-window labels are skipped.

### Lower level

- `genoplot.schema.validate_coords(df)` — check required columns, cast
  `start`/`end` to `Int64`; `check_value_column(df, col)`.
- `genoplot.window.Window`, `make_window`, `resolve_window` — pure
  coordinate math mapping genes into a linear frame where the visible
  window is `[0, width]`. Genes are sorted by `_plot_start`; origin-spanning
  genes stay contiguous.
- `genoplot.anchors.select_anchor(genes, value_col,
  label_contains="Rep")` — pick the highest-value candidate (default: a
  `short_name` containing `"Rep"`). Pass `predicate=...` for a custom
  filter, or `label_contains=None` to disable the label rule.
- `genoplot.colors.build_cmap / build_norm / value_to_color / add_colorbar`
  — colormap machinery; missing values render as `lightgray`.
- `genoplot.render.draw_backbone / draw_gene_arrow / draw_gene_label /
  draw_track` — drawing primitives onto a caller-provided `Axes`.
  `genoplot.render.resolve_label_overlaps(fig, axes)` deduplicates
  colliding gene labels by staggering them into rows / dropping them.

### Mock data

- `genoplot.make_mock_coords(n_plasmids=4, seed=2026, value_col="NPMI_h1")`
  — deterministic synthetic table for demos/tests. The first plasmid's
  anchor sits near the origin and the last near the genome end to exercise
  boundary-crossing windows.

## Render a demo

```bash
pixi run demo
```

writes PNGs into `demo_output/` (gitignored).

## Layout

```
src/genoplot/
  schema.py    column conventions + validation
  window.py    circular-window coordinate math
  anchors.py   anchor gene selection
  colors.py    colormap / norm helpers
  render.py    drawing primitives (backbone, arrows, labels)
  figure.py    high-level plot_neighborhoods API
  mock.py      seeded synthetic data generator
tests/         pytest suite (window, anchors, colors, figure)
scripts/
  demo.py      demo PNG renderer
```