Metadata-Version: 2.5
Name: plot_io
Version: 0.6.0
Summary: Plots for PDF, PNG, JPEG, JPEG XL and AVIF, drawn with vec_io
Author-email: Arthur Gouhier <ajgouhier@gmail.com>
License-Expression: BSD-3-Clause
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: vec-io>=0.1.0
Provides-Extra: dev
Requires-Dist: flake8; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: ziglang==0.15.1; extra == 'dev'
Description-Content-Type: text/markdown

# plot_io

Plots for PDF, PNG, JPEG, JPEG XL and AVIF in Python, drawn with
[vec_io](https://github.com/ajgouhier/vec_io).
No other dependency.

![box plots](examples/box_plots.png)

* **Familiar:** a figure of plots; `scatter`, `line`, `fill`, `box`, `heatmap`, `bar`,
  `hist`, `kde` (1-D and 2-D), `ecdf`, `violin`, `strip`, `swarm`, `area`, `errorbar`, `stem`,
  `regression`, `hist2d`, `hexbin`, `contour`, `pie`; reference lines, spans, text and
  annotations; `color`, `marker`, `alpha`, `limits`, `scale`, `legend`.
* **Units:** lengths in px, pt, in, mm, `em`, `%` of the axes or of another setting, data
  units and durations (`width="1.5in"`, `tick_length="0.5em"`, `width="60%"`,
  `cap_length="12h"`); `var()` and `calc()` relate settings (`"calc(var(--gap) * 2)"`).
* **One way to set things:** keywords at creation or `set(...)` on any object; figure
  settings (font, palette, line width…) are inherited by plots and series.
* **Layouts:** an explicit grid of ids with spans, or automatic rows and columns; plots
  sized individually; axis lines (or plot borders) aligned across the figure; margins from
  the real text metrics; shared axes; secondary axes; facets of a table.
* **Data:** columns of any table by name (`data=`: dicts, pandas, polars, numpy), one
  series per group (`group=`), numbers given as colors or sizes, point density (scipy's
  `gaussian_kde`, in C) for colors, sizes, opacity and drawing order.
* **Axes:** linear, log, log2, log10, sqrt, exp, symlog, logit, time and category scales;
  limits such as `"0/max"` or `"q:0.01/q:0.99"`; ticks with shared decimals and ×10ᵏ
  factors, calendar ticks for dates.
* **Colors:** a colorblind-safe palette, color maps interpolated in Oklab, colorbars;
  marker sizes by area, equal for every shape.
* **Styles:** `style="matplotlib"`, `"seaborn"`, `"ggplot2"`, `"altair"`, `"matlab"`,
  `"chartjs"`, `"plotly"`, `"bokeh"`, `"observable"`, `"excel"`, `"fivethirtyeight"`,
  `"economist"`, `"tufte"` and variants give the look of those libraries; styles are
  presets of settings, and your own build on them.
* **Large data:** one million points mapped and drawn in C; `simplify="auto"` drops what
  cannot be seen, `rasterize=` turns a series into an image inside the PDF.
* **Genome tracks:** bigWig signals, intervals, gene models and Hi-C triangles over a
  region, read from gwseq_io's readers (or any object with their methods) without
  depending on gwseq_io.

## Install

```sh
pip install /path/to/vec_io      # vec_io 0.1.0 or later
pip install .
```

Python 3.11+. Text is shaped (kerned) and its fonts embedded by vec_io; it needs an
installed outline font, the default family list (`SF Pro, Inter, Helvetica, Arial,
sans-serif`) taking its first installed family ([API.md](API.md#1-conventions)). JPEG XL
and AVIF output need vec_io's optional libjxl and libavif.

## Quick start

```python
import plot_io as pl

s = pl.Scatter(x, y, name="data")
s.plot.x_axis(label="Time (s)")
s.plot.save("scatter.pdf")
```

```python
fig = pl.Figure(layout=[[1, 1, 2], [3, 4, 2]])
p = fig.plot(1, title="Signal")
p.line(t, v, name="raw")
p.fill(t, upper, lower, alpha=0.3)
p.y_axis(scale="log10", label="Power")
fig.plot(2).box({"a": va, "b": vb})
h = fig.plot(3).heatmap(matrix, x=columns, y=rows, cmap="magma", colorbar=True)
fig.plot(4).scatter(x, y, c=z, cmap="viridis")
fig.save("figure.pdf")
fig.save("figure.png", dpi=200)
```

```python
fig = pl.Figure(style="ggplot2")                         # or a dict: {"base": "seaborn", "font_size": 9}
```

```python
p = pl.Plot(width="3in", font_size="9pt", vars={"accent": "#c00"})
p.x_axis(tick_length="0.5em", minor_tick_length="50%")
p.bar(categories, counts, width="60%", color="var(--accent)")
p.x_axis.explain("tick_length")                          # where a value comes from
```

```python
p = pl.Plot()
p.scatter("umap_x", "umap_y", data=genes, c="density", draw_order="density")   # columns
p.scatter("x", "y", data=df, group="cluster", marker="auto")              # one series per group
fig, panels = pl.facet(df, col="condition", row="replicate")               # small multiples
panels.scatter("x", "y")
```

## Documentation

* [API.md](API.md): the complete reference, with every setting.
* [EXAMPLES.md](EXAMPLES.md): worked examples.

## Development

```sh
python -m unittest discover -s tests -t tests
flake8 --select F plot_io tests && mypy
python tools/bench.py --check       # timings against the targets
python tools/settings_doc.py        # regenerate the settings tables of API.md
python tools/visual_check.py --save # render the golden scenes and examples as references
python tools/visual_check.py        # ... and later compare with them (pixels, tolerances)
PLOT_IO_GOLDEN=update python -m unittest discover -s tests -t tests -p test_golden.py
python tools/record_stats.py > tests/stats_reference.json  # with numpy, scipy, statsmodels
```

The tests use only their own fonts (`tests/fonts`: PioTest, from vec_io's tests, through
`VEC_IO_FONT_DIRS`; installed fonts are ignored), so their text is the same on every
machine. The goldens (`tests/golden.json`) are keyed by meaning: the laid-out geometry of
each scene (rounded to 1/64 px), its 96 dpi pixels and vec_io's SVG form of its page (the
last two compared where the fonts and vec_io match those recorded).
`PLOT_IO_GOLDEN=dump:DIR` writes the geometry of each scene to `DIR` to see what changed.

The statistics kernels and color mapping are in plot_io's own C library, `plio`
(`plot_io/_native`, built like vec_io's: zig, freestanding, committed binaries for five
targets). Rebuilding and its checks:

```sh
pip install ziglang==0.15.1         # or: pip install -e .[dev]
python tools/build_native.py        # bindings from src/plio_api.h, then all targets
python tools/build_native.py --check
python tools/crosscheck.py --qemu PATH/qemu-aarch64-static   # same results on aarch64
```

See [plot_io/_native/README.md](plot_io/_native/README.md).
