-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
[MNT]: Colormaps odd behavior (1 != 1.0) #28198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The relevant line is here: matplotlib/lib/matplotlib/colors.py Line 713 in d67f08e
The Python docstring appears to comment on the difference:
|
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 |
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.
Big fan of this proposal since I think it makes it clearer that that object is indexable. |
I think
|
I agree that
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 |
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
|
NominalColormap? since it's used for Nominal measurements and the scale is what imposes the constraints on behavior (Stevens 1946)
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 |
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 |
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
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! |
This makes it clearer that we are just indexing into a list of colors. Calling cmaps with an integer argument is somewhat obscure and we want to move away from this. See also matplotlib#28198.
Summary
I encountered some unexpected behavior while usingn
matplotlib.colormaps
. Apparently,a_colormap(1)
outputs the wrong result. The color it produces is indistinguishable froma_colormap(0)
, although it is slightly different.a_colormap(1.0)
produces the correct result, buta_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.
The text was updated successfully, but these errors were encountered: