| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
I vote leave it in cm for now, but since there's HTML colorbar reprs now (#17888) can you use this to print the list of colormaps? |
Sorry, something went wrong.
|
You mean as ColormapRegistry.__repr_html__? In principle yes. - I'm always a bit cautious on long reprs. |
Sorry, something went wrong.
|
I hear but like I have that page of the docs printed out cause of how often I look up the colormaps. |
Sorry, something went wrong.
|
guess I'm not particularly following the use-case for this versus just get_cmap? If we just want a list of colormaps, why can't we just store that as a flat list and give the user access to it? I'm not following why we need a mapping. |
Sorry, something went wrong.
|
This is motivated in the discussion #16991 (review). The idea is to have a more concise colormap mangement and API. The current state is a bit ad-hoc. You have a private (formerly public) dict and functions get_cmap, register_cmap working on that. By making the dict private, users lost access to the keys. You could store the names in a flat list, but that would be redundant to the keys of the private dict and needs synchronization. Also, users could manipulate that list - though I'm not too concerned about this. The whole construct is conceptually a mapping. So why not make it one? Collecting everything into one class makes it easier to manage everything consistently. It makes it also easier to move the user-facing actions out of cm. While to be thought about in more detail, I can imagine that the colormaps can be accessed in the future from the top-level module such as: import matplotlib as mpl mpl.colormaps.register(my_colormap) cmap = mpl.colormaps['jet'] |
Sorry, something went wrong.
|
OK, so this is just a first step to this class being a new interface for the colormap registry. Because right now, if I use cmap_register this class will no longer return the new colormap, right? I guess I think at the very least it should return the current state of the registry, not the state at import. I'd prefer not having to import cm to get this list. I always import pyplot, so I'd be fine with it there, but I understand the aesthetic problem with "we discourage pyplot, except for these 4 things we still think are useful". |
Sorry, something went wrong.
Correct.
It does. The internal self._cmaps is a reference to the same object as _cmap_registry: In [9]: cm.colormaps._cmaps is cm._cmap_registry Out[9]: True Thus, the object plays nicely with the existing functions. (Whether or not we deprecate and remove them or just discourage their use is still to be decided.)
Agreed. There should be no need for the user to import cm. The nice thing with an object is that we can easily reference it from anywhere. We can instantiate it in cm but do from matplotlib.cm import colormaps in matplotlib and pyplot (or we directly instantiate it in matplotlib and only import in pyplot. |
Sorry, something went wrong.
|
👍 I like this. I think it makes it clear what is going on and allows for flexibility moving forward. What would you think about adding a register() method directly to this class? It seems like people would want to add/write directly to this registry. Or even allow people to set the colormaps just like a dict, but raise on name collisions? |
Sorry, something went wrong.
|
@greglucas That's exactly the plan. I'm not completely decided on register() vs. dict-setitem, which is why it's not yet in the PR. I tend towards an explicit register() because we likely won't have a full mutable mapping functionality (or semantic, e.g. concerning overwriting existing keys). |
Sorry, something went wrong.
|
@timhoffm, is there anything here you want help with or feedback on? I'd like to see this get in for the 3.5 release if possible. |
Sorry, something went wrong.
|
Note: I've added register(..., force=False). It is currently not possible to override builtin colormaps via the ColormapRegistry API. That's intentional. I'm questioning the need for such an operation. For now, people can still use cm.register_cmap(..., override_builtin=True) if really needed (or overwrite ColormapRegistry._cmaps). To make mpl.colormaps work, I had to do a late import of cm because that depends on rcParams to be set up. I intend to clean this all up later. But that requires reorganization of the rcParams code, which would be too much for 3.5. Nevertheless, I'd like to get the colormaps API into 3.5. Hence the workaround. |
Sorry, something went wrong.
There was a problem hiding this comment.
Overall 👍 I think it is good to have this easily available at the top-level namespace.
It took me a while to find this information in the docs pages. I think it could be added to the main Colormaps docs section. Right now, those all still use mpl.cm.get_cmap().
https://62710-1385122-gh.circle-artifacts.com/0/doc/build/html/tutorials/colors/colormaps.html#sphx-glr-tutorials-colors-colormaps-py
Another area that is missing is how to register a cmap after creating it. The "Creating colormaps in matplotlib" section just shows how to create a colormap, perhaps a final section describing "this is how to register a colormap for future use"
https://62710-1385122-gh.circle-artifacts.com/0/doc/build/html/tutorials/colors/colormap-manipulation.html#creating-colormaps-in-matplotlib
Sorry, something went wrong.
Docs certainly need adaption. However, I first want to make sure that the API is agreed on before starting to change the docs. |
Sorry, something went wrong.
|
This seems fine to me. I feel that we will still have some confusion, as the colormap registry is ephemeral, so why have so much public interface to it. If I need to create the colormap at startup, I'd just use it directly. So I think the added docs will be helpful to guide folks and make it clear what is happening here. |
Sorry, something went wrong.
| mpl.colormaps.register(my_colormap) | ||
| """ | ||
| def __init__(self, cmaps): | ||
| self._cmaps = cmaps |
There was a problem hiding this comment.
makes sense why to do this as to wrap this around the existing dict, but I am wary of sharing the underlying storage this way.
Sorry, something went wrong.
There was a problem hiding this comment.
It's very intentional, that the registry shares the underlying storage: For a transition period, the registry and the cmap functions will coexists. Due to API quirks in the functions, it's better if both can directly work on the underlying storage and not one API calling the orther.
If you only have concerns with the constructor accepting a prebuilt mapping, we can change it to creating an empty registry by default and have a private ColormapRegistry._from_dict() that we use to inject the existing dict.
Sorry, something went wrong.
There was a problem hiding this comment.
I would be happy warning in the init docstring saying we keep a reference to the input dict.
It makes total sense why you are doing it this way, just set off alarm bells in my head (we have had some cases at BNL where we did not mean to do this and ended up with hard-to-debug action-at-a-distance bugs!).
Sorry, something went wrong.
| return ('ColormapRegistry; available colormaps:\n' + | ||
| ', '.join(f"'{name}'" for name in self)) | ||
|
|
||
| def register(self, cmap, *, name=None, force=False): |
There was a problem hiding this comment.
I think we need an deregister as well? with force you can over-write an existing colormap, but I still think we will want a way to remove a key.
I do not think we want to promote this to a MutableMapping and support the full set of del / pop / clear. The act of adding and removing color maps is a different activity than the act of getting a colormap out of the registry.
Sorry, something went wrong.
There was a problem hiding this comment.
I agree that some sort of deregister should be there at some point. Details would need to be discussed, e.g. if you can deregister built-in cmaps (I think that should not be possible these are reseverd names with fixed meaning and no public API should change them).
Anyway, we currently don't have a deregister function, so there is no hurry with adding that new feature in the initial version of the registry.
Sorry, something went wrong.
There was a problem hiding this comment.
We have unregister_cmap which went out in 3.4 via #15127
matplotlib/lib/matplotlib/cm.py
Lines 197 to 199 in 1761822
Sorry, something went wrong.
|
My sense is that long term we want to deprecate mpl.cm.register_colormap and friends? There will have to be some period in the middle where we support both so if that is the plan I'm not worried about the confusion factor. Getting a bit over my skis, having these in a nice object at least opens a path putting filter(...) methods on it that will return a new one with just the color maps of interest (e.g. ask only for perceptually uniform or diverging color maps...assuming we tag them with enough information) which would be super useful for a UI. |
Sorry, something went wrong.
|
I do not think we are going to get this done in the next few weeks, I propose bumping it to 3.6. |
Sorry, something went wrong.
|
I forget where we stand on deprecating the global colormap mutability and whether this PR is something that needs to get in for that? This PR mentions that we missed the intermediate state and extended the deprecation period: #19766, but I'm not clear if we ever added the intermediate state in 3.5 or not? i.e. is there an action item to add/change the way we get colormaps/warn about it? xref: #19609 for a discussion about mpl.cm.{cmap_name}. |
Sorry, something went wrong.
Overall goalThe registry is a more natural structure for managing colormaps. We have data and corresponding functions that are tightly related. IMHO it's a much nicer API for the user and easier to extend. And we get rid of some API quirks in the functions like get_cmap(.. lut=...) or the parameter order register_cmap(name, cmap) where name is optional. Target state
Transition pathWhile I would like to eventually remove the matplotlib.cm functions, I propose a prolonged transition path because they are quite fundamental functions also used in third party libraries:
I strongly advocate to introduce this PR in 3.5. If everything goes well, we don't need modifications (only extensions) to the registry, which means that downstream libraries would have 3.5 as a minimum dependency when they switch to the registry. On read-only colormapsThe registry colormap access already returns copies of the colormaps and by that implements the change to get_cmap, which is scheduled to take effect in 3.6 (#19766). He can be quicker here, because the new API does not have to maintain backward compatibility for the deprecation period. |
Sorry, something went wrong.
There was a problem hiding this comment.
I agree with @timhoffm's path forward. I think additional capabilities can be added later and doesn't preclude this from going in. I think the chosen location at the base level matplotlib.colormaps with a clear name is a good choice.
One other thought (that I don't think is a great idea because it would add another way to do something) is that you could override __getattr__ on this Registry and allow access to the built-ins that way so that
matplotlib.colormaps.viridis is the same as matplotlib.colormaps['viridis']. That may help with moving matplotlib.cm.viridis and friends over to this new location.
Sorry, something went wrong.
|
My objection is a little more fundamental; I'm not sure why we need a registry at all, so rather than hoisting it to the top level I would tend to discourage passing colormaps by string. |
Sorry, something went wrong.
|
The ship on not supporting colormaps-by-string sailed long ago and I think deprecating that in any meaningful way is out of the question. If we are going to support it, I would rather support it with a nice object which can be both more discover-able and inspect-able than the current state. |
Sorry, something went wrong.
Thanks for the proposal. It’s always good to add ideas to the solution space. However, I agree that this is not a great idea 😀. As you mentioned, this is just another way to achieve the same thing. And worse: it dilutes the mapping concept. The registry is a simple mapping: name -> Colormap, which is canonically associated with item access. The attributes would pollute the register namespace and introduce an unnecessary asymmetry between built in and third party color maps. I think that matplotlib.cm.viridis was an design flaw in the first place. I don't want to replicate this to the registry. OTOH the future of these cm functions is a separate discussion and possibly targeted at 3.6. |
Sorry, something went wrong.
Co-authored-by: Greg Lucas <greg.m.lucas@gmail.com>
|
|
||
| # workaround: we must defer colormaps import to after loading rcParams, because | ||
| # colormap creation depends on rcParams | ||
| from matplotlib.cm import _colormaps as colormaps |
There was a problem hiding this comment.
Could we handle this in the module level __getattr__ now that we have than in place?
Sorry, something went wrong.
|
Let's merge. Someone can always add an unregister later. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PR Summary
Following the thoughts in #16991 (comment), this defines a Mapping subclass ColorbarRegistry with the following properties:
This is a minimal version, that allows to get the registered colormap names. Closes #18490.
Still to be decided: Where should the instance colormaps instance live? In cm, in matplotlib? Should cm be integrated into colors anyway?
PR Checklist