| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
The relevant line is here:
matplotlib/lib/matplotlib/colors.py
Line 713 in d67f08e
| def __call__(self, X, alpha=None, bytes=False): |
The Python docstring appears to comment on the difference:
X : float or int, `~numpy.ndarray` or scalar The data value(s) to convert to RGBA. For floats, *X* should be in the interval ``[0.0, 1.0]`` to return the RGBA values ``X*100`` percent along the Colormap line. For integers, *X* should be in the interval ``[0, Colormap.N)`` to return RGBA values *indexed* from the Colormap with index ``X``.
Colormap.N is 256 by default. It's mentioned in the API reference as well. However, it may still be useful to mention this behavior in the more accessible docs for colormap (link).
It would be useful to clarify this in the docs.
Totally agree, though maybe as two subsections in colormap manipulaton?
Can I work on it , or is it in a decision making phase ?
You can add the documentation as described.
From the API design perspective: First and foremost color maps are a mapping from [0, 1] to colors. The whole quantization concept should be an implementation detail and not mixed into the primary API, i.e. I consider it a design mistake that calling with integers returns the quantized values.
I’ve not completely thought this through yet and there are quirks like the qualitative colormaps (which IMHO should better be a different type of object not a color map) that have a strong dependency on quantization. But I propose to deprecate calling colormaps with ints and instead move this to a new syntax, possibly cmap[i] or cmap.colors[i].
I’ve not completely thought this through yet and there are quirks like the qualitative colormaps (which IMHO should better be a different type of object not a color map)
I think it's a weird quirk/implementation artifact of ListedColormap that it allows resampling since technically there's no guarantee that the colors are arranged in a space where resampling makes any sense. On the different type of object, I think a DiscreteNorm that legends can read maybe makes more sense for how nominal colormaps are used in practice.
that have a strong dependency on quantization. But I propose to deprecate calling colormaps with ints and instead move this to a new syntax, possibly cmap[i] or cmap.colors[i].
Big fan of this proposal since I think it makes it clearer that that object is indexable.
I propose to deprecate calling colormaps with ints and instead move this to a new syntax, possibly cmap[i] or cmap.colors[i].
I think cmap.colors[i] is the better choice because
I agree that cmap.colors[i] is better. But it still needs some work:
makes more sense for how nominal colormaps are used in practice.
How/and what for are they used in practice? The only example I've found in our codebase seems to be https://matplotlib.org/stable/gallery/pie_and_polar_charts/nested_pie.html and I have to confess, that that usage is not quite understandable for someone not well versed in Colormaps
How/and what for are they used in practice?
Mostly choropleths (mapped heatmaps), but also, going back to my GSOC 😓, they're useful for showing categories on rasters:

ETA: Also I have totally exploited interpolation on ListedColormap to make arbitrarily colored diverging color maps.
Ok, deprecating int mapping is not so trivial, because BoundaryNorm returns ints. Before we can deprecate, we need an alternative story for qualitative maps, likely
DiscreteColormap (or QualitativeColormap)
NominalColormap? since it's used for Nominal measurements and the scale is what imposes the constraints on behavior (Stevens 1946)
warn if BoundaryNorm and DiscreteColormap are not used together. Since colormaps and norms don't know about each other, this check would likely have to be done during initialization of ScalarMappable. - This is not perfect, but can detect many cases of I'll-configured mappings.
BoundaryNorm is used a ton for (ETA and far as I can tell, designed for) ordinal data, and in that case it's perfectly appropriate to use most of the sequential and many of the diverging maps & to essentially let the norm cut up the color. That's what geopandas is also doing frequently.
Ah, eventually, I understand BondaryNorm. It needs a ncolors parameter specifying the number of colors in the colormap to be able to map to integers in the whole cmap range. We should investigate whether we can achieve the same by mapping to [0, 1] instead (theoretically no problem, but there may be numeric edge effects. Probably also needs digging into the design decision why it maps to ints in the first place). This would remove the need to accept ints on colorbars for this context. It’s also simpler because the user does not have to do a manual matching of the ncolors parameter.
The only example I've found in our codebase seems to be https://matplotlib.org/stable/gallery/pie_and_polar_charts/nested_pie.html and I have to confess, that that usage is not quite understandable for someone not well versed in Colormaps
A bit of a tangent but I agree the color handling mechanism in that example is indeed rather impossible to understand. I would suggest rewriting as something like
diff --git i/galleries/examples/pie_and_polar_charts/nested_pie.py w/galleries/examples/pie_and_polar_charts/nested_pie.py
index c83b4f6f84..92d77e1975 100644
--- i/galleries/examples/pie_and_polar_charts/nested_pie.py
+++ w/galleries/examples/pie_and_polar_charts/nested_pie.py
@@ -9,6 +9,7 @@ in Matplotlib. Such charts are often referred to as donut charts.
See also the :doc:`/gallery/specialty_plots/leftventricle_bullseye` example.
"""
+import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
@@ -31,9 +32,11 @@ fig, ax = plt.subplots()
size = 0.3
vals = np.array([[60., 32.], [37., 40.], [29., 10.]])
-cmap = plt.colormaps["tab20c"]
-outer_colors = cmap(np.arange(3)*4)
-inner_colors = cmap([1, 2, 5, 6, 9, 10])
+outer_colors = mpl.colors.to_rgba_array(["C0", "C1", "C2"])[:, :3]
+desaturation = [.6, .3]
+inner_hsv = np.repeat(mpl.colors.rgb_to_hsv(outer_colors), len(desaturation), axis=0)
+inner_hsv[:, 1] *= np.tile(desaturation, len(outer_colors))
+inner_colors = mpl.colors.hsv_to_rgb(inner_hsv)
ax.pie(vals.sum(axis=1), radius=1, colors=outer_colors,
wedgeprops=dict(width=size, edgecolor='w'))although perhaps complicated color-generation code here just distracts from the example (but again, perhaps realistic use cases of this kind of nested pies would be happy to have the code to generate suitable "sub-colors" for the inner ring). Alternatively at least cmap(np.arange(3) * 4) could be rewritten as cmap([0, 4, 8]) with the comment "select some suitable colors".
I just ran into this in the wild because I was testing that my code was behaving correctly.
The unfortunate sequence of events is that I have some float field and I'm clamping it... based on the context of this issue, you can guess, to ints, not to floats.
So my
value = calculation() value = min(value,1) value = max(value,0) color = colormap(value)
produced the same color for both 0 and 1. Or for wildly overshooting or undershooting the [0,1] range.
I get that this is maybe not easy to change and I think you're already making the right call with deprecating the int support.
Maybe throw a warning if you're detecting an int in the input, as a quick fix?
Also bump, please fix it. <3
Thanks for your work!
| Back | FazBrowse Home | New Git URL |
Summary
I encountered some unexpected behavior while usingn matplotlib.colormaps. Apparently, a_colormap(1) outputs the wrong result. The color it produces is indistinguishable from a_colormap(0), although it is slightly different. a_colormap(1.0) produces the correct result, but a_colormap(1.0) != a_colormap(1).
What is going on? It would be useful to clarify this in the docs.
Example
Output
Proposed fix
Improved documentation, or added consistency to make integer values work for the upper endpoint.