|
1 | | -# maxplotlib |
| 1 | +# Maxlotlib |
2 | 2 |
|
3 | 3 |
|
4 | | -# maxplotlib |
| 4 | +# Maxplotlib |
5 | 5 |
|
6 | | -A clean, expressive wrapper around **Matplotlib** for producing |
7 | | -publication-quality figures with minimal boilerplate. Swap backends |
8 | | -without rewriting your data — render the same canvas as a crisp PNG, an |
9 | | -interactive Plotly chart, or camera-ready **TikZ** code for LaTeX. |
| 6 | +A clean, expressive wrapper around **Matplotlib** **tikzfigure** for |
| 7 | +producing publication-quality figures with minimal boilerplate. Swap |
| 8 | +backends without rewriting your data — render the same canvas as a crisp |
| 9 | +PNG, an interactive Plotly chart, or camera-ready **TikZ** code for |
| 10 | +LaTeX. |
10 | 11 |
|
11 | 12 | ## Install |
12 | 13 |
|
13 | 14 | ``` bash |
14 | | -python -m venv env && source env/bin/activate |
15 | 15 | pip install maxplotlibx |
16 | 16 | ``` |
17 | 17 |
|
18 | | -For development extras (tests, docs, linting): |
19 | | - |
20 | | -``` bash |
21 | | -pip install "maxplotlibx[dev]" |
22 | | -``` |
23 | | - |
24 | 18 | ## Showcase |
25 | 19 |
|
26 | | -``` python |
27 | | -import numpy as np |
28 | | -from maxplotlib import Canvas |
29 | | - |
30 | | -rng = np.random.default_rng(0) |
31 | | -x = np.linspace(0, 2 * np.pi, 300) |
32 | | - |
33 | | -canvas, axes = Canvas.subplots(nrows=2, ncols=2, width="18cm", ratio=0.65) |
34 | | - |
35 | | -# ── top-left: multi-line with legend ────────────────────────────────────────── |
36 | | -ax = axes[0][0] |
37 | | -ax.plot(x, np.sin(x), color="royalblue", linewidth=2, label=r"$\sin(x)$") |
38 | | -ax.plot(x, np.cos(x), color="tomato", linewidth=2, |
39 | | - linestyle="dashed", label=r"$\cos(x)$") |
40 | | -ax.plot(x, np.sin(2 * x) * 0.5, color="seagreen", linewidth=1.5, |
41 | | - linestyle="dotted", label=r"$\frac{1}{2}\sin(2x)$") |
42 | | -ax.set_xlabel("x") |
43 | | -ax.set_ylabel("amplitude") |
44 | | -ax.set_title("Line plots") |
45 | | -ax.set_legend(True) |
46 | | -ax.set_grid(True) |
47 | | - |
48 | | -# ── top-right: scatter coloured by distance ──────────────────────────────────── |
49 | | -ax = axes[0][1] |
50 | | -n = 250 |
51 | | -sx = rng.standard_normal(n) |
52 | | -sy = rng.standard_normal(n) |
53 | | -ax.scatter(sx, sy, c=np.hypot(sx, sy), s=25, alpha=0.8) |
54 | | -ax.set_xlabel("x") |
55 | | -ax.set_ylabel("y") |
56 | | -ax.set_title("Scatter — coloured by distance") |
57 | | -ax.set_aspect("equal") |
58 | | - |
59 | | -# ── bottom-left: bar chart ──────────────────────────────────────────────────── |
60 | | -ax = axes[1][0] |
61 | | -months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", |
62 | | - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] |
63 | | -temps = [3, 4, 8, 13, 18, 22, 24, 23, 18, 12, 7, 4] |
64 | | -month_idx = np.arange(len(months)) |
65 | | -ax.bar(month_idx, temps, color="steelblue", width=0.7, label="avg °C") |
66 | | -ax.set_xticks(month_idx, labels=months) |
67 | | -ax.set_xlabel("month") |
68 | | -ax.set_ylabel("temperature (°C)") |
69 | | -ax.set_title("Bar chart") |
70 | | - |
71 | | -# ── bottom-right: fill_between confidence band ──────────────────────────────── |
72 | | -ax = axes[1][1] |
73 | | -mean = np.sin(x) * np.exp(-x / (2 * np.pi)) |
74 | | -std = 0.15 + 0.1 * np.abs(np.cos(x)) |
75 | | -ax.plot(x, mean, color="darkorchid", linewidth=2, label="mean") |
76 | | -ax.fill_between(x, mean - std, mean + std, alpha=0.25, color="darkorchid", |
77 | | - label="±1 σ") |
78 | | -ax.set_xlabel("x") |
79 | | -ax.set_ylabel("y") |
80 | | -ax.set_title("Confidence band") |
81 | | -ax.set_legend(True) |
82 | | -ax.set_grid(True) |
83 | | - |
84 | | -canvas.suptitle("maxplotlib — four plot types, one canvas") |
85 | | -fig, _ = canvas.plot() |
86 | | -fig |
87 | | -``` |
88 | | - |
89 | | -<div id="fig-showcase"> |
90 | | - |
91 | | -<div class="cell-output cell-output-display" execution_count="2"> |
92 | | - |
93 | | -<div id="fig-showcase-1"> |
94 | | - |
95 | | -<img src="README_files/figure-commonmark/fig-showcase-output-1.png" |
96 | | -data-ref-parent="fig-showcase" /> |
97 | | - |
98 | | -(a) maxplotlib showcase — four plot types, one canvas. |
99 | | - |
100 | | -</div> |
101 | | - |
102 | | -</div> |
103 | | - |
104 | | -<div class="cell-output cell-output-display"> |
105 | | - |
106 | | -<div id="fig-showcase-2"> |
107 | | - |
108 | | -<img src="README_files/figure-commonmark/fig-showcase-output-2.png" |
109 | | -id="fig-showcase-2" data-ref-parent="fig-showcase" /> |
110 | | - |
111 | | -(b) |
112 | | - |
113 | | -</div> |
114 | | - |
115 | | -</div> |
116 | | - |
117 | | -Figure 1 |
118 | | - |
119 | | -</div> |
120 | | - |
121 | | -## Quick start |
122 | | - |
123 | | -### One-liner with `Canvas.subplots()` |
124 | | - |
125 | | -`Canvas.subplots()` mirrors `plt.subplots()` but returns a `Canvas` and |
126 | | -subplot axes that speak the maxplotlib API. |
| 20 | +### Quickstart |
127 | 21 |
|
128 | 22 | ``` python |
129 | 23 | import numpy as np |
130 | 24 | from maxplotlib import Canvas |
131 | 25 |
|
132 | 26 | x = np.linspace(0, 2 * np.pi, 200) |
| 27 | +y = np.sin(x) |
133 | 28 |
|
134 | | -canvas, ax = Canvas.subplots(width="10cm", ratio=0.55) |
135 | | -ax.plot(x, np.sin(x), color="royalblue", label=r"$\sin(x)$", linewidth=2) |
136 | | -ax.plot(x, np.cos(x), color="tomato", label=r"$\cos(x)$", linewidth=2, |
137 | | - linestyle="dashed") |
138 | | -ax.set_xlabel("x") |
139 | | -ax.set_ylabel("y") |
140 | | -ax.set_legend(True) |
141 | | -ax.set_grid(True) |
142 | | -canvas.show() |
| 29 | +canvas, ax = Canvas.subplots() |
| 30 | +ax.plot(x, y) |
143 | 31 | ``` |
144 | 32 |
|
145 | | -### Canvas-level shortcut API |
146 | | - |
147 | | -For single-subplot figures every plot method is available directly on |
148 | | -the `Canvas` object — no need to grab an axes handle: |
| 33 | +Plot the figure with the default (matplotlib) backend: |
149 | 34 |
|
150 | 35 | ``` python |
151 | | -canvas = Canvas(ratio=0.5, fontsize=12) |
152 | | -canvas.add_line(x, np.sin(x), label="sin", color="steelblue") |
153 | | -canvas.add_line(x, np.cos(x), label="cos", color="darkorange", linestyle="dashed") |
154 | | -canvas.set_xlabel("angle (rad)") |
155 | | -canvas.set_ylabel("amplitude") |
156 | | -canvas.set_legend(True) |
157 | 36 | canvas.show() |
158 | 37 | ``` |
159 | 38 |
|
160 | | -## Key features |
161 | | - |
162 | | -### Multiple backends — same API |
| 39 | + |
163 | 40 |
|
164 | | -| Backend | How to use | Output | |
165 | | -|----|----|----| |
166 | | -| `matplotlib` (default) | `canvas.show()` | inline PNG / static file | |
167 | | -| `plotly` | `fig = canvas.plot(backend='plotly')` | interactive HTML widget | |
168 | | -| `tikzfigure` | `tz = canvas.plot(backend='tikzfigure')` | LaTeX `\draw` commands | |
| 41 | +Alternatively, plot with the TikZ backend (not done yet): |
169 | 42 |
|
170 | 43 | ``` python |
171 | | -# Render as interactive Plotly figure |
172 | | -fig = canvas.plot(backend="plotly") |
173 | | -fig.show() |
174 | | - |
175 | | -# Export TikZ code for a LaTeX document |
176 | | -tikz = canvas.plot(backend="tikzfigure") |
177 | | -print(tikz.generate_tikz()) |
| 44 | +canvas.show(backend="tikzfigure") |
178 | 45 | ``` |
179 | 46 |
|
180 | | -### Layers |
| 47 | + |
181 | 48 |
|
182 | | -Tag each plot call with a `layer=` integer. Then render any subset — |
183 | | -handy for step-by-step derivations, lecture slides, or overlays. |
| 49 | +### Layers |
184 | 50 |
|
185 | 51 | ``` python |
186 | | -canvas, ax = Canvas.subplots() |
187 | | -ax.plot(x, np.sin(x), layer=0, label=r"$\sin(x)$", color="steelblue") |
188 | | -ax.plot(x, np.cos(x), layer=1, label=r"$\cos(x)$", color="tomato") |
189 | | -ax.plot(x, np.sin(x) * np.cos(x), layer=2, |
190 | | - label=r"$\sin(x)\cos(x)$", color="seagreen", linestyle="dashed") |
191 | | - |
192 | | -canvas.show(layers=[0]) # only sin |
193 | | -canvas.show(layers=[0, 1]) # sin + cos |
194 | | -canvas.show() # everything |
195 | | -``` |
| 52 | +x = np.linspace(0, 2 * np.pi, 200) |
196 | 53 |
|
197 | | -### Size and typography |
| 54 | +canvas, ax = Canvas.subplots(width="10cm", ratio=0.55) |
198 | 55 |
|
199 | | -Pass physical dimensions and a font size; maxplotlib converts to the |
200 | | -correct `figsize` automatically. |
| 56 | +ax.plot(x, np.sin(x), color="steelblue", label=r"$\sin(x)$", layer=0) |
| 57 | +ax.plot(x, np.cos(x), color="tomato", label=r"$\cos(x)$", layer=1) |
| 58 | +ax.plot( |
| 59 | + x, |
| 60 | + np.sin(x) * np.cos(x), |
| 61 | + color="seagreen", |
| 62 | + label=r"$\sin(x)\cos(x)$", |
| 63 | + linestyle="dashed", |
| 64 | + layer=2, |
| 65 | +) |
201 | 66 |
|
202 | | -``` python |
203 | | -canvas = Canvas(width="17cm", ratio=0.5, fontsize=11) |
| 67 | +ax.set_xlabel("x") |
| 68 | +ax.set_legend(True) |
204 | 69 | ``` |
205 | 70 |
|
206 | | -`ratio` accepts a float **or** the string `"golden"` (default) for the |
207 | | -golden ratio. |
208 | | - |
209 | | -### Saving figures |
| 71 | +Show layer 0 only, then layers 0 and 1, then everything: |
210 | 72 |
|
211 | 73 | ``` python |
212 | | -canvas.savefig("figure.pdf") # vector PDF |
213 | | -canvas.savefig("figure.png") # raster PNG |
214 | | -canvas.savefig("figure.svg") # SVG |
| 74 | +canvas.show(layers=[0]) |
215 | 75 | ``` |
216 | 76 |
|
217 | | -## Plot types |
218 | | - |
219 | | -| Method | Description | |
220 | | -|-----------------------------------|--------------------------------| |
221 | | -| `ax.plot(x, y)` | Line plot | |
222 | | -| `ax.scatter(x, y)` | Scatter plot | |
223 | | -| `ax.bar(categories, values)` | Bar chart | |
224 | | -| `ax.fill_between(x, y1, y2)` | Shaded band | |
225 | | -| `ax.errorbar(x, y, yerr)` | Error bars | |
226 | | -| `ax.axhline(y)` / `ax.axvline(x)` | Reference lines | |
227 | | -| `ax.annotate(text, xy)` | Annotation with optional arrow | |
228 | | - |
229 | | -## Tutorials |
230 | | - |
231 | | -Step-by-step Jupyter notebooks in `tutorials/`: |
| 77 | + |
232 | 78 |
|
233 | | -| Notebook | Topic | |
234 | | -|----------------------|----------------------------------| |
235 | | -| `tutorial_01` | Quick start | |
236 | | -| `tutorial_02` | Multiple subplots | |
237 | | -| `tutorial_03` | All plot types | |
238 | | -| `tutorial_04` | Colours, linestyles, markers | |
239 | | -| `tutorial_05` | Axes, ticks, scales, annotations | |
240 | | -| `tutorial_06` | Layers | |
241 | | -| `tutorial_07_tikz` | TikZ backend | |
242 | | -| `tutorial_08_plotly` | Plotly backend | |
| 79 | +Show all layers: |
243 | 80 |
|
244 | | -## License |
| 81 | +``` python |
| 82 | +canvas.show() |
| 83 | +``` |
245 | 84 |
|
246 | | -MIT — see [`LICENSE`](LICENSE). |
| 85 | + |
0 commit comments