plotlet 0.5.0__tar.gz → 0.6.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 (210) hide show
  1. {plotlet-0.5.0/src/plotlet.egg-info → plotlet-0.6.0}/PKG-INFO +20 -7
  2. plotlet-0.6.0/README.md +58 -0
  3. {plotlet-0.5.0 → plotlet-0.6.0}/pyproject.toml +14 -4
  4. plotlet-0.6.0/src/plotlet/__init__.py +71 -0
  5. plotlet-0.6.0/src/plotlet/_coord_registry.py +50 -0
  6. plotlet-0.6.0/src/plotlet/_datasets/anscombe.csv +45 -0
  7. plotlet-0.6.0/src/plotlet/_datasets/flights.csv +145 -0
  8. plotlet-0.6.0/src/plotlet/_datasets/tips.csv +245 -0
  9. plotlet-0.6.0/src/plotlet/_ir.py +452 -0
  10. plotlet-0.6.0/src/plotlet/_journal.py +377 -0
  11. plotlet-0.6.0/src/plotlet/_json_layer.py +144 -0
  12. plotlet-0.6.0/src/plotlet/_regions.py +143 -0
  13. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/_spec.py +42 -0
  14. plotlet-0.6.0/src/plotlet/_splits.py +105 -0
  15. plotlet-0.6.0/src/plotlet/_tree.py +113 -0
  16. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/__init__.py +2 -1
  17. plotlet-0.6.0/src/plotlet/artists/_marching.py +140 -0
  18. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/_shared.py +29 -16
  19. plotlet-0.6.0/src/plotlet/artists/bar.py +421 -0
  20. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/boxplot.py +48 -38
  21. plotlet-0.6.0/src/plotlet/artists/contour.py +165 -0
  22. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/dendrogram.py +56 -37
  23. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/density_1d.py +31 -43
  24. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/ecdf.py +24 -31
  25. plotlet-0.6.0/src/plotlet/artists/errorbar.py +263 -0
  26. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/fills.py +38 -55
  27. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/freqpoly.py +24 -34
  28. plotlet-0.6.0/src/plotlet/artists/heatmap.py +704 -0
  29. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/hexbin.py +54 -20
  30. plotlet-0.6.0/src/plotlet/artists/hist.py +367 -0
  31. plotlet-0.6.0/src/plotlet/artists/hist2d.py +180 -0
  32. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/imshow.py +28 -30
  33. plotlet-0.6.0/src/plotlet/artists/kde_2d.py +230 -0
  34. plotlet-0.6.0/src/plotlet/artists/line.py +275 -0
  35. plotlet-0.6.0/src/plotlet/artists/pointplot.py +140 -0
  36. plotlet-0.6.0/src/plotlet/artists/qq.py +129 -0
  37. plotlet-0.6.0/src/plotlet/artists/references.py +367 -0
  38. plotlet-0.6.0/src/plotlet/artists/regression.py +446 -0
  39. plotlet-0.6.0/src/plotlet/artists/ridge.py +142 -0
  40. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/rug.py +32 -38
  41. plotlet-0.6.0/src/plotlet/artists/scatter.py +434 -0
  42. plotlet-0.6.0/src/plotlet/artists/shapes.py +185 -0
  43. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/strip.py +26 -25
  44. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/swarm.py +31 -27
  45. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/text.py +92 -62
  46. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/artists/violin.py +54 -39
  47. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/chart.py +552 -417
  48. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/cluster.py +43 -64
  49. plotlet-0.5.0/src/plotlet/data.py → plotlet-0.6.0/src/plotlet/datasets.py +28 -4
  50. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/draw/__init__.py +16 -8
  51. plotlet-0.6.0/src/plotlet/draw/_css_colors.py +159 -0
  52. plotlet-0.6.0/src/plotlet/draw/_png.py +78 -0
  53. plotlet-0.6.0/src/plotlet/draw/colormaps.py +219 -0
  54. plotlet-0.6.0/src/plotlet/draw/colors.py +172 -0
  55. plotlet-0.6.0/src/plotlet/draw/font.py +352 -0
  56. plotlet-0.6.0/src/plotlet/draw/fonts/Arimo-Bold.ttf +0 -0
  57. plotlet-0.6.0/src/plotlet/draw/fonts/Arimo-BoldItalic.ttf +0 -0
  58. plotlet-0.6.0/src/plotlet/draw/fonts/Arimo-Italic.ttf +0 -0
  59. plotlet-0.6.0/src/plotlet/draw/fonts/Arimo-Regular.ttf +0 -0
  60. plotlet-0.6.0/src/plotlet/draw/fonts/DejaVuSans-Bold.ttf +0 -0
  61. plotlet-0.6.0/src/plotlet/draw/fonts/DejaVuSans-BoldOblique.ttf +0 -0
  62. plotlet-0.6.0/src/plotlet/draw/fonts/DejaVuSans-Oblique.ttf +0 -0
  63. plotlet-0.6.0/src/plotlet/draw/fonts/LICENSE-Arimo.txt +93 -0
  64. plotlet-0.6.0/src/plotlet/draw/fonts/LICENSE-DejaVuSans.txt +99 -0
  65. plotlet-0.6.0/src/plotlet/draw/format.py +34 -0
  66. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/draw/primitives.py +250 -72
  67. plotlet-0.6.0/src/plotlet/extensions/annotation_strip.py +675 -0
  68. plotlet-0.6.0/src/plotlet/extensions/chord_links.py +209 -0
  69. plotlet-0.6.0/src/plotlet/extensions/chord_ribbon.py +318 -0
  70. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/extensions/curved_tree.py +61 -44
  71. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/extensions/numeric_bar.py +21 -10
  72. plotlet-0.6.0/src/plotlet/facet.py +245 -0
  73. plotlet-0.6.0/src/plotlet/formatters.py +164 -0
  74. plotlet-0.6.0/src/plotlet/layout_diagram.py +316 -0
  75. plotlet-0.6.0/src/plotlet/legend.py +120 -0
  76. plotlet-0.6.0/src/plotlet/lint.py +130 -0
  77. plotlet-0.6.0/src/plotlet/registry.py +324 -0
  78. plotlet-0.6.0/src/plotlet/render/__init__.py +77 -0
  79. {plotlet-0.5.0/src/plotlet → plotlet-0.6.0/src/plotlet/render}/_attachments.py +89 -30
  80. plotlet-0.6.0/src/plotlet/render/_chrome.py +953 -0
  81. plotlet-0.6.0/src/plotlet/render/_chrome_circular.py +360 -0
  82. plotlet-0.6.0/src/plotlet/render/_layout_engine.py +1427 -0
  83. plotlet-0.6.0/src/plotlet/render/_legend.py +645 -0
  84. plotlet-0.6.0/src/plotlet/render/_nodes.py +521 -0
  85. plotlet-0.6.0/src/plotlet/render/_validate.py +289 -0
  86. plotlet-0.6.0/src/plotlet/render/coordinates.py +697 -0
  87. plotlet-0.6.0/src/plotlet/render/core.py +2100 -0
  88. plotlet-0.6.0/src/plotlet/render/resolved.py +717 -0
  89. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/scales.py +159 -5
  90. plotlet-0.6.0/src/plotlet/sectors.py +330 -0
  91. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/spec.json +35 -9
  92. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/themes.py +3 -3
  93. plotlet-0.6.0/src/plotlet/utils.py +626 -0
  94. {plotlet-0.5.0 → plotlet-0.6.0/src/plotlet.egg-info}/PKG-INFO +20 -7
  95. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet.egg-info/SOURCES.txt +55 -50
  96. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet.egg-info/requires.txt +5 -0
  97. plotlet-0.6.0/tests/test_attachments.py +287 -0
  98. plotlet-0.6.0/tests/test_chart.py +3594 -0
  99. plotlet-0.6.0/tests/test_circular_coordinate.py +581 -0
  100. plotlet-0.6.0/tests/test_cluster.py +67 -0
  101. plotlet-0.6.0/tests/test_colors.py +247 -0
  102. plotlet-0.6.0/tests/test_datasets.py +53 -0
  103. plotlet-0.6.0/tests/test_extensions.py +40 -0
  104. plotlet-0.6.0/tests/test_fonts.py +310 -0
  105. plotlet-0.6.0/tests/test_formatters.py +64 -0
  106. plotlet-0.6.0/tests/test_help.py +25 -0
  107. plotlet-0.6.0/tests/test_import_boundary.py +149 -0
  108. plotlet-0.6.0/tests/test_io.py +61 -0
  109. plotlet-0.6.0/tests/test_ir.py +265 -0
  110. plotlet-0.6.0/tests/test_ir_resolved.py +289 -0
  111. plotlet-0.6.0/tests/test_ir_validate.py +318 -0
  112. plotlet-0.6.0/tests/test_journal_roundtrip.py +127 -0
  113. {plotlet-0.5.0 → plotlet-0.6.0}/tests/test_layout_diagram.py +11 -11
  114. plotlet-0.6.0/tests/test_legend.py +288 -0
  115. plotlet-0.6.0/tests/test_lint.py +23 -0
  116. plotlet-0.6.0/tests/test_record_signatures.py +75 -0
  117. plotlet-0.6.0/tests/test_sectors.py +393 -0
  118. {plotlet-0.5.0 → plotlet-0.6.0}/tests/test_subplots.py +208 -71
  119. {plotlet-0.5.0 → plotlet-0.6.0}/tests/test_themes.py +8 -7
  120. {plotlet-0.5.0 → plotlet-0.6.0}/tests/test_units.py +17 -17
  121. plotlet-0.5.0/README.md +0 -50
  122. plotlet-0.5.0/src/plotlet/__init__.py +0 -42
  123. plotlet-0.5.0/src/plotlet/_layout_engine.py +0 -909
  124. plotlet-0.5.0/src/plotlet/_splits.py +0 -190
  125. plotlet-0.5.0/src/plotlet/artists/bar.py +0 -243
  126. plotlet-0.5.0/src/plotlet/artists/contour.py +0 -129
  127. plotlet-0.5.0/src/plotlet/artists/errorbar.py +0 -183
  128. plotlet-0.5.0/src/plotlet/artists/heatmap.py +0 -436
  129. plotlet-0.5.0/src/plotlet/artists/hist.py +0 -193
  130. plotlet-0.5.0/src/plotlet/artists/kde_2d.py +0 -145
  131. plotlet-0.5.0/src/plotlet/artists/line.py +0 -152
  132. plotlet-0.5.0/src/plotlet/artists/pointplot.py +0 -150
  133. plotlet-0.5.0/src/plotlet/artists/qq.py +0 -83
  134. plotlet-0.5.0/src/plotlet/artists/references.py +0 -224
  135. plotlet-0.5.0/src/plotlet/artists/regression.py +0 -179
  136. plotlet-0.5.0/src/plotlet/artists/ridge.py +0 -98
  137. plotlet-0.5.0/src/plotlet/artists/scatter.py +0 -279
  138. plotlet-0.5.0/src/plotlet/artists/shapes.py +0 -126
  139. plotlet-0.5.0/src/plotlet/core.py +0 -1973
  140. plotlet-0.5.0/src/plotlet/draw/_png.py +0 -38
  141. plotlet-0.5.0/src/plotlet/draw/colormaps.py +0 -113
  142. plotlet-0.5.0/src/plotlet/draw/colors.py +0 -41
  143. plotlet-0.5.0/src/plotlet/draw/font.py +0 -117
  144. plotlet-0.5.0/src/plotlet/extensions/__init__.py +0 -22
  145. plotlet-0.5.0/src/plotlet/extensions/_gallery.py +0 -205
  146. plotlet-0.5.0/src/plotlet/extensions/alluvial.py +0 -202
  147. plotlet-0.5.0/src/plotlet/extensions/annotation_strip.py +0 -305
  148. plotlet-0.5.0/src/plotlet/extensions/bland_altman.py +0 -93
  149. plotlet-0.5.0/src/plotlet/extensions/boxenplot.py +0 -251
  150. plotlet-0.5.0/src/plotlet/extensions/bubble_grid.py +0 -106
  151. plotlet-0.5.0/src/plotlet/extensions/bump.py +0 -116
  152. plotlet-0.5.0/src/plotlet/extensions/calendar_heatmap.py +0 -130
  153. plotlet-0.5.0/src/plotlet/extensions/calibration_plot.py +0 -135
  154. plotlet-0.5.0/src/plotlet/extensions/cleveland_dot.py +0 -96
  155. plotlet-0.5.0/src/plotlet/extensions/confusion_matrix.py +0 -149
  156. plotlet-0.5.0/src/plotlet/extensions/crossbar.py +0 -98
  157. plotlet-0.5.0/src/plotlet/extensions/diverging_bar.py +0 -99
  158. plotlet-0.5.0/src/plotlet/extensions/dumbbell.py +0 -90
  159. plotlet-0.5.0/src/plotlet/extensions/eventplot.py +0 -114
  160. plotlet-0.5.0/src/plotlet/extensions/forest_plot.py +0 -127
  161. plotlet-0.5.0/src/plotlet/extensions/funnel_plot.py +0 -126
  162. plotlet-0.5.0/src/plotlet/extensions/gene_arrow.py +0 -109
  163. plotlet-0.5.0/src/plotlet/extensions/horizon.py +0 -132
  164. plotlet-0.5.0/src/plotlet/extensions/horizontal_bar.py +0 -80
  165. plotlet-0.5.0/src/plotlet/extensions/jointplot.py +0 -90
  166. plotlet-0.5.0/src/plotlet/extensions/km_curve.py +0 -206
  167. plotlet-0.5.0/src/plotlet/extensions/labels_strip.py +0 -246
  168. plotlet-0.5.0/src/plotlet/extensions/loess.py +0 -95
  169. plotlet-0.5.0/src/plotlet/extensions/lollipop.py +0 -95
  170. plotlet-0.5.0/src/plotlet/extensions/ma_plot.py +0 -113
  171. plotlet-0.5.0/src/plotlet/extensions/manhattan.py +0 -133
  172. plotlet-0.5.0/src/plotlet/extensions/mosaic.py +0 -143
  173. plotlet-0.5.0/src/plotlet/extensions/pair_plot.py +0 -90
  174. plotlet-0.5.0/src/plotlet/extensions/parallel_coordinates.py +0 -126
  175. plotlet-0.5.0/src/plotlet/extensions/pca_biplot.py +0 -113
  176. plotlet-0.5.0/src/plotlet/extensions/percentile_band.py +0 -108
  177. plotlet-0.5.0/src/plotlet/extensions/pr_curve.py +0 -126
  178. plotlet-0.5.0/src/plotlet/extensions/pyramid_plot.py +0 -97
  179. plotlet-0.5.0/src/plotlet/extensions/raincloud.py +0 -309
  180. plotlet-0.5.0/src/plotlet/extensions/residual_diagnostics.py +0 -112
  181. plotlet-0.5.0/src/plotlet/extensions/roc_curve.py +0 -123
  182. plotlet-0.5.0/src/plotlet/extensions/sales_funnel.py +0 -86
  183. plotlet-0.5.0/src/plotlet/extensions/sankey.py +0 -257
  184. plotlet-0.5.0/src/plotlet/extensions/significance_brackets.py +0 -132
  185. plotlet-0.5.0/src/plotlet/extensions/slope_chart.py +0 -88
  186. plotlet-0.5.0/src/plotlet/extensions/split_violin.py +0 -177
  187. plotlet-0.5.0/src/plotlet/extensions/text_label.py +0 -78
  188. plotlet-0.5.0/src/plotlet/extensions/upset_plot.py +0 -152
  189. plotlet-0.5.0/src/plotlet/extensions/volcano.py +0 -133
  190. plotlet-0.5.0/src/plotlet/extensions/waterfall.py +0 -131
  191. plotlet-0.5.0/src/plotlet/facet.py +0 -163
  192. plotlet-0.5.0/src/plotlet/layout_diagram.py +0 -237
  193. plotlet-0.5.0/src/plotlet/legend.py +0 -414
  194. plotlet-0.5.0/src/plotlet/registry.py +0 -114
  195. plotlet-0.5.0/src/plotlet/utils.py +0 -319
  196. plotlet-0.5.0/tests/test_attachments.py +0 -193
  197. plotlet-0.5.0/tests/test_chart.py +0 -1844
  198. plotlet-0.5.0/tests/test_extensions.py +0 -74
  199. plotlet-0.5.0/tests/test_legend.py +0 -148
  200. {plotlet-0.5.0 → plotlet-0.6.0}/LICENSE +0 -0
  201. {plotlet-0.5.0 → plotlet-0.6.0}/setup.cfg +0 -0
  202. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/_datasets/penguins.csv +0 -0
  203. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/draw/_cm_data.py +0 -0
  204. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/draw/fonts/DejaVuSans.ttf +0 -0
  205. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/draw/linestyles.py +0 -0
  206. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/themes/dark.json +0 -0
  207. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/themes/minimal.json +0 -0
  208. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet/themes/void.json +0 -0
  209. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet.egg-info/dependency_links.txt +0 -0
  210. {plotlet-0.5.0 → plotlet-0.6.0}/src/plotlet.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotlet
3
- Version: 0.5.0
4
- Summary: Python library for SVG plots, with multi-panel composition and an extension API for custom plot types.
3
+ Version: 0.6.0
4
+ Summary: Python library for reproducible, multi-panel scientific figures, with byte-identical output across machines 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
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3.10
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
19
20
  Classifier: Topic :: Scientific/Engineering :: Visualization
20
21
  Classifier: Topic :: Multimedia :: Graphics
21
22
  Requires-Python: >=3.10
@@ -23,41 +24,53 @@ Description-Content-Type: text/markdown
23
24
  License-File: LICENSE
24
25
  Requires-Dist: fonttools>=4.0
25
26
  Requires-Dist: scipy>=1.10
27
+ Requires-Dist: resvg_py>=0.3
26
28
  Provides-Extra: dev
27
29
  Requires-Dist: jupyter; extra == "dev"
28
30
  Requires-Dist: nbconvert; extra == "dev"
29
31
  Provides-Extra: test
30
32
  Requires-Dist: pytest>=7; extra == "test"
33
+ Requires-Dist: pandas; extra == "test"
34
+ Provides-Extra: pdf
35
+ Requires-Dist: cairosvg; extra == "pdf"
31
36
  Dynamic: license-file
32
37
 
33
38
  # plotlet
34
39
 
35
- plotlet is a Python library for SVG plots. It provides reproducible, byte-identical multi-panel scientific figures with a standard plotting vocabulary and a first-class extension story for custom plot types.
40
+ plotlet is a Python library for reproducible, multi-panel scientific figures. Byte-identical output across machines, a standard plotting vocabulary, and a first-class extension story for custom plot types.
36
41
 
37
42
  ## Documentation
38
43
 
39
44
  Hands-on tour with executable examples:
40
45
 
41
- - [notebooks/00_introduction.ipynb](notebooks/00_introduction.ipynb) — long-form data, aesthetic inheritance, layering, composition
46
+ - [notebooks/00_introduction.ipynb](notebooks/00_introduction.ipynb) — long-form data, aesthetic inheritance, layering, composition (more topic notebooks in [`notebooks/`](notebooks/))
42
47
 
43
48
  Reference docs in [`docs/`](docs/):
44
49
 
45
50
  - [API reference](docs/API.md) — mark methods, frame options, scales, tick overrides
46
51
  - [Philosophy](docs/PHILOSOPHY.md) — what's in core, what's not, and why
47
52
  - [Subplots](docs/SUBPLOTS.md) — multi-panel composition, shared scales
53
+ - [Coordinates](docs/COORDINATES.md) — Circular, custom non-Cartesian projections
48
54
  - [Extending](docs/EXTENDING.md) — write your own plot type
49
55
  - [Themes](docs/THEMES.md) — visual presets
50
56
  - [AI attributes](docs/AI_ATTRS.md) — `data-plotlet-*` schema for automation
51
57
 
52
- Reference plot types beyond the standard vocabulary live in [`src/plotlet/extensions/`](src/plotlet/extensions/) (single-file) and [`cookbook/`](cookbook/) (multi-file projects).
58
+ Reference plot types beyond the standard vocabulary ship in the separate [`plotlet-extensions`](https://github.com/gitbamboo42/plotlet-extensions) package (single-file artists — sankey, alluvial, raincloud, upset_plot, …; `pip install plotlet-extensions`, then `import plotlet.extensions.<name>`) and in the [`plotlet-cookbook`](https://github.com/gitbamboo42/plotlet-cookbook) repo (multi-file, domain-specific projects). A few extensions the core tests depend on still ship in core under [`src/plotlet/extensions/`](src/plotlet/extensions/).
59
+
60
+ ## For AI assistants
61
+
62
+ Compact, vendor-neutral onboarding docs live in [`skills/`](skills/):
63
+
64
+ - [`skills/users.md`](skills/users.md) — for AI tools generating plotlet code on a user's behalf.
65
+ - [`skills/developers.md`](skills/developers.md) — for AI (or human) contributors working on plotlet itself. Symlink as `CLAUDE.md` / `.cursorrules` / `AGENTS.md` as your tool expects.
53
66
 
54
67
  ## Dependencies
55
68
 
56
69
  plotlet supports Python 3.10+.
57
70
 
58
- Required: `fonttools`, `scipy` (used by regression, qq, pointplot, dendrogram). numpy / pandas / polars inputs work transparently.
71
+ Required: `fonttools`, `scipy` (regression, qq, CI error bands, clustering/dendrogram), `resvg_py` (PNG rendering — prebuilt wheels, no system libraries). numpy / pandas / polars inputs work transparently.
59
72
 
60
- Optional: `cairosvg` for PNG / PDF export.
73
+ Optional: `cairosvg` for PDF export (`pip install plotlet[pdf]`).
61
74
 
62
75
  ## Installation
63
76
 
@@ -0,0 +1,58 @@
1
+ # plotlet
2
+
3
+ plotlet is a Python library for reproducible, multi-panel scientific figures. Byte-identical output across machines, a standard plotting vocabulary, and a first-class extension story for custom plot types.
4
+
5
+ ## Documentation
6
+
7
+ Hands-on tour with executable examples:
8
+
9
+ - [notebooks/00_introduction.ipynb](notebooks/00_introduction.ipynb) — long-form data, aesthetic inheritance, layering, composition (more topic notebooks in [`notebooks/`](notebooks/))
10
+
11
+ Reference docs in [`docs/`](docs/):
12
+
13
+ - [API reference](docs/API.md) — mark methods, frame options, scales, tick overrides
14
+ - [Philosophy](docs/PHILOSOPHY.md) — what's in core, what's not, and why
15
+ - [Subplots](docs/SUBPLOTS.md) — multi-panel composition, shared scales
16
+ - [Coordinates](docs/COORDINATES.md) — Circular, custom non-Cartesian projections
17
+ - [Extending](docs/EXTENDING.md) — write your own plot type
18
+ - [Themes](docs/THEMES.md) — visual presets
19
+ - [AI attributes](docs/AI_ATTRS.md) — `data-plotlet-*` schema for automation
20
+
21
+ Reference plot types beyond the standard vocabulary ship in the separate [`plotlet-extensions`](https://github.com/gitbamboo42/plotlet-extensions) package (single-file artists — sankey, alluvial, raincloud, upset_plot, …; `pip install plotlet-extensions`, then `import plotlet.extensions.<name>`) and in the [`plotlet-cookbook`](https://github.com/gitbamboo42/plotlet-cookbook) repo (multi-file, domain-specific projects). A few extensions the core tests depend on still ship in core under [`src/plotlet/extensions/`](src/plotlet/extensions/).
22
+
23
+ ## For AI assistants
24
+
25
+ Compact, vendor-neutral onboarding docs live in [`skills/`](skills/):
26
+
27
+ - [`skills/users.md`](skills/users.md) — for AI tools generating plotlet code on a user's behalf.
28
+ - [`skills/developers.md`](skills/developers.md) — for AI (or human) contributors working on plotlet itself. Symlink as `CLAUDE.md` / `.cursorrules` / `AGENTS.md` as your tool expects.
29
+
30
+ ## Dependencies
31
+
32
+ plotlet supports Python 3.10+.
33
+
34
+ Required: `fonttools`, `scipy` (regression, qq, CI error bands, clustering/dendrogram), `resvg_py` (PNG rendering — prebuilt wheels, no system libraries). numpy / pandas / polars inputs work transparently.
35
+
36
+ Optional: `cairosvg` for PDF export (`pip install plotlet[pdf]`).
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install plotlet
42
+ ```
43
+
44
+ ## Testing
45
+
46
+ ```bash
47
+ pip install -e ".[test]"
48
+ pytest tests/ # check vs. committed baselines
49
+ pytest tests/ --update # regenerate (review the diff!)
50
+ ```
51
+
52
+ ## Development
53
+
54
+ Development takes place on GitHub. Please submit bugs to the issue tracker with a reproducible example.
55
+
56
+ ## License
57
+
58
+ MIT
@@ -1,11 +1,11 @@
1
1
  [build-system]
2
- requires = ["setuptools>=64", "wheel"]
2
+ requires = ["setuptools>=77", "wheel"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "plotlet"
7
- version = "0.5.0"
8
- description = "Python library for SVG plots, with multi-panel composition and an extension API for custom plot types."
7
+ version = "0.6.0"
8
+ description = "Python library for reproducible, multi-panel scientific figures, with byte-identical output across machines and an extension API for custom plot types."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = ["LICENSE"]
@@ -30,12 +30,14 @@ classifiers = [
30
30
  "Programming Language :: Python :: 3.10",
31
31
  "Programming Language :: Python :: 3.11",
32
32
  "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3.13",
33
34
  "Topic :: Scientific/Engineering :: Visualization",
34
35
  "Topic :: Multimedia :: Graphics",
35
36
  ]
36
37
  dependencies = [
37
38
  "fonttools>=4.0",
38
39
  "scipy>=1.10",
40
+ "resvg_py>=0.3",
39
41
  ]
40
42
 
41
43
  [project.optional-dependencies]
@@ -45,6 +47,10 @@ dev = [
45
47
  ]
46
48
  test = [
47
49
  "pytest>=7",
50
+ "pandas",
51
+ ]
52
+ pdf = [
53
+ "cairosvg",
48
54
  ]
49
55
 
50
56
  [tool.pytest.ini_options]
@@ -58,6 +64,10 @@ Issues = "https://github.com/gitbamboo42/plotlet/issues"
58
64
 
59
65
  [tool.setuptools.packages.find]
60
66
  where = ["src"]
67
+ namespaces = true
61
68
 
62
69
  [tool.setuptools.package-data]
63
- plotlet = ["spec.json", "themes/*.json", "draw/fonts/*.ttf", "_datasets/*.csv"]
70
+ # draw/fonts/*.txt are the font licenses — DejaVu (Bitstream Vera–derived)
71
+ # and Arimo (SIL OFL 1.1) both require the license text to accompany
72
+ # redistribution, so they must ship in the wheel next to the TTFs.
73
+ plotlet = ["spec.json", "themes/*.json", "draw/fonts/*.ttf", "draw/fonts/*.txt", "_datasets/*.csv"]
@@ -0,0 +1,71 @@
1
+ """plotlet — pure-Python deferred-rendering SVG plot library.
2
+
3
+ import plotlet as pt
4
+ c = pt.chart(df, title="...", xlabel="x", ylabel="y", legend=True, gridlines=True)
5
+ c.line(x="time", y="value", color="series")
6
+ c # auto-renders in Jupyter
7
+
8
+ Chart methods chain for incremental composition:
9
+
10
+ df = {"x": [1, 2, 3], "y": [1, 4, 9]}
11
+ c = pt.chart()
12
+ c.line(df, x="x", y="y", label="squares")
13
+ c.title("Hello").legend().gridlines(True)
14
+ c
15
+ """
16
+ from ._spec import SPEC
17
+ from .draw import TAB10, palette, list_palettes
18
+ from .draw import colormap, list_colormaps, register_colormap
19
+ from . import artists # noqa: F401 — registers built-in artists on import
20
+ from .chart import Chart, Layout, chart, grid
21
+ from .legend import legend
22
+ from .registry import ArtistSpec, add_artist, artist_table, declare_coord_support
23
+ from .sectors import Sectors
24
+ from .layout_diagram import layout_diagram
25
+ from .themes import load_theme, list_themes, register_theme
26
+ from .facet import facet, FacetGrid
27
+ from .datasets import load_dataset, list_datasets
28
+ from .cluster import linkage, linkage_split, SplitTree
29
+ from .formatters import (register_formatter, list_formatters,
30
+ superscript, subscript)
31
+ from ._coord_registry import register_coord_codec
32
+ from ._journal import (to_journal, from_journal, to_json, from_json,
33
+ JournalNode, Journal)
34
+ from ._ir import to_ir, from_ir, resolve_ir, FigureIR, IRNode
35
+ from . import draw, utils
36
+
37
+
38
+ def __getattr__(name):
39
+ # The one render-half class in the top-level namespace stays a lazy
40
+ # import so that `import plotlet` loads only the recording half —
41
+ # the render half loads on first render (or first touch of this
42
+ # name). Enforced by tests/test_import_boundary.py.
43
+ if name == "CircularCoordinate":
44
+ from .render.coordinates import CircularCoordinate
45
+ return CircularCoordinate
46
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
47
+
48
+ __all__ = ["chart", "Chart", "Layout", "SPEC", "TAB10",
49
+ "palette", "list_palettes",
50
+ "colormap", "list_colormaps", "register_colormap",
51
+ "grid", "legend",
52
+ "ArtistSpec", "add_artist", "artist_table", "declare_coord_support",
53
+ "CircularCoordinate", "Sectors",
54
+ "layout_diagram",
55
+ "load_theme", "list_themes", "register_theme",
56
+ "facet", "FacetGrid",
57
+ "load_dataset", "list_datasets",
58
+ "linkage", "linkage_split", "SplitTree",
59
+ "register_formatter", "list_formatters",
60
+ "superscript", "subscript",
61
+ "to_json", "from_json", "register_coord_codec",
62
+ "to_journal", "from_journal", "JournalNode", "Journal",
63
+ "to_ir", "from_ir", "FigureIR", "IRNode", "resolve_ir",
64
+ "draw", "utils"]
65
+
66
+ # Single source of truth: pyproject.toml. importlib.metadata reads it at
67
+ # runtime from the installed package metadata (works for `pip install` and
68
+ # `pip install -e .` alike). Migrating to setuptools_scm later doesn't
69
+ # touch this code — it just changes how pyproject.toml's version is set.
70
+ from importlib.metadata import version as _pkg_version
71
+ __version__ = _pkg_version("plotlet")
@@ -0,0 +1,50 @@
1
+ """Coord codec registry — a neutral home for the `class_name → class` map
2
+ that both `serialize.py` and `_journal.py` need for round-tripping coord
3
+ objects like `CircularCoordinate`.
4
+
5
+ Lives here rather than in either consumer so that removing one of them
6
+ later (e.g. retiring the JSON tree serializer once the journal covers
7
+ the same ground) doesn't leave the registry orphaned. The public API
8
+ `plotlet.register_coord_codec` re-exports from this module directly.
9
+ """
10
+ from __future__ import annotations
11
+
12
+
13
+ # Populated by `register_coord_codec`; coord classes register themselves
14
+ # at their definition site (or serialize.py registers built-ins on module
15
+ # import, for the ones that ship with plotlet).
16
+ _COORD_REGISTRY: dict[str, type] = {}
17
+
18
+
19
+ def register_coord_codec(cls: type) -> type:
20
+ """Register `cls` as a serializable coord. The class must define
21
+ `_to_dict(self) -> dict` and `_from_dict(cls, dict) -> cls`. Returns
22
+ `cls` so this can be used as a decorator."""
23
+ if not hasattr(cls, "_to_dict") or not hasattr(cls, "_from_dict"):
24
+ raise TypeError(
25
+ f"register_coord_codec: {cls.__name__} must define "
26
+ f"`_to_dict` and `_from_dict`."
27
+ )
28
+ _COORD_REGISTRY[cls.__name__] = cls
29
+ return cls
30
+
31
+
32
+ def resolve_coord(name: str) -> type:
33
+ """Look up a coord class by registered name, importing the built-in
34
+ coord module on first miss — built-ins register at their definition
35
+ site, and nothing else guarantees that module has been imported when
36
+ a `{"$coord": ...}` envelope arrives from JSON in a fresh process."""
37
+ try:
38
+ return _COORD_REGISTRY[name]
39
+ except KeyError:
40
+ pass
41
+ from .render import coordinates # noqa: F401 — registers built-ins
42
+ try:
43
+ return _COORD_REGISTRY[name]
44
+ except KeyError:
45
+ raise KeyError(
46
+ f"unknown coordinate {name!r}. Registered coords: "
47
+ f"{sorted(_COORD_REGISTRY)}. Custom coord classes must be "
48
+ f"imported (and registered via `register_coord_codec`) before "
49
+ f"a figure referencing them is decoded."
50
+ ) from None
@@ -0,0 +1,45 @@
1
+ dataset,x,y
2
+ I,10.0,8.04
3
+ I,8.0,6.95
4
+ I,13.0,7.58
5
+ I,9.0,8.81
6
+ I,11.0,8.33
7
+ I,14.0,9.96
8
+ I,6.0,7.24
9
+ I,4.0,4.26
10
+ I,12.0,10.84
11
+ I,7.0,4.82
12
+ I,5.0,5.68
13
+ II,10.0,9.14
14
+ II,8.0,8.14
15
+ II,13.0,8.74
16
+ II,9.0,8.77
17
+ II,11.0,9.26
18
+ II,14.0,8.1
19
+ II,6.0,6.13
20
+ II,4.0,3.1
21
+ II,12.0,9.13
22
+ II,7.0,7.26
23
+ II,5.0,4.74
24
+ III,10.0,7.46
25
+ III,8.0,6.77
26
+ III,13.0,12.74
27
+ III,9.0,7.11
28
+ III,11.0,7.81
29
+ III,14.0,8.84
30
+ III,6.0,6.08
31
+ III,4.0,5.39
32
+ III,12.0,8.15
33
+ III,7.0,6.42
34
+ III,5.0,5.73
35
+ IV,8.0,6.58
36
+ IV,8.0,5.76
37
+ IV,8.0,7.71
38
+ IV,8.0,8.84
39
+ IV,8.0,8.47
40
+ IV,8.0,7.04
41
+ IV,8.0,5.25
42
+ IV,19.0,12.5
43
+ IV,8.0,5.56
44
+ IV,8.0,7.91
45
+ IV,8.0,6.89
@@ -0,0 +1,145 @@
1
+ year,month,passengers
2
+ 1949,January,112
3
+ 1949,February,118
4
+ 1949,March,132
5
+ 1949,April,129
6
+ 1949,May,121
7
+ 1949,June,135
8
+ 1949,July,148
9
+ 1949,August,148
10
+ 1949,September,136
11
+ 1949,October,119
12
+ 1949,November,104
13
+ 1949,December,118
14
+ 1950,January,115
15
+ 1950,February,126
16
+ 1950,March,141
17
+ 1950,April,135
18
+ 1950,May,125
19
+ 1950,June,149
20
+ 1950,July,170
21
+ 1950,August,170
22
+ 1950,September,158
23
+ 1950,October,133
24
+ 1950,November,114
25
+ 1950,December,140
26
+ 1951,January,145
27
+ 1951,February,150
28
+ 1951,March,178
29
+ 1951,April,163
30
+ 1951,May,172
31
+ 1951,June,178
32
+ 1951,July,199
33
+ 1951,August,199
34
+ 1951,September,184
35
+ 1951,October,162
36
+ 1951,November,146
37
+ 1951,December,166
38
+ 1952,January,171
39
+ 1952,February,180
40
+ 1952,March,193
41
+ 1952,April,181
42
+ 1952,May,183
43
+ 1952,June,218
44
+ 1952,July,230
45
+ 1952,August,242
46
+ 1952,September,209
47
+ 1952,October,191
48
+ 1952,November,172
49
+ 1952,December,194
50
+ 1953,January,196
51
+ 1953,February,196
52
+ 1953,March,236
53
+ 1953,April,235
54
+ 1953,May,229
55
+ 1953,June,243
56
+ 1953,July,264
57
+ 1953,August,272
58
+ 1953,September,237
59
+ 1953,October,211
60
+ 1953,November,180
61
+ 1953,December,201
62
+ 1954,January,204
63
+ 1954,February,188
64
+ 1954,March,235
65
+ 1954,April,227
66
+ 1954,May,234
67
+ 1954,June,264
68
+ 1954,July,302
69
+ 1954,August,293
70
+ 1954,September,259
71
+ 1954,October,229
72
+ 1954,November,203
73
+ 1954,December,229
74
+ 1955,January,242
75
+ 1955,February,233
76
+ 1955,March,267
77
+ 1955,April,269
78
+ 1955,May,270
79
+ 1955,June,315
80
+ 1955,July,364
81
+ 1955,August,347
82
+ 1955,September,312
83
+ 1955,October,274
84
+ 1955,November,237
85
+ 1955,December,278
86
+ 1956,January,284
87
+ 1956,February,277
88
+ 1956,March,317
89
+ 1956,April,313
90
+ 1956,May,318
91
+ 1956,June,374
92
+ 1956,July,413
93
+ 1956,August,405
94
+ 1956,September,355
95
+ 1956,October,306
96
+ 1956,November,271
97
+ 1956,December,306
98
+ 1957,January,315
99
+ 1957,February,301
100
+ 1957,March,356
101
+ 1957,April,348
102
+ 1957,May,355
103
+ 1957,June,422
104
+ 1957,July,465
105
+ 1957,August,467
106
+ 1957,September,404
107
+ 1957,October,347
108
+ 1957,November,305
109
+ 1957,December,336
110
+ 1958,January,340
111
+ 1958,February,318
112
+ 1958,March,362
113
+ 1958,April,348
114
+ 1958,May,363
115
+ 1958,June,435
116
+ 1958,July,491
117
+ 1958,August,505
118
+ 1958,September,404
119
+ 1958,October,359
120
+ 1958,November,310
121
+ 1958,December,337
122
+ 1959,January,360
123
+ 1959,February,342
124
+ 1959,March,406
125
+ 1959,April,396
126
+ 1959,May,420
127
+ 1959,June,472
128
+ 1959,July,548
129
+ 1959,August,559
130
+ 1959,September,463
131
+ 1959,October,407
132
+ 1959,November,362
133
+ 1959,December,405
134
+ 1960,January,417
135
+ 1960,February,391
136
+ 1960,March,419
137
+ 1960,April,461
138
+ 1960,May,472
139
+ 1960,June,535
140
+ 1960,July,622
141
+ 1960,August,606
142
+ 1960,September,508
143
+ 1960,October,461
144
+ 1960,November,390
145
+ 1960,December,432