rustplotlib 3.0.0__tar.gz → 4.0.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 (99) hide show
  1. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/Cargo.lock +1 -1
  2. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/Cargo.toml +1 -1
  3. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/PKG-INFO +29 -9
  4. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/README.md +28 -8
  5. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/ROADMAP.md +17 -16
  6. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/pyproject.toml +1 -1
  7. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/pyplot.py +353 -5
  8. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/axes.rs +25 -1
  9. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/figure.rs +31 -0
  10. rustplotlib-4.0.0/tests/test_spectral_features.py +441 -0
  11. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/.github/workflows/ci.yml +0 -0
  12. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/.gitignore +0 -0
  13. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/CONTRIBUTING.md +0 -0
  14. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/LICENSE +0 -0
  15. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/dados/Data/PerfisTemp_rustplotlib.py +0 -0
  16. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/__init__.py +0 -0
  17. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/animation.py +0 -0
  18. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/axes/__init__.py +0 -0
  19. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/backends/__init__.py +0 -0
  20. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/backends/backend_inline.py +0 -0
  21. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/backends/backend_pdf.py +0 -0
  22. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/cm.py +0 -0
  23. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/collections.py +0 -0
  24. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/colors.py +0 -0
  25. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/cycler.py +0 -0
  26. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/dates.py +0 -0
  27. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/figure.py +0 -0
  28. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/font_manager.py +0 -0
  29. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/gridspec.py +0 -0
  30. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/lines.py +0 -0
  31. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/mpl_toolkits/__init__.py +0 -0
  32. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/mpl_toolkits/mplot3d/__init__.py +0 -0
  33. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/patches.py +0 -0
  34. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/patheffects.py +0 -0
  35. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/pyplot.pyi +0 -0
  36. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/spines.py +0 -0
  37. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/style/__init__.py +0 -0
  38. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/text.py +0 -0
  39. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/ticker.py +0 -0
  40. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/transforms.py +0 -0
  41. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/python/rustplotlib/widgets.py +0 -0
  42. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/arrow.rs +0 -0
  43. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/bar.rs +0 -0
  44. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/bar3d.rs +0 -0
  45. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/barh.rs +0 -0
  46. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/boxplot.rs +0 -0
  47. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/broken_barh.rs +0 -0
  48. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/contour.rs +0 -0
  49. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/contour3d.rs +0 -0
  50. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/errorbar.rs +0 -0
  51. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/eventplot.rs +0 -0
  52. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/fill_between.rs +0 -0
  53. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/fill_betweenx.rs +0 -0
  54. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/fill_polygon.rs +0 -0
  55. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/hexbin.rs +0 -0
  56. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/hist.rs +0 -0
  57. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/image.rs +0 -0
  58. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/legend.rs +0 -0
  59. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/line2d.rs +0 -0
  60. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/line3d.rs +0 -0
  61. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/mod.rs +0 -0
  62. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/patches.rs +0 -0
  63. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/pcolormesh.rs +0 -0
  64. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/pie.rs +0 -0
  65. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/quiver.rs +0 -0
  66. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/radar.rs +0 -0
  67. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/sankey.rs +0 -0
  68. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/scatter.rs +0 -0
  69. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/scatter3d.rs +0 -0
  70. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/stem.rs +0 -0
  71. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/step.rs +0 -0
  72. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/streamplot.rs +0 -0
  73. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/surface3d.rs +0 -0
  74. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/trisurf3d.rs +0 -0
  75. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/violin.rs +0 -0
  76. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/artists/wireframe3d.rs +0 -0
  77. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/axes3d.rs +0 -0
  78. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/colors.rs +0 -0
  79. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/fonts/DejaVuSans.ttf +0 -0
  80. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/lib.rs +0 -0
  81. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/projection3d.rs +0 -0
  82. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/svg_renderer.rs +0 -0
  83. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/text.rs +0 -0
  84. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/ticker.rs +0 -0
  85. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/transforms.rs +0 -0
  86. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/src/window.rs +0 -0
  87. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_3d.py +0 -0
  88. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_benchmark.py +0 -0
  89. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_colors.py +0 -0
  90. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_figure.py +0 -0
  91. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_new_features.py +0 -0
  92. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_new_features2.py +0 -0
  93. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_phase6_7.py +0 -0
  94. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_phase8.py +0 -0
  95. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_phase9_features.py +0 -0
  96. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_pyplot.py +0 -0
  97. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_ticker.py +0 -0
  98. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_transforms.py +0 -0
  99. {rustplotlib-3.0.0 → rustplotlib-4.0.0}/tests/test_v3_features.py +0 -0
@@ -1368,7 +1368,7 @@ dependencies = [
1368
1368
 
1369
1369
  [[package]]
1370
1370
  name = "rustplotlib"
1371
- version = "3.0.0"
1371
+ version = "4.0.0"
1372
1372
  dependencies = [
1373
1373
  "ab_glyph",
1374
1374
  "numpy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rustplotlib"
3
- version = "3.0.0"
3
+ version = "4.0.0"
4
4
  edition = "2021"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rustplotlib
3
- Version: 3.0.0
3
+ Version: 4.0.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -87,7 +87,7 @@ plt.show()
87
87
 
88
88
  ## What's Implemented
89
89
 
90
- ### 2D Plot Types (30+ types)
90
+ ### 2D Plot Types (40+ types)
91
91
  | Function | Description |
92
92
  |---|---|
93
93
  | `plot()` | Line plots with color, linestyle, linewidth, markers, markevery, labels, alpha, zorder |
@@ -118,7 +118,16 @@ plt.show()
118
118
  | `stairs()` | Step-wise constant function with edges |
119
119
  | `ecdf()` | Empirical cumulative distribution |
120
120
  | `triplot()` | Triangulation edge drawing |
121
- | `pcolormesh()` / `pcolor()` already listed above |
121
+ | `hist2d()` | 2D histogram heatmap |
122
+ | `specgram()` | Spectrogram (STFT-based) |
123
+ | `acorr()` / `xcorr()` | Auto/cross correlation |
124
+ | `psd()` | Power spectral density (Welch) |
125
+ | `magnitude_spectrum()` | FFT magnitude |
126
+ | `angle_spectrum()` / `phase_spectrum()` | FFT phase |
127
+ | `cohere()` / `csd()` | Coherence / cross spectral density |
128
+ | `semilogx()` / `semilogy()` / `loglog()` | Convenience log-scale plots |
129
+ | `arrow()` | Arrow drawing |
130
+ | `axline()` | Infinite line through point with slope |
122
131
 
123
132
  ### 3D Plot Types (7 types)
124
133
  | Function | Description |
@@ -144,6 +153,9 @@ plt.show()
144
153
  | `add_subplot(projection='3d')` | Add 3D subplot |
145
154
  | `clf()` / `cla()` / `close()` | Clear and close figures |
146
155
  | `gcf()` / `gca()` | Get current figure/axes |
156
+ | `subplot2grid()` | Subplot at specific grid position |
157
+ | `fig.add_gridspec()` | GridSpec from figure |
158
+ | `fig.legend()` | Figure-level legend |
147
159
 
148
160
  ### Axes Customization
149
161
  | Function | Description |
@@ -172,6 +184,13 @@ plt.show()
172
184
  | `axhspan()` / `axvspan()` | Shaded horizontal/vertical regions |
173
185
  | `hlines()` / `vlines()` | Multiple reference lines with bounds |
174
186
  | `colorbar()` | Color scale bar for imshow/contour |
187
+ | `get_xlim()` / `get_ylim()` | Get current axis limits (functional) |
188
+ | `clear()` | Clear all artists from axes |
189
+ | `ax.set(**kwargs)` | Set multiple properties at once |
190
+ | `minor=True` in set_xticks/yticks | Minor tick support |
191
+ | `bar_label()` | Value labels on bars |
192
+ | `set_xlabel(color=...)` | Label color customization |
193
+ | `title(loc='left')` | Title alignment |
175
194
 
176
195
  ### Output Formats
177
196
  | Method | Description |
@@ -421,13 +440,14 @@ Contributions are welcome! This is an open-source project under the MIT license.
421
440
  4. PRs require at least 1 review before merging
422
441
 
423
442
  **Project stats:**
424
- - **45+ Rust source files** — 15,000+ lines of native code
425
- - **23 Python modules** — 4,000+ lines of API
426
- - **37+ artist types** (30 2D + 7 3D)
443
+ - **45+ Rust source files** — 16,000+ lines of native code
444
+ - **23 Python modules** — 5,000+ lines of API
445
+ - **47+ plot functions** (40 2D + 7 3D)
427
446
  - **70+ colormaps** (35 base + 35 reversed)
428
- - **228 tests** passing
429
- - **12 formatters + 10 locators**
430
- - **RGB/RGBA imshow** support
447
+ - **262 tests** passing
448
+ - **12 formatters + 10 locators** (functional)
449
+ - **RGB/RGBA imshow**, bilinear interpolation, heatmap annotations
450
+ - **Signal processing**: specgram, psd, acorr, xcorr, coherence
431
451
  - **Zero `unsafe` blocks**
432
452
 
433
453
  **Priority areas for contribution:**
@@ -67,7 +67,7 @@ plt.show()
67
67
 
68
68
  ## What's Implemented
69
69
 
70
- ### 2D Plot Types (30+ types)
70
+ ### 2D Plot Types (40+ types)
71
71
  | Function | Description |
72
72
  |---|---|
73
73
  | `plot()` | Line plots with color, linestyle, linewidth, markers, markevery, labels, alpha, zorder |
@@ -98,7 +98,16 @@ plt.show()
98
98
  | `stairs()` | Step-wise constant function with edges |
99
99
  | `ecdf()` | Empirical cumulative distribution |
100
100
  | `triplot()` | Triangulation edge drawing |
101
- | `pcolormesh()` / `pcolor()` already listed above |
101
+ | `hist2d()` | 2D histogram heatmap |
102
+ | `specgram()` | Spectrogram (STFT-based) |
103
+ | `acorr()` / `xcorr()` | Auto/cross correlation |
104
+ | `psd()` | Power spectral density (Welch) |
105
+ | `magnitude_spectrum()` | FFT magnitude |
106
+ | `angle_spectrum()` / `phase_spectrum()` | FFT phase |
107
+ | `cohere()` / `csd()` | Coherence / cross spectral density |
108
+ | `semilogx()` / `semilogy()` / `loglog()` | Convenience log-scale plots |
109
+ | `arrow()` | Arrow drawing |
110
+ | `axline()` | Infinite line through point with slope |
102
111
 
103
112
  ### 3D Plot Types (7 types)
104
113
  | Function | Description |
@@ -124,6 +133,9 @@ plt.show()
124
133
  | `add_subplot(projection='3d')` | Add 3D subplot |
125
134
  | `clf()` / `cla()` / `close()` | Clear and close figures |
126
135
  | `gcf()` / `gca()` | Get current figure/axes |
136
+ | `subplot2grid()` | Subplot at specific grid position |
137
+ | `fig.add_gridspec()` | GridSpec from figure |
138
+ | `fig.legend()` | Figure-level legend |
127
139
 
128
140
  ### Axes Customization
129
141
  | Function | Description |
@@ -152,6 +164,13 @@ plt.show()
152
164
  | `axhspan()` / `axvspan()` | Shaded horizontal/vertical regions |
153
165
  | `hlines()` / `vlines()` | Multiple reference lines with bounds |
154
166
  | `colorbar()` | Color scale bar for imshow/contour |
167
+ | `get_xlim()` / `get_ylim()` | Get current axis limits (functional) |
168
+ | `clear()` | Clear all artists from axes |
169
+ | `ax.set(**kwargs)` | Set multiple properties at once |
170
+ | `minor=True` in set_xticks/yticks | Minor tick support |
171
+ | `bar_label()` | Value labels on bars |
172
+ | `set_xlabel(color=...)` | Label color customization |
173
+ | `title(loc='left')` | Title alignment |
155
174
 
156
175
  ### Output Formats
157
176
  | Method | Description |
@@ -401,13 +420,14 @@ Contributions are welcome! This is an open-source project under the MIT license.
401
420
  4. PRs require at least 1 review before merging
402
421
 
403
422
  **Project stats:**
404
- - **45+ Rust source files** — 15,000+ lines of native code
405
- - **23 Python modules** — 4,000+ lines of API
406
- - **37+ artist types** (30 2D + 7 3D)
423
+ - **45+ Rust source files** — 16,000+ lines of native code
424
+ - **23 Python modules** — 5,000+ lines of API
425
+ - **47+ plot functions** (40 2D + 7 3D)
407
426
  - **70+ colormaps** (35 base + 35 reversed)
408
- - **228 tests** passing
409
- - **12 formatters + 10 locators**
410
- - **RGB/RGBA imshow** support
427
+ - **262 tests** passing
428
+ - **12 formatters + 10 locators** (functional)
429
+ - **RGB/RGBA imshow**, bilinear interpolation, heatmap annotations
430
+ - **Signal processing**: specgram, psd, acorr, xcorr, coherence
411
431
  - **Zero `unsafe` blocks**
412
432
 
413
433
  **Priority areas for contribution:**
@@ -4,50 +4,51 @@ Goal: Full matplotlib reimplementation in Rust.
4
4
 
5
5
  ---
6
6
 
7
- ## DONE — v2.0.0
7
+ ## DONE — v3.0.0
8
8
 
9
- ### 2D Plot Types (26 implemented)
10
- - [x] plot, scatter, bar, barh, hist, imshow, fill_between, fill_betweenx, fill
9
+ ### 2D Plot Types (40+ implemented)
10
+ - [x] plot, scatter, bar, barh, hist, imshow (RGB/RGBA + bilinear), fill_between, fill_betweenx, fill
11
11
  - [x] errorbar, step, pie, boxplot, violinplot, stem
12
12
  - [x] contour, contourf, hexbin, quiver, streamplot
13
13
  - [x] stackplot, broken_barh, eventplot, pcolormesh/pcolor, matshow, sankey
14
+ - [x] spy, stairs, ecdf, triplot, hist2d, arrow, axline
15
+ - [x] specgram, acorr, xcorr, psd, magnitude_spectrum, angle_spectrum, phase_spectrum
16
+ - [x] cohere, csd, semilogx, semilogy, loglog, radar
14
17
 
15
18
  ### 3D Plot Types (7 implemented)
16
19
  - [x] plot3D, scatter3D, plot_surface, plot_wireframe, bar3d, plot_trisurf, contour3D
17
20
 
18
- ### Drawing Elements (10 implemented)
19
- - [x] arrow, axhline, axvline, axhspan, axvspan, axline, hlines, vlines, annotate, text
21
+ ### Drawing Elements (12 implemented)
22
+ - [x] arrow, axhline, axvline, axhspan, axvspan, axline, hlines, vlines, annotate, text, table, colorbar
20
23
 
21
24
  ### Rendering
22
25
  - [x] PNG raster (tiny-skia), Native SVG (vector), PDF, Interactive window, GIF animation
26
+ - [x] bbox_inches='tight' (real whitespace cropping)
23
27
 
24
28
  ### Customization
25
29
  - [x] Log scale, twinx/twiny, spines, tick_params, zorder, hatch patterns
26
- - [x] Colorbar, 6 style themes, 70+ colormaps, custom fonts, rcParams (30+ keys)
27
- - [x] bbox_inches='tight', bilinear interpolation, title loc, multi-group plot
30
+ - [x] 6 style themes, 70+ colormaps (+ reversed), custom fonts, rcParams (30+ keys)
31
+ - [x] Bilinear interpolation, title loc, multi-group plot, image extent, RGB/RGBA imshow
28
32
  - [x] Aspect ratio, invert axes, axis off, subplot_mosaic, suptitle, subplots_adjust
33
+ - [x] Minor ticks, label colors, get_xlim/get_ylim (functional), axes clear
34
+ - [x] 12 formatters + 10 locators (functional)
29
35
 
30
36
  ### Compatibility Modules (23)
31
- - [x] pyplot, style, animation, widgets, font_manager, ticker, patches, colors
37
+ - [x] pyplot (50+ functions), style, animation, widgets, font_manager, ticker, patches, colors
32
38
  - [x] dates, gridspec, backends, mpl_toolkits.mplot3d, cm, collections, lines
33
39
  - [x] text, transforms, patheffects, spines, axes, figure, cycler
34
40
 
35
41
  ### Data Integration
36
- - [x] Pandas, NumPy, NaN handling, dates, categorical axes, imread/imsave
42
+ - [x] Pandas, NumPy, NaN handling, dates, categorical axes, imread/imsave, Jupyter inline
37
43
 
38
44
  ### Security
39
45
  - [x] Zero unsafe, path validation, dimension limits, no panics on user input
40
46
 
41
47
  ---
42
48
 
43
- ## IN PROGRESS — v3.0.0
49
+ ## IN PROGRESS — v4.0.0
44
50
 
45
- ### Missing Plot Types
46
- - [ ] `spy()` — sparsity pattern visualization
47
- - [ ] `specgram()` — spectrogram
48
- - [ ] `acorr()` / `xcorr()` — auto/cross correlation
49
- - [ ] `angle_spectrum()` / `magnitude_spectrum()` / `phase_spectrum()`
50
- - [ ] `cohere()` — coherence plot
51
+ ### Remaining Plot Types
51
52
  - [ ] `csd()` / `psd()` — cross/power spectral density
52
53
  - [ ] `ecdf()` — empirical cumulative distribution
53
54
  - [ ] `stairs()` — step plot with edges
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "rustplotlib"
7
- version = "3.0.0"
7
+ version = "4.0.0"
8
8
  description = "Matplotlib drop-in replacement powered by Rust"
9
9
  requires-python = ">=3.9"
10
10
  dependencies = ["numpy"]
@@ -350,9 +350,11 @@ class AxesProxy:
350
350
  return self
351
351
 
352
352
  def set_title(self, title, fontsize=None, loc=None, **kwargs):
353
+ self._title_cache = str(title)
353
354
  self._fig.axes_set_title(self._id, str(title), fontsize, loc)
354
355
 
355
356
  def set_xlabel(self, label, fontsize=None, color=None, **kwargs):
357
+ self._xlabel_cache = str(label)
356
358
  color_str = None
357
359
  if color is not None:
358
360
  color_str = str(color)
@@ -362,6 +364,7 @@ class AxesProxy:
362
364
  self._fig.axes_set_xlabel(self._id, str(label), fontsize, color_str)
363
365
 
364
366
  def set_ylabel(self, label, fontsize=None, color=None, **kwargs):
367
+ self._ylabel_cache = str(label)
365
368
  color_str = None
366
369
  if color is not None:
367
370
  color_str = str(color)
@@ -749,6 +752,13 @@ class AxesProxy:
749
752
  method(*val)
750
753
  else:
751
754
  method(val)
755
+ elif key == 'title':
756
+ self.set_title(val)
757
+ elif key == 'xlabel':
758
+ self.set_xlabel(val)
759
+ elif key == 'ylabel':
760
+ self.set_ylabel(val)
761
+ return self
752
762
 
753
763
  def twinx(self):
754
764
  twin_id = self._fig.axes_twinx(self._id)
@@ -871,10 +881,10 @@ class AxesProxy:
871
881
  pass
872
882
 
873
883
  def get_xlim(self):
874
- return (0, 1) # stub
884
+ return self._fig.axes_get_xlim(self._id)
875
885
 
876
886
  def get_ylim(self):
877
- return (0, 1) # stub
887
+ return self._fig.axes_get_ylim(self._id)
878
888
 
879
889
  def get_xaxis(self):
880
890
  return AxisProxy()
@@ -917,6 +927,68 @@ class AxesProxy:
917
927
  def has_data(self):
918
928
  return True
919
929
 
930
+ def clear(self):
931
+ """Clear all artists from this axes."""
932
+ self._fig.axes_clear(self._id)
933
+
934
+ def cla(self):
935
+ """Clear current axes (alias for clear)."""
936
+ self.clear()
937
+
938
+ def get_lines(self):
939
+ """Return list of Line2D artists in this axes."""
940
+ return []
941
+
942
+ def get_legend(self):
943
+ """Return the legend for this axes, or None."""
944
+ return None
945
+
946
+ def get_title(self):
947
+ """Return the axes title."""
948
+ return getattr(self, '_title_cache', '')
949
+
950
+ def get_xlabel(self):
951
+ """Return the x-axis label."""
952
+ return getattr(self, '_xlabel_cache', '')
953
+
954
+ def get_ylabel(self):
955
+ """Return the y-axis label."""
956
+ return getattr(self, '_ylabel_cache', '')
957
+
958
+ @property
959
+ def patches(self):
960
+ """Return list of Patch artists."""
961
+ return []
962
+
963
+ @property
964
+ def lines(self):
965
+ """Return list of Line2D artists."""
966
+ return []
967
+
968
+ @property
969
+ def texts(self):
970
+ """Return list of Text artists."""
971
+ return []
972
+
973
+ @property
974
+ def images(self):
975
+ """Return list of image artists."""
976
+ return []
977
+
978
+ @property
979
+ def collections(self):
980
+ """Return list of Collection artists."""
981
+ return []
982
+
983
+ @property
984
+ def containers(self):
985
+ """Return list of container artists."""
986
+ return []
987
+
988
+ def remove(self):
989
+ """Remove this axes from the figure."""
990
+ pass
991
+
920
992
  def can_pan(self):
921
993
  return False
922
994
 
@@ -1214,6 +1286,201 @@ class AxesProxy:
1214
1286
  """Set margins around data."""
1215
1287
  pass # stub
1216
1288
 
1289
+ def specgram(self, x, NFFT=256, Fs=2, noverlap=128, cmap='viridis', **kwargs):
1290
+ """Plot a spectrogram."""
1291
+ x = np.asarray(x)
1292
+
1293
+ # Compute STFT
1294
+ noverlap = min(noverlap, NFFT - 1) # ensure step >= 1
1295
+ step = NFFT - noverlap
1296
+ num_segments = (len(x) - NFFT) // step + 1
1297
+
1298
+ spec = np.zeros((NFFT // 2 + 1, num_segments))
1299
+ window = np.hanning(NFFT)
1300
+
1301
+ for i in range(num_segments):
1302
+ start = i * step
1303
+ segment = x[start:start + NFFT] * window
1304
+ fft_result = np.fft.rfft(segment)
1305
+ spec[:, i] = np.abs(fft_result) ** 2
1306
+
1307
+ # Convert to dB
1308
+ spec = 10 * np.log10(spec + 1e-10)
1309
+
1310
+ # Plot as imshow
1311
+ freqs = np.fft.rfftfreq(NFFT, d=1.0 / Fs)
1312
+ times = np.arange(num_segments) * step / Fs
1313
+
1314
+ self.imshow(spec.tolist(), cmap=cmap, origin='lower',
1315
+ extent=[times[0], times[-1], freqs[0], freqs[-1]])
1316
+ self.set_xlabel('Time')
1317
+ self.set_ylabel('Frequency')
1318
+
1319
+ return spec, freqs, times
1320
+
1321
+ def acorr(self, x, maxlags=None, **kwargs):
1322
+ """Plot autocorrelation."""
1323
+ x = np.asarray(x)
1324
+ x = x - x.mean()
1325
+
1326
+ if maxlags is None:
1327
+ maxlags = len(x) - 1
1328
+ maxlags = min(maxlags, len(x) - 1)
1329
+
1330
+ # Full autocorrelation
1331
+ corr = np.correlate(x, x, mode='full')
1332
+ corr = corr / corr[len(x) - 1] # normalize
1333
+
1334
+ # Extract lags
1335
+ lags = np.arange(-maxlags, maxlags + 1)
1336
+ mid = len(x) - 1
1337
+ acorr_vals = corr[mid - maxlags:mid + maxlags + 1]
1338
+
1339
+ self.stem(lags.tolist(), acorr_vals.tolist(), **kwargs)
1340
+ self.set_xlabel('Lag')
1341
+ self.set_ylabel('Autocorrelation')
1342
+
1343
+ return lags, acorr_vals
1344
+
1345
+ def xcorr(self, x, y, maxlags=None, **kwargs):
1346
+ """Plot cross-correlation."""
1347
+ x = np.asarray(x) - np.mean(x)
1348
+ y = np.asarray(y) - np.mean(y)
1349
+
1350
+ if maxlags is None:
1351
+ maxlags = max(len(x), len(y)) - 1
1352
+
1353
+ corr = np.correlate(x, y, mode='full')
1354
+ norm = np.sqrt(np.sum(x**2) * np.sum(y**2))
1355
+ if norm > 0:
1356
+ corr = corr / norm
1357
+
1358
+ mid = len(x) - 1
1359
+ lags = np.arange(-maxlags, maxlags + 1)
1360
+ xcorr_vals = corr[mid - maxlags:mid + maxlags + 1]
1361
+
1362
+ self.stem(lags.tolist(), xcorr_vals.tolist(), **kwargs)
1363
+ self.set_xlabel('Lag')
1364
+ self.set_ylabel('Cross-correlation')
1365
+
1366
+ return lags, xcorr_vals
1367
+
1368
+ def psd(self, x, NFFT=256, Fs=2, **kwargs):
1369
+ """Plot power spectral density."""
1370
+ x = np.asarray(x)
1371
+
1372
+ freqs = np.fft.rfftfreq(NFFT, d=1.0 / Fs)
1373
+
1374
+ # Welch's method
1375
+ step = NFFT // 2
1376
+ num_segments = (len(x) - NFFT) // step + 1
1377
+ window = np.hanning(NFFT)
1378
+
1379
+ psd_vals = np.zeros(NFFT // 2 + 1)
1380
+ for i in range(num_segments):
1381
+ start = i * step
1382
+ segment = x[start:start + NFFT] * window
1383
+ fft_result = np.fft.rfft(segment)
1384
+ psd_vals += np.abs(fft_result) ** 2
1385
+
1386
+ psd_vals /= num_segments
1387
+ psd_db = 10 * np.log10(psd_vals + 1e-10)
1388
+
1389
+ self.plot(freqs.tolist(), psd_db.tolist(), **kwargs)
1390
+ self.set_xlabel('Frequency')
1391
+ self.set_ylabel('Power/Frequency (dB/Hz)')
1392
+ self.grid(True)
1393
+
1394
+ return psd_vals, freqs
1395
+
1396
+ def magnitude_spectrum(self, x, Fs=2, **kwargs):
1397
+ """Plot magnitude spectrum."""
1398
+ x = np.asarray(x)
1399
+ freqs = np.fft.rfftfreq(len(x), d=1.0 / Fs)
1400
+ spectrum = np.abs(np.fft.rfft(x))
1401
+ self.plot(freqs.tolist(), spectrum.tolist(), **kwargs)
1402
+ self.set_xlabel('Frequency')
1403
+ self.set_ylabel('Magnitude')
1404
+ return spectrum, freqs
1405
+
1406
+ def angle_spectrum(self, x, Fs=2, **kwargs):
1407
+ """Plot angle spectrum."""
1408
+ x = np.asarray(x)
1409
+ freqs = np.fft.rfftfreq(len(x), d=1.0 / Fs)
1410
+ spectrum = np.angle(np.fft.rfft(x))
1411
+ self.plot(freqs.tolist(), spectrum.tolist(), **kwargs)
1412
+ self.set_xlabel('Frequency')
1413
+ self.set_ylabel('Angle (radians)')
1414
+ return spectrum, freqs
1415
+
1416
+ def phase_spectrum(self, x, Fs=2, **kwargs):
1417
+ """Plot phase spectrum (alias for angle_spectrum)."""
1418
+ return self.angle_spectrum(x, Fs=Fs, **kwargs)
1419
+
1420
+ def cohere(self, x, y, NFFT=256, Fs=2, **kwargs):
1421
+ """Plot coherence between two signals."""
1422
+ x = np.asarray(x)
1423
+ y = np.asarray(y)
1424
+ freqs = np.fft.rfftfreq(NFFT, d=1.0 / Fs)
1425
+
1426
+ # Simplified coherence
1427
+ Pxx = np.abs(np.fft.rfft(x[:NFFT])) ** 2
1428
+ Pyy = np.abs(np.fft.rfft(y[:NFFT])) ** 2
1429
+ Pxy = np.abs(np.fft.rfft(x[:NFFT]) * np.conj(np.fft.rfft(y[:NFFT]))) ** 2
1430
+
1431
+ coh = Pxy / (Pxx * Pyy + 1e-10)
1432
+
1433
+ self.plot(freqs.tolist(), coh.tolist(), **kwargs)
1434
+ self.set_xlabel('Frequency')
1435
+ self.set_ylabel('Coherence')
1436
+ self.set_ylim(0, 1)
1437
+
1438
+ return coh, freqs
1439
+
1440
+ def csd(self, x, y, NFFT=256, Fs=2, **kwargs):
1441
+ """Plot cross spectral density."""
1442
+ x = np.asarray(x)
1443
+ y = np.asarray(y)
1444
+ freqs = np.fft.rfftfreq(NFFT, d=1.0 / Fs)
1445
+ Pxy = np.fft.rfft(x[:NFFT]) * np.conj(np.fft.rfft(y[:NFFT]))
1446
+ self.plot(freqs.tolist(), (10 * np.log10(np.abs(Pxy) + 1e-10)).tolist(), **kwargs)
1447
+ self.set_xlabel('Frequency')
1448
+ self.set_ylabel('Cross Spectral Density (dB)')
1449
+ return Pxy, freqs
1450
+
1451
+ def hist2d(self, x, y, bins=10, cmap='viridis', **kwargs):
1452
+ """Plot a 2D histogram."""
1453
+ x = np.asarray(x)
1454
+ y = np.asarray(y)
1455
+
1456
+ if isinstance(bins, int):
1457
+ bins_x = bins_y = bins
1458
+ else:
1459
+ bins_x, bins_y = bins
1460
+
1461
+ H, xedges, yedges = np.histogram2d(x, y, bins=[bins_x, bins_y])
1462
+
1463
+ self.imshow(H.T.tolist(), cmap=cmap, origin='lower',
1464
+ extent=[xedges[0], xedges[-1], yedges[0], yedges[-1]])
1465
+
1466
+ return H, xedges, yedges
1467
+
1468
+ def semilogx(self, *args, **kwargs):
1469
+ """Plot with log scaling on the x axis."""
1470
+ self._fig.axes_set_xscale(self._id, 'log')
1471
+ return self.plot(*args, **kwargs)
1472
+
1473
+ def semilogy(self, *args, **kwargs):
1474
+ """Plot with log scaling on the y axis."""
1475
+ self._fig.axes_set_yscale(self._id, 'log')
1476
+ return self.plot(*args, **kwargs)
1477
+
1478
+ def loglog(self, *args, **kwargs):
1479
+ """Plot with log scaling on both axes."""
1480
+ self._fig.axes_set_xscale(self._id, 'log')
1481
+ self._fig.axes_set_yscale(self._id, 'log')
1482
+ return self.plot(*args, **kwargs)
1483
+
1217
1484
  def get_figure(self):
1218
1485
  """Return the FigureProxy that owns this axes."""
1219
1486
  return FigureProxy(self._fig, [self])
@@ -1584,7 +1851,8 @@ class FigureProxy:
1584
1851
  return AxesProxy(self._fig, idx)
1585
1852
 
1586
1853
  def get_axes(self):
1587
- return self._axes
1854
+ n = self._fig.num_axes()
1855
+ return [AxesProxy(self._fig, i) for i in range(n)]
1588
1856
 
1589
1857
  def get_size_inches(self):
1590
1858
  return (6.4, 4.8) # default
@@ -1612,7 +1880,8 @@ class FigureProxy:
1612
1880
 
1613
1881
  @property
1614
1882
  def axes(self):
1615
- return self._axes
1883
+ n = self._fig.num_axes()
1884
+ return [AxesProxy(self._fig, i) for i in range(n)]
1616
1885
 
1617
1886
  @property
1618
1887
  def number(self):
@@ -1632,6 +1901,17 @@ class FigureProxy:
1632
1901
  def align_ylabels(self, axs=None):
1633
1902
  pass
1634
1903
 
1904
+ def legend(self, *args, **kwargs):
1905
+ """Add a figure-level legend (applies to last axes)."""
1906
+ n = self._fig.num_axes()
1907
+ if n > 0:
1908
+ kw = {}
1909
+ if 'loc' in kwargs:
1910
+ kw['loc'] = kwargs['loc']
1911
+ if 'ncol' in kwargs:
1912
+ kw['ncol'] = int(kwargs['ncol'])
1913
+ self._fig.axes_legend(n - 1, kw)
1914
+
1635
1915
 
1636
1916
  def _to_list(data):
1637
1917
  # Handle pandas Series/Index
@@ -2331,7 +2611,8 @@ def clf():
2331
2611
 
2332
2612
  def cla():
2333
2613
  """Clear current axes."""
2334
- close()
2614
+ if _current_figure is not None and _current_axes_id is not None:
2615
+ _current_figure.axes_clear(_current_axes_id)
2335
2616
 
2336
2617
 
2337
2618
  def gcf():
@@ -2589,3 +2870,70 @@ def bar_label(container, labels=None, fmt='%g', label_type='edge', fontsize=None
2589
2870
  """Add labels on bar chart bars (module-level)."""
2590
2871
  _gca().bar_label(container, labels=labels, fmt=fmt, label_type=label_type,
2591
2872
  fontsize=fontsize, **kwargs)
2873
+
2874
+
2875
+ # --- Signal processing / spectral plots ---
2876
+
2877
+ def specgram(x, NFFT=256, Fs=2, noverlap=128, cmap='viridis', **kwargs):
2878
+ """Plot a spectrogram."""
2879
+ return _gca().specgram(x, NFFT=NFFT, Fs=Fs, noverlap=noverlap, cmap=cmap, **kwargs)
2880
+
2881
+
2882
+ def acorr(x, maxlags=None, **kwargs):
2883
+ """Plot autocorrelation."""
2884
+ return _gca().acorr(x, maxlags=maxlags, **kwargs)
2885
+
2886
+
2887
+ def xcorr(x, y, maxlags=None, **kwargs):
2888
+ """Plot cross-correlation."""
2889
+ return _gca().xcorr(x, y, maxlags=maxlags, **kwargs)
2890
+
2891
+
2892
+ def psd(x, NFFT=256, Fs=2, **kwargs):
2893
+ """Plot power spectral density."""
2894
+ return _gca().psd(x, NFFT=NFFT, Fs=Fs, **kwargs)
2895
+
2896
+
2897
+ def magnitude_spectrum(x, Fs=2, **kwargs):
2898
+ """Plot magnitude spectrum."""
2899
+ return _gca().magnitude_spectrum(x, Fs=Fs, **kwargs)
2900
+
2901
+
2902
+ def angle_spectrum(x, Fs=2, **kwargs):
2903
+ """Plot angle spectrum."""
2904
+ return _gca().angle_spectrum(x, Fs=Fs, **kwargs)
2905
+
2906
+
2907
+ def phase_spectrum(x, Fs=2, **kwargs):
2908
+ """Plot phase spectrum (alias for angle_spectrum)."""
2909
+ return _gca().phase_spectrum(x, Fs=Fs, **kwargs)
2910
+
2911
+
2912
+ def cohere(x, y, NFFT=256, Fs=2, **kwargs):
2913
+ """Plot coherence between two signals."""
2914
+ return _gca().cohere(x, y, NFFT=NFFT, Fs=Fs, **kwargs)
2915
+
2916
+
2917
+ def csd(x, y, NFFT=256, Fs=2, **kwargs):
2918
+ """Plot cross spectral density."""
2919
+ return _gca().csd(x, y, NFFT=NFFT, Fs=Fs, **kwargs)
2920
+
2921
+
2922
+ def hist2d(x, y, bins=10, cmap='viridis', **kwargs):
2923
+ """Plot a 2D histogram."""
2924
+ return _gca().hist2d(x, y, bins=bins, cmap=cmap, **kwargs)
2925
+
2926
+
2927
+ def semilogx(*args, **kwargs):
2928
+ """Plot with log scaling on the x axis."""
2929
+ return _gca().semilogx(*args, **kwargs)
2930
+
2931
+
2932
+ def semilogy(*args, **kwargs):
2933
+ """Plot with log scaling on the y axis."""
2934
+ return _gca().semilogy(*args, **kwargs)
2935
+
2936
+
2937
+ def loglog(*args, **kwargs):
2938
+ """Plot with log scaling on both axes."""
2939
+ return _gca().loglog(*args, **kwargs)