matplotlibx 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: matplotlibx
3
+ Version: 0.1.0
4
+ Summary: Enhanced Matplotlib wrapper with publication-quality styling and colorblind accessibility
5
+ Author-email: Igor Sokolov <sokolov.igor.ch@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/isokolov/pltx
8
+ Project-URL: Bug Tracker, https://github.com/isokolov/pltx/issues
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Topic :: Scientific/Engineering :: Visualization
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: matplotlib>=3.5.0
21
+ Requires-Dist: numpy>=1.20.0
22
+ Provides-Extra: all
23
+ Requires-Dist: seaborn>=0.11.0; extra == "all"
24
+ Dynamic: license-file
25
+
26
+ # pltx - Enhanced Matplotlib for Scientific Visualization
27
+
28
+ **A matplotlib wrapper with publication-quality styling and colorblind accessibility features.**
29
+
30
+
31
+ <img src="img/pltx.png" alt="pltx visualization" width="400">
32
+
33
+
34
+
35
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
36
+ <!-- [![License](https://img.shields.io/badge/license-APACHE-green.svg)](LICENSE) -->
37
+
38
+ ---
39
+
40
+ I got bored to always create some custom formatting for my plots, run into reviewers asking for improving readability.
41
+ I want just to import and use it with minor modifications. Here I made something one can install and have pretty looking plots ready to go (just some personal styling on top of matplotlib).
42
+
43
+
44
+ Credits to Mathieu Garrigues for Pasqal colormap.
45
+
46
+ ## Key Features
47
+
48
+ - **Colorblind Accessible** - Progressive line width variation distinguishes lines by thickness AND color
49
+ - **Journal-Ready Presets** - Nature, presentation, and poster styles in one function call
50
+ - **Line Visibility** - Optional outlines and centerlines for better contrast
51
+ - **Drop-in Replacement** - Works with all matplotlib plot types
52
+ - **Auto Color Cycling** - Intelligent palette management with intensity control
53
+
54
+ ## Example Gallery
55
+
56
+ <img src="img/showcase.png" alt="pltx visualization" width="600">
57
+
58
+
59
+ ## Quick Start
60
+
61
+ ```python
62
+ import pltx.pyplot as plt
63
+ import numpy as np
64
+
65
+ # Enable colorblind-friendly progressive widths
66
+ plt.initialize_style(
67
+ palette_name='pasqal',
68
+ vary_linewidth=True, # Lines get progressively thicker
69
+ linewidth_progression_factor=1.3 # 30% increase per line
70
+ )
71
+
72
+ # Plot with automatic styling
73
+ x = np.linspace(0, 10, 100)
74
+ for i in range(5):
75
+ plt.plot_styled(x, np.sin(x + i*0.5),
76
+ color_idx=i,
77
+ centerline=True, # Add thin line on top
78
+ label=f'Line {i+1}')
79
+
80
+ plt.setup_axis(xlabel='x', ylabel='y', grid=True)
81
+ plt.legend()
82
+ plt.savefig('plot.png', dpi=300)
83
+ ```
84
+
85
+ <img src="img/plot.png" alt="pltx visualization" width="600">
86
+
87
+ Some other examples of the colormaps for which you can use `examples/generate_readme_images.py` script.
88
+
89
+ <img src="img/pasqal_heatmaps.png" alt="pltx visualization" width="600">
90
+ <img src="img/pasqal_swatches.png" alt="pltx visualization" width="600">
91
+ <img src="img/pasqal_sine_cosine.png" alt="pltx visualization" width="600">
92
+
93
+
94
+ ## Installation
95
+
96
+ ```bash
97
+ pip install pltx
98
+
99
+ # With optional seaborn support for extended palettes
100
+ pip install pltx[all]
101
+ ```
102
+
103
+ ## Key Features in Detail
104
+
105
+ ### 1. Progressive Line Width (Colorblind Accessible)
106
+
107
+ Each line automatically gets thicker - distinguishable by both color and width:
108
+
109
+ ```python
110
+ plt.initialize_style(
111
+ vary_linewidth=True,
112
+ base_linewidth=2.0,
113
+ linewidth_progression_factor=1.3 # 1.2=gentle, 1.3=moderate, 1.5=strong
114
+ )
115
+ ```
116
+
117
+ **Result:** Line 0: 2.0pt -> Line 1: 2.6pt -> Line 2: 3.4pt -> Line 3: 4.4pt
118
+
119
+ ### 2. Line Enhancements
120
+
121
+ ```python
122
+ # Outline (thick line behind)
123
+ plt.plot_styled(x, y, color_idx=0, outline=True)
124
+
125
+ # Centerline (thin line on top)
126
+ plt.plot_styled(x, y, color_idx=0, centerline=True)
127
+
128
+ # Both (maximum contrast)
129
+ plt.plot_styled(x, y, color_idx=0, outline=True, centerline=True)
130
+ ```
131
+
132
+ ### 3. Journal Presets
133
+
134
+ ```python
135
+ from pltx.rcparams import apply_style_preset
136
+
137
+ apply_style_preset('nature') # Nature journal (Arial, 7-9pt, 3.5")
138
+ apply_style_preset('presentation') # Slides (16-18pt, thick lines)
139
+ apply_style_preset('poster') # Posters (24-28pt)
140
+ ```
141
+
142
+ ### 4. Works with All Plot Types
143
+
144
+ ```python
145
+ from pltx.colors import get_color
146
+
147
+ # Bar plots
148
+ colors = [get_color(i) for i in range(5)]
149
+ plt.bar(categories, values, color=colors)
150
+
151
+ # Scatter plots
152
+ plt.plot_styled(x, y, marker='o', linestyle='', color_idx=0)
153
+
154
+ # All matplotlib functions available!
155
+ ```
156
+
157
+ ## Real-World Example
158
+
159
+ ### Nature Journal Submission
160
+
161
+ ```python
162
+ from pltx.rcparams import apply_style_preset
163
+ import pltx.pyplot as plt
164
+
165
+ # Apply Nature style + accessibility
166
+ apply_style_preset('nature')
167
+ plt.initialize_style(
168
+ vary_linewidth=True,
169
+ base_linewidth=1.0,
170
+ linewidth_progression_factor=1.3
171
+ )
172
+
173
+ # Single column figure
174
+ fig, ax = plt.subplots(figsize=(3.5, 2.6))
175
+
176
+ for i in range(4):
177
+ plt.plot_styled(x, data[i],
178
+ color_idx=i,
179
+ centerline=True, # Better in print
180
+ label=labels[i])
181
+
182
+ plt.setup_axis(xlabel='Time (s)', ylabel='Amplitude (a.u.)')
183
+ plt.legend()
184
+ plt.savefig('figure1.pdf', dpi=300)
185
+ ```
186
+
187
+ ## Documentation
188
+
189
+ - **[FULL_DOCUMENTATION.md](FULL_DOCUMENTATION.md)** - Complete API reference and detailed guide
190
+ - **[examples/](examples/)** - Working examples (demo.py, showcase.py, simple_example.py)
191
+ - 12 test PDFs demonstrating all features
192
+
193
+ ## Quick Reference
194
+
195
+ | Feature | Command |
196
+ |---------|---------|
197
+ | Progressive width | `vary_linewidth=True` |
198
+ | Outline | `outline=True` |
199
+ | Centerline | `centerline=True` |
200
+ | Nature style | `apply_style_preset('nature')` |
201
+ | Color cycling | `color_idx=i` |
202
+ | Axis setup | `setup_axis(xlabel=..., ylabel=...)` |
203
+
204
+ ## Feature Comparison
205
+
206
+ | Feature | matplotlib | pltx |
207
+ |---------|-----------|------|
208
+ | Colorblind accessible | Manual | `vary_linewidth=True` |
209
+ | Line visibility | Complex code | `outline=True` |
210
+ | Journal styles | Research guidelines | One function call |
211
+ | Color palettes | Manual setup | Automatic |
212
+
213
+ ## Progressive Width Factors
214
+
215
+ | Factor | Increase | Best For |
216
+ |--------|----------|----------|
217
+ | 1.2 | 20% | 8-10 lines |
218
+ | 1.3 | 30% | 4-6 lines (DEFAULT) |
219
+ | 1.5 | 50% | 2-4 lines |
220
+
221
+ ## Style Presets
222
+
223
+ | Preset | Fonts | Figure Size | Use Case |
224
+ |--------|-------|-------------|----------|
225
+ | nature | 7-9pt | 3.5"x2.6" | Nature journal |
226
+ | presentation | 16-18pt | 10"x6" | Slides |
227
+ | poster | 24-28pt | 12"x8" | Posters |
228
+ | default | 12-13pt | 6"x4" | General |
229
+
230
+ ## Dependencies
231
+
232
+ **Required:**
233
+ - matplotlib >= 3.5.0
234
+ - numpy >= 1.20.0
235
+
236
+ **Optional:**
237
+ - seaborn >= 0.11.0 (for extended palettes; falls back to matplotlib colormaps)
238
+
239
+ ## Why pltx?
240
+
241
+ - **Accessible** - Works for colorblind viewers (~8% of males)
242
+ - **Publication-Ready** - Journal-specific presets
243
+ - **Easy to Use** - Drop-in replacement for matplotlib
244
+ - **Flexible** - Works with all plot types
245
+ - **Well-Documented** - Comprehensive guides and examples
246
+
247
+ ## Quick Tips
248
+
249
+ 1. **Always use** `vary_linewidth=True` for multi-line plots
250
+ 2. **Choose factor** based on number of lines (1.2 for many, 1.5 for few)
251
+ 3. **Test in grayscale** to verify accessibility
252
+ 4. **Use presets** to match your target medium
253
+ 5. **Combine features** for maximum effect
254
+
255
+ ## Testing
256
+
257
+ The package includes a comprehensive test suite using `pytest`.
258
+
259
+ ```bash
260
+ # Install test dependencies
261
+ pip install pytest
262
+
263
+ # Run all tests
264
+ pytest tests
265
+ ```
266
+
267
+ The test suite covers color palette logic, style initialization, enhanced plotting functions, and style context management.
268
+
269
+
270
+ ---
271
+
272
+ **Version:** 0.1.0
273
+ **Python:** 3.10+
274
+ **Created:** 2026-01-09
275
+ **Author:** Igor Sokolov
276
+
277
+ For complete documentation, see [FULL_DOCUMENTATION.md](FULL_DOCUMENTATION.md)
@@ -0,0 +1,10 @@
1
+ matplotlibx-0.1.0.dist-info/licenses/LICENSE,sha256=wb8wd58mXxEg2_oT9igF1MDuWtT68qfPCh8L7bTVjwc,1069
2
+ pltx/__init__.py,sha256=8sTnid4l5tkZvUKIddWrBykbC51d0iqsa_jO4cqV_l0,86
3
+ pltx/colors.py,sha256=v_bXazCiIFzd_Xpi_QV5d6tOXS7TrYWTXU_9PoNvHf4,8485
4
+ pltx/pyplot.py,sha256=ePyqwf11b7Tt1CtTMKOnsBcQO5f9jlQbtG5gcPM22Bc,14239
5
+ pltx/rcparams.py,sha256=zEl70LP1QNf7Ee0Se4b0kZJatL3zSN4wIGmYAaFSOfc,8919
6
+ pltx/style.py,sha256=8eW3WwKensMZKL_QmCI3Km4V8rIVTHUywnuGY4wLBts,24473
7
+ matplotlibx-0.1.0.dist-info/METADATA,sha256=S4KlJKD9W1YdlfmgyX6EEWz9x_0pcy9N1Qdfg7UVAIQ,7976
8
+ matplotlibx-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
9
+ matplotlibx-0.1.0.dist-info/top_level.txt,sha256=jpElzOmUpfFC4IoEBFvyUA9zYKlg9RPr59Iv14MrzFc,5
10
+ matplotlibx-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Igor Sokolov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ pltx
pltx/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """pltx - Enhanced Matplotlib for Scientific Visualization."""
2
+
3
+ __version__ = "0.1.0"
pltx/colors.py ADDED
@@ -0,0 +1,335 @@
1
+ """Color palettes and utilities for pltx."""
2
+
3
+ from typing import List, Tuple
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+
7
+ try:
8
+ import seaborn as sns
9
+ HAS_SEABORN = True
10
+ except ImportError:
11
+ HAS_SEABORN = False
12
+
13
+ # Type alias for RGB color
14
+ Color = Tuple[float, float, float]
15
+
16
+
17
+ class ColorPalette:
18
+ """Manages color palettes for scientific visualizations."""
19
+
20
+ # Default palettes - easily customizable
21
+ DEFAULT_PALETTE = "plasma_r"
22
+ DEFAULT_SIZE = 10
23
+
24
+ # Predefined palette collections
25
+ SEQUENTIAL_PALETTES = [
26
+ "plasma_r",
27
+ "viridis",
28
+ "mako_r",
29
+ "rocket_r",
30
+ "crest",
31
+ ]
32
+
33
+ DIVERGING_PALETTES = [
34
+ "coolwarm",
35
+ "RdBu_r",
36
+ "vlag",
37
+ "icefire",
38
+ ]
39
+
40
+ CATEGORICAL_PALETTES = [
41
+ "tab10",
42
+ "Set2",
43
+ "husl",
44
+ "Paired",
45
+ ]
46
+
47
+ def __init__(
48
+ self,
49
+ palette_name: str = DEFAULT_PALETTE,
50
+ palette_size: int = DEFAULT_SIZE,
51
+ ):
52
+ """Initialize color palette.
53
+
54
+ Parameters
55
+ ----------
56
+ palette_name : str
57
+ Name of the seaborn or matplotlib color palette
58
+ palette_size : int
59
+ Number of colors in the palette
60
+ """
61
+ self.palette_name = palette_name
62
+ self.palette_size = palette_size
63
+ self._palette = self._create_palette(palette_name, palette_size)
64
+
65
+ @staticmethod
66
+ def _create_palette(palette_name: str, palette_size: int) -> List[Color]:
67
+ """Create color palette using seaborn if available, else matplotlib.
68
+
69
+ If seaborn is available but doesn't know the palette name (e.g. it is
70
+ a registered matplotlib colormap such as 'pasqal'), we fall back to
71
+ sampling the matplotlib colormap directly.
72
+
73
+ Parameters
74
+ ----------
75
+ palette_name : str
76
+ Name of the color palette (seaborn palette or mpl colormap name)
77
+ palette_size : int
78
+ Number of colors
79
+
80
+ Returns
81
+ -------
82
+ List[Color]
83
+ List of RGB tuples
84
+ """
85
+ if HAS_SEABORN:
86
+ try:
87
+ return sns.color_palette(palette_name, palette_size)
88
+ except (ValueError, KeyError):
89
+ # palette_name is not a seaborn palette — try it as an mpl
90
+ # colormap (e.g. a custom one registered via mpl.colormaps)
91
+ pass
92
+
93
+ # Sample from matplotlib colormap
94
+ try:
95
+ cmap = plt.get_cmap(palette_name)
96
+ except (ValueError, KeyError):
97
+ # Last resort: fall back to viridis
98
+ cmap = plt.get_cmap('viridis')
99
+
100
+ # Sample colors evenly across the colormap
101
+ colors = [
102
+ cmap(i / max(palette_size - 1, 1))
103
+ for i in range(palette_size)
104
+ ]
105
+ # Convert to RGB tuples (drop alpha channel)
106
+ return [(c[0], c[1], c[2]) for c in colors]
107
+
108
+ @property
109
+ def palette(self) -> List[Color]:
110
+ """Get the current color palette."""
111
+ return self._palette
112
+
113
+ def get_color(self, idx: int) -> Color:
114
+ """Get a color from the palette by index.
115
+
116
+ Parameters
117
+ ----------
118
+ idx : int
119
+ Index of the color in the palette
120
+
121
+ Returns
122
+ -------
123
+ Color
124
+ RGB color tuple
125
+
126
+ Raises
127
+ ------
128
+ IndexError
129
+ If index is out of range
130
+ """
131
+ if idx < 0 or idx >= len(self._palette):
132
+ raise IndexError(
133
+ f"Color index {idx} out of range "
134
+ f"for palette of size {len(self._palette)}"
135
+ )
136
+ return self._palette[idx]
137
+
138
+ def get_colors(self, indices: List[int]) -> List[Color]:
139
+ """Get multiple colors from the palette.
140
+
141
+ Parameters
142
+ ----------
143
+ indices : List[int]
144
+ Indices of colors to retrieve
145
+
146
+ Returns
147
+ -------
148
+ List[Color]
149
+ List of RGB color tuples
150
+ """
151
+ return [self.get_color(idx) for idx in indices]
152
+
153
+ def cycle_color(self, idx: int) -> Color:
154
+ """Get a color with automatic wrapping for large indices.
155
+
156
+ Parameters
157
+ ----------
158
+ idx : int
159
+ Index (will be wrapped if >= palette_size)
160
+
161
+ Returns
162
+ -------
163
+ Color
164
+ RGB color tuple
165
+ """
166
+ return self._palette[idx % len(self._palette)]
167
+
168
+ @staticmethod
169
+ def adjust_intensity(
170
+ color: Color,
171
+ intensity: float,
172
+ ) -> Color:
173
+ """Adjust color intensity (brightness).
174
+
175
+ Parameters
176
+ ----------
177
+ color : Color
178
+ RGB color tuple
179
+ intensity : float
180
+ Intensity factor (0.0-1.0). Lower values make colors lighter.
181
+
182
+ Returns
183
+ -------
184
+ Color
185
+ Adjusted RGB color tuple
186
+ """
187
+ intensity = np.clip(intensity, 0.0, 1.0)
188
+ return tuple(c * intensity + (1 - intensity) for c in color)
189
+
190
+ @staticmethod
191
+ def adjust_alpha(
192
+ color: Color,
193
+ alpha: float,
194
+ ) -> Tuple[float, float, float, float]:
195
+ """Add alpha channel to RGB color.
196
+
197
+ Parameters
198
+ ----------
199
+ color : Color
200
+ RGB color tuple
201
+ alpha : float
202
+ Alpha value (0.0-1.0)
203
+
204
+ Returns
205
+ -------
206
+ Tuple[float, float, float, float]
207
+ RGBA color tuple
208
+ """
209
+ alpha = np.clip(alpha, 0.0, 1.0)
210
+ return (*color, alpha)
211
+
212
+ @staticmethod
213
+ def create_gradient(
214
+ color1: Color,
215
+ color2: Color,
216
+ n_steps: int,
217
+ ) -> List[Color]:
218
+ """Create a gradient between two colors.
219
+
220
+ Parameters
221
+ ----------
222
+ color1 : Color
223
+ Starting color
224
+ color2 : Color
225
+ Ending color
226
+ n_steps : int
227
+ Number of steps in the gradient
228
+
229
+ Returns
230
+ -------
231
+ List[Color]
232
+ List of colors forming the gradient
233
+ """
234
+ gradient = []
235
+ for i in range(n_steps):
236
+ t = i / (n_steps - 1)
237
+ color = tuple(
238
+ c1 * (1 - t) + c2 * t
239
+ for c1, c2 in zip(color1, color2)
240
+ )
241
+ gradient.append(color)
242
+ return gradient
243
+
244
+ def set_palette(
245
+ self,
246
+ palette_name: str,
247
+ palette_size: int = None,
248
+ ) -> None:
249
+ """Change the current palette.
250
+
251
+ Parameters
252
+ ----------
253
+ palette_name : str
254
+ Name of the seaborn or matplotlib color palette
255
+ palette_size : int, optional
256
+ Number of colors. If None, uses current palette_size.
257
+ """
258
+ self.palette_name = palette_name
259
+ if palette_size is not None:
260
+ self.palette_size = palette_size
261
+ self._palette = self._create_palette(self.palette_name, self.palette_size)
262
+
263
+ def __len__(self) -> int:
264
+ """Return the number of colors in the palette."""
265
+ return len(self._palette)
266
+
267
+ def __getitem__(self, idx: int) -> Color:
268
+ """Get a color by index."""
269
+ return self.get_color(idx)
270
+
271
+ def __iter__(self):
272
+ """Iterate over colors in the palette."""
273
+ return iter(self._palette)
274
+
275
+ def __repr__(self) -> str:
276
+ """String representation."""
277
+ return f"ColorPalette('{self.palette_name}', size={self.palette_size})"
278
+
279
+
280
+ # Global default color palette
281
+ _default_palette = ColorPalette()
282
+
283
+
284
+ def get_default_palette() -> ColorPalette:
285
+ """Get the global default color palette."""
286
+ return _default_palette
287
+
288
+
289
+ def set_default_palette(
290
+ palette_name: str,
291
+ palette_size: int = ColorPalette.DEFAULT_SIZE,
292
+ ) -> None:
293
+ """Set the global default color palette.
294
+
295
+ Parameters
296
+ ----------
297
+ palette_name : str
298
+ Name of the seaborn color palette
299
+ palette_size : int
300
+ Number of colors in the palette
301
+ """
302
+ global _default_palette
303
+ _default_palette = ColorPalette(palette_name, palette_size)
304
+
305
+
306
+ def get_color(idx: int) -> Color:
307
+ """Get a color from the default palette.
308
+
309
+ Parameters
310
+ ----------
311
+ idx : int
312
+ Color index
313
+
314
+ Returns
315
+ -------
316
+ Color
317
+ RGB color tuple
318
+ """
319
+ return _default_palette.get_color(idx)
320
+
321
+
322
+ def cycle_color(idx: int) -> Color:
323
+ """Get a color from the default palette with automatic wrapping.
324
+
325
+ Parameters
326
+ ----------
327
+ idx : int
328
+ Color index (will wrap if >= palette size)
329
+
330
+ Returns
331
+ -------
332
+ Color
333
+ RGB color tuple
334
+ """
335
+ return _default_palette.cycle_color(idx)