home / github / issue_comments

Menu
  • GraphQL API
  • Search all tables

issue_comments: 1412027546

This data as json

html_url issue_url id node_id user created_at updated_at author_association body reactions performed_via_github_app issue
https://github.com/pydata/xarray/issues/7014#issuecomment-1412027546 https://api.github.com/repos/pydata/xarray/issues/7014 1412027546 IC_kwDOAMm_X85UKdSa 13662783 2023-02-01T13:06:09Z 2023-02-01T13:06:45Z CONTRIBUTOR

Debugging this, @headtr1ck points correctly to _determine_cmap_params:

python if levels is not None or isinstance(norm, mpl.colors.BoundaryNorm): cmap, newnorm = _build_discrete_cmap(cmap, levels, extend, filled) norm = newnorm if norm is None else norm

The problem lies in the second line. In _build_discrete_cmap, a new cmap is returned with a different number of levels. However, if the original norm is not None, you end up with a mismatch, as the old norm expects the old cmap.

What then happens, is that the norm calls into the cmap. Calling into cmap doesn't do any checks whether the value is larger than N, it just takes the highest available value. The examples in #4061 show this quite clearly, but to illustrate:

```python import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np

data = np.arange(100).reshape((10, 10)) cmap = mpl.cm.get_cmap("viridis") print(cmap.N) # 256 boundaries = [0, 25, 50, 75, 100] norm = mpl.colors.BoundaryNorm(boundaries, cmap.N) fig, ax = plt.subplots() ax.imshow(data, norm=norm, cmap=cmap)

%%

colors = [cmap(i/255) for i in np.linspace(0, cmap.N, len(boundaries) - 1)] new_cmap, new_norm = mpl.colors.from_levels_and_colors(boundaries, colors) print(new_cmap.N) # 4 fig, ax = plt.subplots() ax.imshow(data, norm=new_norm, cmap=new_cmap)

%%

Mismatched

fig, ax = plt.subplots() ax.imshow(data, norm=norm, cmap=new_cmap)

%%

```

This is avoided here by removing the conditional in the second line, or just making sure both cmap and norm are replaced by their new values:

python if levels is not None or isinstance(norm, mpl.colors.BoundaryNorm): cmap, norm = _build_discrete_cmap(cmap, levels, extend, filled)

Then, the cmap and norm remain in sync.

However, when running the tests in test_plot.py, this gives a single questionable(?) failure:

```python def test_norm_sets_vmin_vmax(self) -> None: vmin = self.data.min() vmax = self.data.max()

    for norm, extend, levels in zip(
        [
            mpl.colors.Normalize(),
            mpl.colors.Normalize(),
            mpl.colors.Normalize(vmin + 0.1, vmax - 0.1),
            mpl.colors.Normalize(None, vmax - 0.1),
            mpl.colors.Normalize(vmin + 0.1, None),
        ],
        ["neither", "neither", "both", "max", "min"],
        [7, None, None, None, None],
    ):

        test_min = vmin if norm.vmin is None else norm.vmin
        test_max = vmax if norm.vmax is None else norm.vmax

        cmap_params = _determine_cmap_params(self.data, norm=norm, levels=levels)
        assert cmap_params["vmin"] is None
        assert cmap_params["vmax"] is None
        assert cmap_params["norm"].vmin == test_min
        assert cmap_params["norm"].vmax == test_max
        assert cmap_params["extend"] == extend
      assert cmap_params["norm"] == norm

E assert <matplotlib.colors.BoundaryNorm object at 0x000001C7A4CB07C0> == <matplotlib.colors.Normalize object at 0x000001C7A50C4760> ```

I don't understand why the conditional is there. At first sight, it doesn't make a lot of sense to create a new norm and cmap, but then take the original norm?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
  1368027148
Powered by Datasette · Queries took 0.468ms · About: xarray-datasette