Skip to content

Missing tests for norm=Normalize(...) #372

Closed

Description

@Sonja-Stockhaus

Currently, we don't have enough tests including the norm=Normalize(...) argument. It should work for matplotlib and also datshader (after #309), but I think we need some more tests and maybe also a bit more documentation.

Also, I noted that both seem to give the same result, not sure if that's a bug

from spatialdata.datasets import blobs
from matplotlib.colors import Normalize
blob=blobs()

blob.pl.render_points(color="instance_id", size=40, norm=Normalize(4, 7, clip=True)).pl.show()
blob.pl.render_points(color="instance_id", size=40, norm=Normalize(4, 7, clip=False)).pl.show()

Image
Image

Activity

  1. LucaMarconato commented on Jan 5, 2025

    @LucaMarconato
    Member

    Thanks for reporting. The instance_id column has values in [0, 1, ..., 9]. With clip=True one would expect the values [0, 1, 2, 3, 4] to have the same color, and the values [7, 8, 9] to have the same color. So it seems like that there is a bug.

    Did you notice this only for matplotlib or also for datashader?

  2. Sonja-Stockhaus commented on Jan 6, 2025

    @Sonja-Stockhaus
    CollaboratorAuthor

    When using datashader, both plots look identical as well.

  3. Sonja-Stockhaus commented on Jan 14, 2025

    @Sonja-Stockhaus
    CollaboratorAuthor

    After looking into this a bit more, I would say the behavior is not a bug: according to the matplotlib documentation (https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.Normalize.html), with clip=False, values outside of [vmin, vmax] are mapped to values outside [0, 1] (and not exactly to 0 or 1 as would happen with clip=True). Then, the respective colormap can "can mark these under and over values with specific colors".
    In the examples above, we use the default colormap viridis, which uses its minimal value for under values and its maximal value for over values (see below).

    Image

    So, if you would set over and under differently, the result looks like this for clip=False (the clip=True plot looks like above)

    from matplotlib import rcParams
    import matplotlib
    
    cmap = matplotlib.colormaps[rcParams["image.cmap"]]
    cmap.set_under("grey")
    cmap.set_over("grey")
    
    blob.pl.render_points(color="instance_id", size=40, norm=Normalize(4, 7, clip=False), cmap=cmap).pl.show()

    Image

  4. LucaMarconato commented on Jan 15, 2025

    @LucaMarconato
    Member

    You are right! Thanks for checking this 😊

  5. Sonja-Stockhaus commented on Feb 4, 2025

    @Sonja-Stockhaus
    CollaboratorAuthor

    Notes on the datashader behavior here: ds.tf.shade() has an argument span that we can use for passing [norm.vmin, norm.vmax]. Unfortunately, there is no way to pass the norm.clip value and datashader always behaves like clip=True (described here: https://datashader.org/getting_started/Pipeline.html#controlling-ranges-for-colormapping). This also means that doing a manual normalization before calling shade() won't lead to the correct result, since these values would be clipped to [0, 1] automatically.

  6. linked a pull request that will close this issueFix norm behavior, add tests #419on Feb 4, 2025
  7. Sonja-Stockhaus commented on Feb 10, 2025

    @Sonja-Stockhaus
    CollaboratorAuthor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingdocumentationImprovements or additions to documentation

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions