Skip to content

color name pass through norm #18139

Open
Open

Description

@story645

If users pass in a list of colors as data, they should be able to set a norm that passes the colors through as colors. So for example, if this was their current data:

colors = np.array([['yellowgreen', 'greenyellow', 'yellow'],
                   ['yellowgreen', 'yellow', 'greenyellow'],
                   ['yellow', 'yellowgreen', 'greenyellow']], dtype=object)

It'd be useful for applications like unsupervised machine learning labeling where the class id is arbitrary but the label color is important for identification if users could do something like:

plt.imshow(colors, norm=colorNamedNorm())

index

The current code isn't that bad, but requires remapping back and forth which mean's we're forcing people to do an artificial remapping of their data when the data->color mapping is the job of the norm.

cdata = np.empty(colors.shape)
for i, c in enumerate(np.unique(colors)):
    cdata[colors==c] = i

import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
plt.imshow(cdata, cmap=mcolors.ListedColormap(np.unique(colors)))

I think this should either be separate form NoNorm 'cause it's got an implicit colormap and also because otherwise NoNorm would have to try an do semantic inference on the data because oestensibly the colormap norm would also hold for RGB/HTML/Floating Points - basically any color matplotlib supports.

This functionality exists implicitly in other scientific visualization tools.

Activity

  1. jklymak commented on Jul 31, 2020

    @jklymak
    Member

    Not sure this is a Good first issue a) because there are API decisions and b) because the inevitable next step is some sort of colorbar, and I don't see how you will make a sensible one here.

    This functionality exists implicitly in other scientific visualization tools.

    Can you provide examples?

    Note that we already just pass color MxNx3/4 arrays through. So, is there actually a use case where users have an array of named colors? Knowing who creates data sets like this and why may inform the API choices...

  2. removed
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Jul 31, 2020
  3. story645 commented on Jul 31, 2020

    @story645
    MemberAuthor

    Knowing who creates data sets like this and why may inform the API choices...

    Honestly this issue was half motivated by a discussion with @tacaswell about encoding aesthetic information in data semantics so he might have thoughts.

    Can you provide examples?

    google has been unhelpful enough that I maybe shouldn't have claimed that :/ but @nicolaskruchten pointed out that plotly express implements this in a similar way to what I'm proposing (and actually IdentityNorm is a good name for this):

    image

    Nicolas also worked out the ggplot version:
    image
    image

  4. jklymak commented on Jul 31, 2020

    @jklymak
    Member

    scatter already takes a list of colors. I agree that makes sense.

  5. jklymak commented on Jul 31, 2020

    @jklymak
    Member

    i.e. we already have:

    import matplotlib.pyplot as plt
    x=y=c=['red', 'green', 'blue']
    plt.scatter(x,y,c=c)

    scatterCat

  6. story645 commented on Jul 31, 2020

    @story645
    MemberAuthor

    😬 I don't like that scatter does it implicitly, but yes we should do that for all scaler mappables and preferably at least under the hood with something like an IdentityNorm.

  7. jklymak commented on Jul 31, 2020

    @jklymak
    Member

    I guess I would go exactly the other way and say that if the user is giving us the colors we don't need to use a Normalize + colormap formalism at all, but rather we should just plot the colors we've been given. Thats what scatter already does.

    Adding named colors for imshow doesn't seem to hard because we already do that for MxNx3 colorspecs; I'd just loop through the matrix of color names, and make the MxNx3 array.

    The only reason to make a Norm explicit is if the same data set can get different norms. So floats can get a linear Norm or a logarithmic norm. Thats not the cased for an array of named colors or MxNx3 array of RGB colours.

  8. tacaswell commented on Jul 31, 2020

    @tacaswell
    Member

    This does not fit well with the current scalar mappable though because in every other case we have a cmap(norm(data)) -> RGBA where norm is data space -> [0, 1] and cmap is [0, 1] -> RBGA. This is fuses the two together so would require special casing no matter what.

    The other option to get explicit colors in is to pre-convert them to rbga (rather than strings).

    Somewhat annoyingly a 1D array of name colors works:

    In [485]: mcolors.to_rgba_array(np.asarray(['red', 'blue', 'green', 'C0']))
    Out[485]: 
    array([[1.        , 0.        , 0.        , 1.        ],
           [0.        , 0.        , 1.        , 1.        ],
           [0.        , 0.50196078, 0.        , 1.        ],
           [0.12156863, 0.46666667, 0.70588235, 1.        ]])

    but a 2D array of colors fails

    In [483]: mcolors.to_rgba_array(np.asarray([['red', 'blue'],['green', 'C0']]))
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    <ipython-input-483-7d841bdf3eb6> in <module>
    ----> 1 mcolors.to_rgba_array(np.asarray([['red', 'blue'],['green', 'C0']]))
    
    ~/.virtualenvs/bleeding/lib/python3.10/site-packages/matplotlib/colors.py in to_rgba_array(c, alpha)
    
    ~/.virtualenvs/bleeding/lib/python3.10/site-packages/matplotlib/colors.py in <listcomp>(.0)
    
    ~/.virtualenvs/bleeding/lib/python3.10/site-packages/matplotlib/colors.py in to_rgba(c, alpha)
        192     rgba = None
        193 if rgba is None:  # Suppress exception chaining of cache lookup failure.
    --> 194     rgba = _to_rgba_no_colorcycle(c, alpha)
        195     try:
        196         _colors_full_map.cache[c, alpha] = rgba
    
    ~/.virtualenvs/bleeding/lib/python3.10/site-packages/matplotlib/colors.py in _to_rgba_no_colorcycle(c, alpha)
        268     raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
        269 if len(c) not in [3, 4]:
    --> 270     raise ValueError("RGBA sequence should have length 3 or 4")
        271 if not all(isinstance(x, Number) for x in c):
        272     # Checks that don't work: `map(float, ...)`, `np.array(..., float)` and
        273     # `np.array(...).astype(float)` would all convert "0.5" to 0.5.
        274     raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
    
    ValueError: RGBA sequence should have length 3 or 4

    I think the good first issue here is fixing that!

  9. story645 commented on Jul 31, 2020

    @story645
    MemberAuthor

    but rather we should just plot the colors we've been given.

    But then we have to under the hood do type reference against matplotlib supported colors to guess that what they gave us is colors and not categoricals so I think it's safer to force them to be explicit the way plotly and ggplot are.

    l, but rather we should just plot the colors we've been given. Thats what scatter already does. Adding named colors for imshow doesn't seem to hard because we already do that for MxNx3 colorspecs; I'd just loop through the matrix of color names, and make the MxNx3 array.

    I think this code that we may want to reuse across at least two functions should be encapsulated as an IdentityNorm because at least in my understanding of Matplotlib, Norms are what translate from "data space" -> "something Matplotlib can use to make colors" so if it's data->color=color that's why I like the name IdentityNorm. I'm thinking of norms as analogous to maps in plotly and scales in ggplot & the discussion of a FuncNorm also indicated that Norms function like scales. So basically even if it's not doing the [0,1] that the other norms are doing, it's still in the same functional place in the matplotlib architecture/pipeline.

  10. jklymak commented on Jul 31, 2020

    @jklymak
    Member

    But then we have to under the hood do type reference against matplotlib supported colors to guess that what they gave us is colors and not categoricals

    We don't support categoricals for anything that we pass through a norm...

  11. story645 commented on Jul 31, 2020

    @story645
    MemberAuthor

    We don't support categoricals for anything that we pass through a norm...

    Yes but we really really should...and precisely because we don't and categoricals don't work cause no unit conversion (which I still think the easiest way to get unitized colors is to throw a convertor on NoNorm), we're going to have to do this implicit checking if this PR would go in. I think it's easier to force explicit and if there are enough complaints or enough uptake make it implicit rather than try to go the other way around.

  12. jklymak commented on Jul 31, 2020

    @jklymak
    Member

    Yes but we really really should

    I don't know that there is universal agreement on that: #7383

  13. story645 commented on Jul 31, 2020

    @story645
    MemberAuthor

    I don't know that there is universal agreement on that: #7383

    Reading through that thread, my takeaway is that it consensus is maybe/maybe not, really needs to be implemented for folks to make up their minds. And also that we mostly coalesced around the recommend implementation being something like running it through a Norm 'cause dataspace->something that can go to color space is what matplotlib uses the norm for.

  14. anntzer commented on Oct 6, 2021

    @anntzer
    Contributor

    A variant of the request here would be to support mappables that are direct indexes into a ListedColormap, together with NoNorm, i.e. something like

    scatter(range(3), range(3), c=range(3), norm=mpl.colors.NoNorm(), cmap="tab10")
    

    which would plot the points using the first three colors of tab10 (especially for categorical applications, it seems unlikely that your pipeline directly produces colors; rather it typically produces categories/indices).

    Right now the above simply maps the first point to the first color, the second to the last, and the next ones to the above color.

  15. github-actions commented on Aug 14, 2023

    @github-actions

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  16. added
    keepItems to be ignored by the “Stale” Github Action
    and removed
    status: inactiveMarked by the “Stale” Github Action
    on Aug 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions