plotlet 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {plotlet-0.3.0/src/plotlet.egg-info → plotlet-0.4.0}/PKG-INFO +15 -11
  2. {plotlet-0.3.0 → plotlet-0.4.0}/README.md +12 -9
  3. {plotlet-0.3.0 → plotlet-0.4.0}/pyproject.toml +3 -2
  4. plotlet-0.4.0/src/plotlet/artists.py +503 -0
  5. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/builtin_artists.py +230 -16
  6. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/chart.py +207 -52
  7. plotlet-0.4.0/src/plotlet/colormaps.py +113 -0
  8. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/core.py +175 -91
  9. plotlet-0.4.0/src/plotlet/dendrogram.py +197 -0
  10. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/layout.py +120 -59
  11. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/layout_diagram.py +5 -3
  12. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/legend.py +19 -11
  13. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/registry.py +16 -0
  14. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/spec.json +13 -4
  15. {plotlet-0.3.0 → plotlet-0.4.0/src/plotlet.egg-info}/PKG-INFO +15 -11
  16. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet.egg-info/SOURCES.txt +1 -0
  17. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet.egg-info/requires.txt +1 -0
  18. {plotlet-0.3.0 → plotlet-0.4.0}/tests/test_chart.py +241 -9
  19. {plotlet-0.3.0 → plotlet-0.4.0}/tests/test_legend.py +20 -17
  20. {plotlet-0.3.0 → plotlet-0.4.0}/tests/test_subplots.py +83 -63
  21. {plotlet-0.3.0 → plotlet-0.4.0}/tests/test_units.py +35 -16
  22. plotlet-0.3.0/src/plotlet/artists.py +0 -314
  23. plotlet-0.3.0/src/plotlet/colormaps.py +0 -50
  24. {plotlet-0.3.0 → plotlet-0.4.0}/LICENSE +0 -0
  25. {plotlet-0.3.0 → plotlet-0.4.0}/setup.cfg +0 -0
  26. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/__init__.py +0 -0
  27. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/_cm_data.py +0 -0
  28. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/_png.py +0 -0
  29. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/_spec.py +0 -0
  30. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/colors.py +0 -0
  31. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/font.py +0 -0
  32. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/fonts/DejaVuSans.ttf +0 -0
  33. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet/scales.py +0 -0
  34. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet.egg-info/dependency_links.txt +0 -0
  35. {plotlet-0.3.0 → plotlet-0.4.0}/src/plotlet.egg-info/top_level.txt +0 -0
  36. {plotlet-0.3.0 → plotlet-0.4.0}/tests/test_layout_diagram.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotlet
3
- Version: 0.3.0
4
- Summary: Small, hackable Python library that emits matplotlib-style SVG plots.
3
+ Version: 0.4.0
4
+ Summary: Python library for SVG plots, with multi-panel composition and an extension API for custom plot types.
5
5
  Author: gitbamboo42
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/gitbamboo42/plotlet
@@ -22,6 +22,7 @@ Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: fonttools>=4.0
25
+ Requires-Dist: scipy>=1.10
25
26
  Provides-Extra: dev
26
27
  Requires-Dist: jupyter; extra == "dev"
27
28
  Requires-Dist: nbconvert; extra == "dev"
@@ -29,13 +30,13 @@ Dynamic: license-file
29
30
 
30
31
  # plotlet
31
32
 
32
- A small, hackable Python library that emits matplotlib-style SVG plots.
33
+ A Python library for SVG plots — with multi-panel composition, shared-axis layouts, and an extension API for custom plot types.
33
34
 
34
- ## Why
35
+ ## What it's for
35
36
 
36
- matplotlib is the right tool when you want the kitchen sink. plotlet's niche is **custom plot types** — genome tracks, Manhattan plots, phylogenetic trees, anything matplotlib's extension API makes painful. The whole library has a deliberately tiny, exposed core: adding a new plot type is a 3-step recipe, not an architecture project.
37
+ plotlet is built for **multi-panel scientific figures with custom plot types** — genome tracks, spike rasters, climate stacks, Manhattan plots, phylogenetic trees. The core ships ~5 standard plots plus multi-panel composition (`|`, `/`, `share_x()`).
37
38
 
38
- It's a **scaffold, not a feature catalog**: the core ships ~5 standard plots and the infrastructure for extending. Custom plot types live in your own project (or [`cookbook/`](cookbook/)), not upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
39
+ Custom plot types are a 3-step recipe (`record`, `xdomain`/`ydomain`, `draw`) and live in your own project (or [`cookbook/`](cookbook/)) rather than upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
39
40
 
40
41
  ```python
41
42
  import plotlet as pt
@@ -59,11 +60,11 @@ pip install plotlet
59
60
 
60
61
  ## Properties
61
62
 
62
- - **Lightweight.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
63
+ - **Minimal dependencies.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
63
64
  - **Static SVG output.** No interactivity, no animation. Same script → byte-identical SVG.
64
65
  - **Cross-machine reproducible.** Bundled DejaVu Sans + text-as-paths means rendering is identical on Linux, macOS, Windows, headless CI.
65
66
  - **Jupyter-native.** `Chart._repr_html_` auto-renders the last expression in a cell.
66
- - **Tiny output.** Each plot is ~50 KB SVG, self-contained.
67
+ - **Compact output.** Each plot is ~50 KB SVG, self-contained.
67
68
  - **Compositional.** Multi-panel layouts via `|`, `/`, `pt.grid`; share scales with `(a | b).share_x()` or `pt.grid(..., share_x="col")`; layout-level legend with `pt.legend()` covering both discrete swatches and continuous gradients (the colorbar).
68
69
  - **AI-readable.** Every figure ships `data-plotlet-*` attributes describing plot type, axes, scales, ranges, and series labels — readable in one XML parse, no glyph-path OCR. Schema: [docs/AI_ATTRS.md](docs/AI_ATTRS.md).
69
70
 
@@ -75,7 +76,7 @@ pip install plotlet
75
76
 
76
77
  Pass at construction (`pt.chart(data, title=..., grid=True, ...)`) or as chained setters (`c.title(...)`, etc.):
77
78
 
78
- `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — preferred), or `canvas_width`, `canvas_height` (the full SVG canvas — mutually exclusive with the data form). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`).
79
+ `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — the figure canvas grows to fit titles, tick labels, and axis labels). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`). To fit a composition into a target SVG canvas, chain `.fit(canvas_width=…, canvas_height=…)` after composing — it rescales data regions while keeping fonts, spines, and margins at their absolute pixel sizes.
79
80
 
80
81
  String-valued data on either axis (`scatter(["a","b","c"], ...)`, `bar`, …) auto-switches to a categorical scale, alphabetical by default. `padding=0` makes category bands contiguous (heatmap-track look).
81
82
 
@@ -93,11 +94,14 @@ Tick customization: `c.xticks([0, 5, 10], ["A","B","C"], rotation=45, fontsize=1
93
94
  | `.axhline(y, **opts)` / `.axvline(x, **opts)` | `color`, `linewidth`, `linestyle`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
94
95
  | `.axhspan(ymin, ymax, **opts)` / `.axvspan(xmin, xmax, **opts)` | `color`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
95
96
  | `.imshow(data, **opts)` | `cmap` (any matplotlib name, default `"viridis"`), `vmin`, `vmax`, `extent=(left, right, bottom, top)` |
97
+ | `.heatmap(df, **opts)` | `cmap`, `vmin`, `vmax`, `norm`, `center`, `xticklabels`, `yticklabels`, `legend` |
96
98
 
97
99
  `hue=<col>` (on `.line` / `.scatter`) splits into one call per unique value with auto-labels and tab10 colors. Reference lines and spans default to black; spans use `alpha=0.2`. They're drawn outside the data color cycle and don't participate in autoscaling — they're decorations on the frame, not data.
98
100
 
99
101
  `.imshow(data)` renders a 2-D array as a colored grid. Small grids (`nrows × ncols ≤ 10000`) emit one `<rect>` per cell and stay vector-clean at any zoom; larger grids encode as a single base64 PNG and quantize to 256 levels. Image row 0 is rendered at the top of its rectangle; the y axis stays Cartesian (small at bottom). All ~180 matplotlib colormaps are vendored — see `pt.list_colormaps()`.
100
102
 
103
+ `.heatmap(df)` is the DataFrame-aware companion to `.imshow`. A pandas DataFrame's `index` becomes the row tick labels and `columns` becomes the column tick labels; row 0 sits at the top. For a plain 2-D array, default labels are integer indices — pass `xticklabels=` / `yticklabels=` to override. Cells render at integer + 0.5 centers on a linear axis, which lines up with scipy's dendrogram leaf positions so a top/left dendrogram pairs cleanly via `share_x` / `share_y`.
104
+
101
105
  ### Subplots
102
106
 
103
107
  Compose multi-panel layouts with operators on `Chart`:
@@ -149,7 +153,7 @@ c.write_html("plot.html") # standalone HTML
149
153
 
150
154
  ## Adding a new plot type
151
155
 
152
- plotlet's central hackability claim: a custom plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
156
+ plotlet is designed so that adding a new plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
153
157
 
154
158
  ## Testing
155
159
 
@@ -163,7 +167,7 @@ python tests/test_subplots.py # subplot baselines + composition invarian
163
167
  ## Non-goals
164
168
 
165
169
  - No interactivity (hover, zoom, click). Static rendering is the point.
166
- - Not competing with matplotlib on standard plots; matplotlib is bigger and battle-tested.
170
+ - Not aiming for full coverage of standard statistical plots — those needs are well-served elsewhere.
167
171
  - Not a 3D plotter, not a dashboard tool.
168
172
  - Not a feature catalog — new plot types belong in user projects or `cookbook/`, not in the core.
169
173
 
@@ -1,12 +1,12 @@
1
1
  # plotlet
2
2
 
3
- A small, hackable Python library that emits matplotlib-style SVG plots.
3
+ A Python library for SVG plots — with multi-panel composition, shared-axis layouts, and an extension API for custom plot types.
4
4
 
5
- ## Why
5
+ ## What it's for
6
6
 
7
- matplotlib is the right tool when you want the kitchen sink. plotlet's niche is **custom plot types** — genome tracks, Manhattan plots, phylogenetic trees, anything matplotlib's extension API makes painful. The whole library has a deliberately tiny, exposed core: adding a new plot type is a 3-step recipe, not an architecture project.
7
+ plotlet is built for **multi-panel scientific figures with custom plot types** — genome tracks, spike rasters, climate stacks, Manhattan plots, phylogenetic trees. The core ships ~5 standard plots plus multi-panel composition (`|`, `/`, `share_x()`).
8
8
 
9
- It's a **scaffold, not a feature catalog**: the core ships ~5 standard plots and the infrastructure for extending. Custom plot types live in your own project (or [`cookbook/`](cookbook/)), not upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
9
+ Custom plot types are a 3-step recipe (`record`, `xdomain`/`ydomain`, `draw`) and live in your own project (or [`cookbook/`](cookbook/)) rather than upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
10
10
 
11
11
  ```python
12
12
  import plotlet as pt
@@ -30,11 +30,11 @@ pip install plotlet
30
30
 
31
31
  ## Properties
32
32
 
33
- - **Lightweight.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
33
+ - **Minimal dependencies.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
34
34
  - **Static SVG output.** No interactivity, no animation. Same script → byte-identical SVG.
35
35
  - **Cross-machine reproducible.** Bundled DejaVu Sans + text-as-paths means rendering is identical on Linux, macOS, Windows, headless CI.
36
36
  - **Jupyter-native.** `Chart._repr_html_` auto-renders the last expression in a cell.
37
- - **Tiny output.** Each plot is ~50 KB SVG, self-contained.
37
+ - **Compact output.** Each plot is ~50 KB SVG, self-contained.
38
38
  - **Compositional.** Multi-panel layouts via `|`, `/`, `pt.grid`; share scales with `(a | b).share_x()` or `pt.grid(..., share_x="col")`; layout-level legend with `pt.legend()` covering both discrete swatches and continuous gradients (the colorbar).
39
39
  - **AI-readable.** Every figure ships `data-plotlet-*` attributes describing plot type, axes, scales, ranges, and series labels — readable in one XML parse, no glyph-path OCR. Schema: [docs/AI_ATTRS.md](docs/AI_ATTRS.md).
40
40
 
@@ -46,7 +46,7 @@ pip install plotlet
46
46
 
47
47
  Pass at construction (`pt.chart(data, title=..., grid=True, ...)`) or as chained setters (`c.title(...)`, etc.):
48
48
 
49
- `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — preferred), or `canvas_width`, `canvas_height` (the full SVG canvas — mutually exclusive with the data form). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`).
49
+ `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — the figure canvas grows to fit titles, tick labels, and axis labels). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`). To fit a composition into a target SVG canvas, chain `.fit(canvas_width=…, canvas_height=…)` after composing — it rescales data regions while keeping fonts, spines, and margins at their absolute pixel sizes.
50
50
 
51
51
  String-valued data on either axis (`scatter(["a","b","c"], ...)`, `bar`, …) auto-switches to a categorical scale, alphabetical by default. `padding=0` makes category bands contiguous (heatmap-track look).
52
52
 
@@ -64,11 +64,14 @@ Tick customization: `c.xticks([0, 5, 10], ["A","B","C"], rotation=45, fontsize=1
64
64
  | `.axhline(y, **opts)` / `.axvline(x, **opts)` | `color`, `linewidth`, `linestyle`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
65
65
  | `.axhspan(ymin, ymax, **opts)` / `.axvspan(xmin, xmax, **opts)` | `color`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
66
66
  | `.imshow(data, **opts)` | `cmap` (any matplotlib name, default `"viridis"`), `vmin`, `vmax`, `extent=(left, right, bottom, top)` |
67
+ | `.heatmap(df, **opts)` | `cmap`, `vmin`, `vmax`, `norm`, `center`, `xticklabels`, `yticklabels`, `legend` |
67
68
 
68
69
  `hue=<col>` (on `.line` / `.scatter`) splits into one call per unique value with auto-labels and tab10 colors. Reference lines and spans default to black; spans use `alpha=0.2`. They're drawn outside the data color cycle and don't participate in autoscaling — they're decorations on the frame, not data.
69
70
 
70
71
  `.imshow(data)` renders a 2-D array as a colored grid. Small grids (`nrows × ncols ≤ 10000`) emit one `<rect>` per cell and stay vector-clean at any zoom; larger grids encode as a single base64 PNG and quantize to 256 levels. Image row 0 is rendered at the top of its rectangle; the y axis stays Cartesian (small at bottom). All ~180 matplotlib colormaps are vendored — see `pt.list_colormaps()`.
71
72
 
73
+ `.heatmap(df)` is the DataFrame-aware companion to `.imshow`. A pandas DataFrame's `index` becomes the row tick labels and `columns` becomes the column tick labels; row 0 sits at the top. For a plain 2-D array, default labels are integer indices — pass `xticklabels=` / `yticklabels=` to override. Cells render at integer + 0.5 centers on a linear axis, which lines up with scipy's dendrogram leaf positions so a top/left dendrogram pairs cleanly via `share_x` / `share_y`.
74
+
72
75
  ### Subplots
73
76
 
74
77
  Compose multi-panel layouts with operators on `Chart`:
@@ -120,7 +123,7 @@ c.write_html("plot.html") # standalone HTML
120
123
 
121
124
  ## Adding a new plot type
122
125
 
123
- plotlet's central hackability claim: a custom plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
126
+ plotlet is designed so that adding a new plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
124
127
 
125
128
  ## Testing
126
129
 
@@ -134,7 +137,7 @@ python tests/test_subplots.py # subplot baselines + composition invarian
134
137
  ## Non-goals
135
138
 
136
139
  - No interactivity (hover, zoom, click). Static rendering is the point.
137
- - Not competing with matplotlib on standard plots; matplotlib is bigger and battle-tested.
140
+ - Not aiming for full coverage of standard statistical plots — those needs are well-served elsewhere.
138
141
  - Not a 3D plotter, not a dashboard tool.
139
142
  - Not a feature catalog — new plot types belong in user projects or `cookbook/`, not in the core.
140
143
 
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "plotlet"
7
- version = "0.3.0"
8
- description = "Small, hackable Python library that emits matplotlib-style SVG plots."
7
+ version = "0.4.0"
8
+ description = "Python library for SVG plots, with multi-panel composition and an extension API for custom plot types."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = ["LICENSE"]
@@ -36,6 +36,7 @@ classifiers = [
36
36
  ]
37
37
  dependencies = [
38
38
  "fonttools>=4.0",
39
+ "scipy>=1.10",
39
40
  ]
40
41
 
41
42
  [project.optional-dependencies]