1212PALETTES = {
1313 'alges' : ['#070f15' , '#0f212d' , '#19384d' , '#275474' , '#326c94' , '#4b87af' , '#78abce' , '#a7c9e1' , '#cfe2ef' ],
1414 'alges_muted' : ['#142b3b' ,'#1e4058' , '#285676' , '#326c94' , '#5a89a9' , '#84a6be' , '#c1d2de' ],
15+ 'navia' : ['#0b1627' ,'#16345c' ,'#19598c' ,'#29738e' ,'#398285' ,'#4b9379' ,'#66aa6a' ,'#98ca6e' ,'#d9e5a6' ,'#fcf5d9' ],
1516 'navia_r' : ['#011a5a' ,'#104261' ,'#205f61' ,'#4c734d' ,'#828434' ,'#c09036' ,'#f2a069' ,'#fcb3b3' ,'#fee5e5' ],
1617 'precision' : ['#51648a' ,'#796982' ,'#9d6c79' ,'#c47673' ,'#e98c76' ,'#edb18e' ,'#edd3b3' ],
1718 'precision_dark' : ['#0b1425' ,'#133859' ,'#48587a' ,'#6c5e74' ,'#90606c' ,'#bc6461' ,'#e67960' ,'#eb937f' ,'#f2bcaf' ],
2021 'batlow' : ['#fbcdfa' ,'#fcb3b3' ,'#f2a069' ,'#c09036' ,'#828434' ,'#4c734d' ,'#205f61' ,'#104261' ,'#011a5a' ],
2122 'glasgow' : ['#371437' ,'#4e1921' ,'#6a2810' ,'#74471c' ,'#716328' ,'#697c47' ,'#61917c' ,'#74a8af' ,'#a8bed7' ,'#dcd1e8' ],
2223 'lipari' : ['#0b1425' ,'#133859' ,'#48587a' ,'#6c5e74' ,'#90606c' ,'#bc6461' ,'#e67960' ,'#e9a278' ,'#e8c79e' ,'#fef5db' ],
23- 'navia' : ['#0b1627' ,'#16345c' ,'#19598c' ,'#29738e' ,'#398285' ,'#4b9379' ,'#66aa6a' ,'#98ca6e' ,'#d9e5a6' ,'#fcf5d9' ],
2424 'nuuk' : ['#11598c' ,'#2c6384' ,'#4b7182' ,'#70868c' ,'#939b96' ,'#acad95' ,'#bbb98b' ,'#c7c581' ,'#e1e08c' ,'#fbf7b3' ],
2525 'bamako' : ['#073a46' ,'#12433f' ,'#214f33' ,'#385d2c' ,'#547032' ,'#738437' ,'#978e33' ,'#bfa830' ,'#e2c66b' ,'#fee4ab' ],
2626 'tokio' : ['#1f1032' ,'#4b1f42' ,'#6a404e' ,'#715651' ,'#746651' ,'#767a54' ,'#7d9857' ,'#8ec26d' ,'#c3e0a7' ,'#eff5db' ],
2727 'bilbao' : ['#471010' ,'#752329' , '#94454b' , '#a16157' , '#a6775a' , '#ac8c5f' , '#b6a672' , '#c2bca1' , '#d4d1cd' , '#ffffff' ]}
2828
29+ def get_available_palettes ():
30+ """
31+ Get a list of all available palette names.
32+
33+ Returns
34+ -------
35+ list of str
36+ Names of available color palettes
37+
38+ Examples
39+ --------
40+ >>> palettes = get_available_palettes()
41+ >>> print(palettes)
42+ ['alges', 'alges_muted', 'navia_r', 'precision', ...]
43+ """
44+ return list (PALETTES .keys ())
45+
46+ def get_palette (name ):
47+ """
48+ Get a color palette by name.
49+
50+ Parameters
51+ ----------
52+ name : str
53+ Name of the palette. Use get_available_palettes() to see options.
54+
55+ Returns
56+ -------
57+ list of str
58+ List of hex color codes in the palette
59+
60+ Raises
61+ ------
62+ ValueError
63+ If palette name is not found
64+
65+ Examples
66+ --------
67+ >>> colors = get_palette('alges')
68+ >>> print(colors[0])
69+ '#070f15'
70+
71+ >>> # Use palette colors directly
72+ >>> colors = get_palette('glasgow')
73+ >>> plt.scatter(x, y, c=colors[3])
74+ """
75+ if name not in PALETTES :
76+ available = ', ' .join (get_available_palettes ())
77+ raise ValueError (f"Palette '{ name } ' not found. Available palettes: { available } " )
78+ return PALETTES [name ]
79+
80+ def get_palette_colormap (name , reverse = False ):
81+ """
82+ Create a matplotlib colormap from a named palette.
83+
84+ Parameters
85+ ----------
86+ name : str
87+ Name of the palette. Use get_available_palettes() to see options.
88+ reverse : bool, optional
89+ If True, reverses the color order. Default is False.
90+
91+ Returns
92+ -------
93+ matplotlib.colors.LinearSegmentedColormap
94+ Matplotlib colormap object ready for use in plotting
95+
96+ Raises
97+ ------
98+ ValueError
99+ If palette name is not found
100+
101+ Examples
102+ --------
103+ >>> cmap = get_palette_colormap('batlow')
104+ >>> plt.imshow(data, cmap=cmap)
105+
106+ >>> # Reverse the colormap
107+ >>> cmap_r = get_palette_colormap('navia', reverse=True)
108+ >>> plt.contourf(X, Y, Z, cmap=cmap_r)
109+
110+ >>> # Use in PlotStyle
111+ >>> style = PlotStyle(cmap=get_palette_colormap('lipari'))
112+ >>> plt.scatter(x, y, c=values, cmap=style.cmap)
113+ """
114+ colors = get_palette (name )
115+ if reverse :
116+ colors = colors [::- 1 ]
117+ return Colormap .from_list (f'{ name } { "_r" if reverse else "" } ' , colors )
118+
119+ def show_palettes (names = None , figsize = None , n_colors = 256 , title = "Available Color Palettes" ):
120+ """
121+ Display color palettes as horizontal gradient bars for visual comparison.
122+
123+ Parameters
124+ ----------
125+ names : list of str, optional
126+ Specific palette names to display. If None, shows all available palettes.
127+ figsize : tuple, optional
128+ Figure size as (width, height) in inches. If None, auto-calculated based
129+ on the number of palettes (width=8, height=0.4*n_palettes).
130+ n_colors : int, optional
131+ Number of color samples to display for each palette gradient. Default is 256.
132+ title : str, optional
133+ Title for the figure. Default is "Available Color Palettes".
134+
135+ Returns
136+ -------
137+ matplotlib.figure.Figure
138+ The figure object containing the palette visualizations
139+
140+ Examples
141+ --------
142+ >>> # Show all palettes
143+ >>> show_palettes()
144+
145+ >>> # Show specific palettes
146+ >>> show_palettes(names=['alges', 'precision', 'navia'])
147+
148+ >>> # Customize figure size
149+ >>> show_palettes(figsize=(10, 8))
150+
151+ >>> # Show palettes without title
152+ >>> show_palettes(title=None)
153+ """
154+ # Determine which palettes to show
155+ if names is None :
156+ names = get_available_palettes ()
157+ else :
158+ # Validate provided names
159+ invalid = [n for n in names if n not in PALETTES ]
160+ if invalid :
161+ raise ValueError (f"Invalid palette names: { invalid } . Use get_available_palettes() to see options." )
162+
163+ n_palettes = len (names )
164+
165+ # Auto-calculate figure size if not provided
166+ if figsize is None :
167+ figsize = (8 , 0.4 * n_palettes + 0.5 )
168+
169+ # Create figure and axes
170+ fig , axes = plt .subplots (n_palettes , 1 , figsize = figsize )
171+
172+ # Handle single palette case (axes won't be an array)
173+ if n_palettes == 1 :
174+ axes = [axes ]
175+
176+ # Create gradient array for displaying colors
177+ gradient = np .linspace (0 , 1 , n_colors ).reshape (1 , - 1 )
178+
179+ # Plot each palette
180+ for ax , name in zip (axes , names ):
181+ # Get colormap and display as gradient
182+ cmap = get_palette_colormap (name )
183+ ax .imshow (gradient , aspect = 'auto' , cmap = cmap )
184+
185+ # Remove ticks and spines
186+ ax .set_xticks ([])
187+ ax .set_yticks ([])
188+ for spine in ax .spines .values ():
189+ spine .set_visible (False )
190+
191+ # Add palette name as y-label
192+ ax .set_ylabel (name , rotation = 0 , ha = 'right' , va = 'center' , fontsize = 10 )
193+
194+ # Add title if provided
195+ if title :
196+ fig .suptitle (title , fontsize = 12 , fontweight = 'bold' )
197+
198+ plt .tight_layout ()
199+
200+ if not in_notebook ():
201+ return fig
202+
203+
29204class PlotStyle :
30205 """
31206 Manage matplotlib plot styles with predefined themes.
32-
207+
33208 Parameters
34209 ----------
35210 theme : str, optional
36- Theme name. Available: 'darkgrid', 'whitegrid', 'dark', 'white',
211+ Theme name. Available: 'darkgrid', 'whitegrid', 'dark', 'white',
37212 'alges', 'minimal', 'publication'
38213 color : str, optional
39214 Primary color for plots
40- cmap : str or colormap, optional
41- Colormap for plots
42-
215+ cmap : str, list, or colormap, optional
216+ Colormap for plots. Can be:
217+ - str: Name from PALETTES (e.g., 'alges', 'batlow') or matplotlib colormap (e.g., 'viridis')
218+ - list: List of hex colors to create a custom colormap
219+ - Colormap object: Matplotlib colormap instance
220+ precision_cmap : str, list, or colormap, optional
221+ Colormap for precision/uncertainty plots. Same format as cmap.
222+
43223 Attributes
44224 ----------
45225 theme : str
@@ -48,7 +228,9 @@ class PlotStyle:
48228 Primary plot color
49229 cmap : str or colormap
50230 Plot colormap
51-
231+ precision_cmap : str or colormap
232+ Precision/uncertainty colormap
233+
52234 Examples
53235 --------
54236 Basic usage with context manager::
@@ -57,6 +239,22 @@ class PlotStyle:
57239 plt.plot(x, y, color=style.color)
58240 plt.imshow(data, cmap=style.cmap)
59241
242+ Using custom palettes from PALETTES::
243+
244+ style = PlotStyle(cmap='batlow') # Uses palette from PALETTES
245+ plt.imshow(data, cmap=style.cmap)
246+
247+ Using custom color list::
248+
249+ custom_colors = ['#ff0000', '#00ff00', '#0000ff']
250+ style = PlotStyle(cmap=custom_colors)
251+ plt.scatter(x, y, c=values, cmap=style.cmap)
252+
253+ Using matplotlib colormaps::
254+
255+ style = PlotStyle(cmap='viridis') # Standard matplotlib colormap
256+ plt.contourf(X, Y, Z, cmap=style.cmap)
257+
60258 Simple usage without context manager::
61259
62260 style = PlotStyle(theme='alges')
@@ -222,7 +420,7 @@ def __init__(self,
222420 theme = None ,
223421 color = None ,
224422 cmap = None ,
225- precision_cmap = None , ):
423+ precision_cmap = None ):
226424
227425 if theme and theme not in self .THEMES :
228426 raise ValueError (f"Theme '{ theme } ' not found. Available: { list (self .THEMES .keys ())} " )
@@ -247,15 +445,39 @@ def _set_color(self, color):
247445
248446 def _set_cmap (self , cmap ):
249447 if cmap :
250- return cmap
448+ # Handle string input - check PALETTES first, then assume matplotlib colormap
449+ if isinstance (cmap , str ):
450+ if cmap in PALETTES :
451+ return get_palette_colormap (cmap )
452+ else :
453+ # Return as-is, assuming it's a matplotlib colormap name
454+ return cmap
455+ # Handle list input - create colormap from color list
456+ elif isinstance (cmap , list ):
457+ return Colormap .from_list ('custom_cmap' , cmap )
458+ # Otherwise return as-is (already a colormap object)
459+ else :
460+ return cmap
251461 elif self .theme :
252462 return self .THEMES [self .theme ]['cmap' ]
253463 else :
254464 return self .DEFAULT_CMAP
255-
465+
256466 def _set_precision_cmap (self , cmap ):
257467 if cmap :
258- return cmap
468+ # Handle string input - check PALETTES first, then assume matplotlib colormap
469+ if isinstance (cmap , str ):
470+ if cmap in PALETTES :
471+ return get_palette_colormap (cmap )
472+ else :
473+ # Return as-is, assuming it's a matplotlib colormap name
474+ return cmap
475+ # Handle list input - create colormap from color list
476+ elif isinstance (cmap , list ):
477+ return Colormap .from_list ('custom_precision_cmap' , cmap )
478+ # Otherwise return as-is (already a colormap object)
479+ else :
480+ return cmap
259481 elif self .theme :
260482 return self .THEMES [self .theme ]['precision_cmap' ]
261483 else :
0 commit comments