proplot-dev / proplot

🎨 A succinct matplotlib wrapper for making beautiful, publication-quality graphics
https://proplot.readthedocs.io
MIT License
1.07k stars 96 forks source link

Update imports and func references to support up to latest matplotlib (3.8.3) #450

Open riley-brady opened 4 months ago

riley-brady commented 4 months ago
chudlerk commented 4 months ago

I get this error when I call pplt.figure() :(

AttributeError: 'Figure' object has no attribute '_cachedRenderer'

Matplotlib v3.8.3 Python 3.11.8

riley-brady commented 4 months ago

Hi Kyle @chudlerk! Thanks for pointing this out. I was just trying to get the package to import but clearly didn't do any testing. It's hard to capture the depth of dependency issues here until https://github.com/proplot-dev/proplot/pull/413 is merged.

Might need to defer to @lukelbd on this, since he has a better idea of what this _cachedRenderer does. It looks like it was dropped starting with 3.6.0. See https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.6.0.html#renderer-optional-for-get-tightbbox-and-get-window-extent.

They do have a Figure._get_renderer() that seems to have replaced the cached renderer.

There's two spots in proplot this needs to be addressed.

  1. Under figure.py, Figure._get_renderer(). That just copies the call from matplotlib's tight_layout, which seemingly was deprecated at 3.6. This method might be able to check matplotlib version and say <3.6 use that block, otherwise just run Figure._get_renderer() from matplotlib.Figure.

  2. Under figure.py, Figure._context_adjusting():

    def _context_adjusting(self, cache=True):
        """
        Prevent re-running auto layout steps due to draws triggered by figure
        resizes. Otherwise can get infinite loops.
        """
        kw = {'_is_adjusting': True}
        if not cache:
            kw['_cachedRenderer'] = None  # temporarily ignore it
        return context._state_context(self, **kw)

I'm less sure of what's going on here and how this needs to be modified for more recent versions.

riley-brady commented 4 months ago

@lukelbd , I gave it a little bit of a try today and can't really figure out what the renderer stuff is doing. I have it turned on so you can push commits directly to this if you'd like to try this weekend.

riley-brady commented 3 months ago

@chudlerk I've found success on my work stack just pinning matplotlib==3.5.3 and that will jive with all the most recent xarray/xclim/dask/metpy, etc. etc. If you do these small import fixes from this PR with that I think it's a good temporary solution.

chudlerk commented 3 months ago

Thanks Riley @riley-brady! I've got a working environment also by pinning matplotlib that way. At one point I think I had some issues with newer versions of python not working with the old matplotlib version. But everything is working fine with 3.9.

riley-brady commented 3 months ago

I was getting some weird errors with:

python==3.10.13
matplotlib==3.4.3
proplot==0.9.7
cartopy=0.22

Where cartopy was throwing an error related to packaging.version. It only seemed to resolve by bumping matplotlib to 3.5.3. Was able to keep python at 3.10.13 this way. Not sure how that matplotlib version impacted it, because the packaging version and python version was fixed.

reemagit commented 3 months ago

Hello, I had the same issue with cmap_d and locally modified the code in a similar way as shown above. It imports fine, but the I also got the _cachedRenderer error. So I modified the line in proplot'sfigure.py:

if self._cachedRenderer:
            renderer = self._cachedRenderer 

to

if hasattr(self,'_cachedRenderer') and self._cachedRenderer:
            renderer = self._cachedRenderer 

I was testing some example code:

fig,axs = pplt.subplots(nrows=1,ncols=1)
axs.scatter([0,2,3,4],[1,2,4,2])

but now I get a new error:

ValueError: 'Fire' is not a valid value for cmap; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'

Unfortunately I can't use matplotlib==3.5.3 since I have another package that requires matplotlib>=3.6.0.

Has anyone found a workaround for this?

riley-brady commented 3 months ago

@reemagit , in the meantime you can just use a different colormap from that list. It looks like Fire is the default and not being registered. Just pass in cmap='viridis' to scatter.

In the meantime:

  1. Check that your packaged source code has the cmaps folder with Fire.json in it. (My pathing is e.g. ~/miniforge3/envs/<conda_env_name>/lib/python3.10/site-packages/proplot). If there is a cmaps folder with Fire.json in it, that means that with the modern matplotlib version it's just not registering the custom ones correctly.
  2. If (1) is true, it probably has to do with my changes to proplot/colors.py. See the changes in this PR. If you haven't implemented those -- try them out. You just need to make sure that database = mcm._gen_cmap_registry() creates the proper colormap registry with Fire.json included.
reemagit commented 3 months ago

Thanks for the suggestions. With the PR changes and using the standard colormaps it seems to work.

I tried understanding why it wouldn't load the colormaps. Posting what I understood below in case it can be useful:

I checked and the 'Fire.json' file exists, and proplot is able to load it. However it doesn't get loaded in the global ColormapRegistry so it throws an error. I think this is because the line in matplotlib mpl._api.check_in_list(sorted(mpl.cm._colormaps) checks if Fire exists in mpl.cm._colormaps, but in the corrected code we add the colormap database to setattr(mcm, attr, database), with attr being either "_cmap_registry" or "cmap_d". So at the end of the proplot's _init_cmap_database function I changed it to

#setattr(mcm, attr, database) # Line that I removed
setattr(mcm, '_colormaps', database)
setattr(mpl, 'colormaps', mcm._colormaps)

In this way the Database is loaded in the global mpl.colormaps and 'mcm._colormaps' objects.

Even after doing this, it still wouldn't find the 'Fire' colormap. Then I saw that the matplotlib.colormaps contained the 'fire' colormap (lowercase). As I found out, the ColormapDatabase is not case-sensitive when requesting a colormap, so it works regardless. However, the function checking if 'Fire' exists uses

_api.check_in_list(sorted(_colormaps), name=name)

The sorted functions converts the keys of _colormaps to a list, and so it becomes case-sensitive. So the final solution was to just call the "fire" (lowercase) colormap, and it would work. So the final solution if one wants to use the proplot's colormaps is to change the lines above and always specify the lowercase name.

Not sure if my workaround introduced other issues, but it seems to be working so far. Hope this helps.

kastnerp commented 3 months ago

Works for me on Python 3.11.5 (main, Sep 11 2023, 08:31:25) [Clang 14.0.6 ] on darwin and matplotlib 3.8.3 matplotlib-inline 0.1.6!

riley-brady commented 3 months ago

Works for me on Python 3.11.5 (main, Sep 11 2023, 08:31:25) [Clang 14.0.6 ] on darwin and matplotlib 3.8.3 matplotlib-inline 0.1.6!

Is this with @reemagit's fixes? Have you tried plotting some stuff? PR head will only fix imports, but not plotting.

kastnerp commented 3 months ago

I installed from the commit c0bf6b6b8ec047e302eea78631942467575e75cb and plotting works without an issue on my end.

austin-hoover commented 2 months ago

matplotlib 3.8.4 proplot 1.0.4

I tried

import matplotlib.pyplot as plt
import numpy as np
import proplot as pplt

x = y = np.linspace(-1.0, 1.0, 50)
values = np.random.rand(x.size, y.size)

fig, ax = pplt.subplots()
ax.pcolormesh(x, y, values.T, cmap="fire")
plt.show()

and got the following error:

Traceback (most recent call last):
  File "/Users/46h/repo/proplot-test/test/test_plot_2d.py", line 9, in <module>
    fig, ax = pplt.subplots(ncols=2, nrows=2)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/repo/proplot-test/proplot/ui.py", line 232, in subplots
    axs = fig.add_subplots(*args, rc_kw=rc_kw, **kwsubs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/repo/proplot-test/proplot/figure.py", line 1407, in add_subplots
    return self._add_subplots(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/repo/proplot-test/proplot/figure.py", line 1170, in _add_subplots
    axs[idx] = self.add_subplot(ss, **kw)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/repo/proplot-test/proplot/figure.py", line 1393, in add_subplot
    return self._add_subplot(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/repo/proplot-test/proplot/figure.py", line 1063, in _add_subplot
    ax = super().add_subplot(ss, _subplot_spec=ss, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/figure.py", line 782, in add_subplot
    ax = projection_class(self, *args, **pkw)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/46h/repo/proplot-test/proplot/axes/cartesian.py", line 343, in __init__
    super().__init__(*args, **kwargs)
  File "/Users/46h/repo/proplot-test/proplot/axes/plot.py", line 1279, in __init__
    super().__init__(*args, **kwargs)
  File "/Users/46h/repo/proplot-test/proplot/axes/base.py", line 828, in __init__
    self._apply_auto_share()
  File "/Users/46h/repo/proplot-test/proplot/axes/base.py", line 1397, in _apply_auto_share
    child._sharey_setup(parent)
  File "/Users/46h/repo/proplot-test/proplot/axes/cartesian.py", line 640, in _sharey_setup
    self._sharey_limits(sharey)
  File "/Users/46h/repo/proplot-test/proplot/axes/cartesian.py", line 583, in _sharey_limits
    self.get_shared_y_axes().join(self, sharey)  # share limit/scale changes
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'GrouperView' object has no attribute 'join'. Did you mean: 'joined'?

I updated this line to self.sharey(sharey) and it worked. I don't have any issue with colormaps.

austin-hoover commented 2 months ago

Running

fig, ax = pplt.subplots()

in JupyterLab throws the cached renderer error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File [~/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/pyplot.py:197](http://localhost:8888/~/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/pyplot.py#line=196), in _draw_all_if_interactive()
    195 def _draw_all_if_interactive() -> None:
    196     if matplotlib.is_interactive():
--> 197         draw_all()

File [~/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/_pylab_helpers.py:132](http://localhost:8888/~/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/_pylab_helpers.py#line=131), in Gcf.draw_all(cls, force)
    130 for manager in cls.get_all_fig_managers():
    131     if force or manager.canvas.figure.stale:
--> 132         manager.canvas.draw_idle()

File [~/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/backend_bases.py:1893](http://localhost:8888/~/miniconda3/envs/proplot-test/lib/python3.11/site-packages/matplotlib/backend_bases.py#line=1892), in FigureCanvasBase.draw_idle(self, *args, **kwargs)
   1891 if not self._is_idle_drawing:
   1892     with self._idle_draw_cntx():
-> 1893         self.draw(*args, **kwargs)

File [~/repo/proplot-test/proplot/figure.py:463](http://localhost:8888/~/repo/proplot-test/proplot/figure.py#line=462), in _add_canvas_preprocessor.<locals>._canvas_preprocess(self, *args, **kwargs)
    461 ctx3 = rc.context(fig._render_context)  # draw with figure-specific setting
    462 with ctx1, ctx2, ctx3:
--> 463     fig.auto_layout()
    464     return func(self, *args, **kwargs)

File [~/repo/proplot-test/proplot/figure.py:1443](http://localhost:8888/~/repo/proplot-test/proplot/figure.py#line=1442), in Figure.auto_layout(self, renderer, aspect, tight, resize)
   1440 # *Impossible* to get notebook backend to work with auto resizing so we
   1441 # just do the tight layout adjustments and skip resizing.
   1442 gs = self.gridspec
-> 1443 renderer = self._get_renderer()
   1444 if aspect is None:
   1445     aspect = True

File [~/repo/proplot-test/proplot/figure.py:891](http://localhost:8888/~/repo/proplot-test/proplot/figure.py#line=890), in Figure._get_renderer(self)
    887 def _get_renderer(self):
    888     """
    889     Get a renderer at all costs. See matplotlib's tight_layout.py.
    890     """
--> 891     if self._cachedRenderer:
    892         renderer = self._cachedRenderer
    893     else:

AttributeError: 'Figure' object has no attribute '_cachedRenderer'

But running

fig, ax = pplt.subplots()
plt.show()

works just fine.

It seems to work if I delete the if statement above.

austin-hoover commented 2 months ago

There still seems to be a cmap registry issue but only for scatter plots.

x = np.random.uniform(size=(500, 2))

fig, ax = pplt.subplots()
ax.scatter(x[:, 0], x[:, 1])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[15], line 4
      1 x = np.random.uniform(size=(500, 2))
      3 fig, ax = pplt.subplots()
----> 4 ax.scatter(x[:, 0], x[:, 1])

File [~/repo/proplot-test/proplot/internals/inputs.py:296](http://localhost:8888/~/repo/proplot-test/proplot/internals/inputs.py#line=295), in _preprocess_or_redirect.<locals>._decorator.<locals>._preprocess_or_redirect(self, *args, **kwargs)
    293             ureg.setup_matplotlib(True)
    295 # Call main function
--> 296 return func(self, *args, **kwargs)

File [~/repo/proplot-test/proplot/axes/plot.py:3271](http://localhost:8888/~/repo/proplot-test/proplot/axes/plot.py#line=3270), in PlotAxes.scatter(self, *args, **kwargs)
   3267 """
   3268 %(plot.scatter)s
   3269 """
   3270 kwargs = _parse_vert(default_vert=True, **kwargs)
-> 3271 return self._apply_scatter(*args, **kwargs)

File [~/repo/proplot-test/proplot/axes/plot.py:3239](http://localhost:8888/~/repo/proplot-test/proplot/axes/plot.py#line=3238), in PlotAxes._apply_scatter(self, xs, ys, ss, cc, vert, **kwargs)
   3237 for _, n, x, y, s, c, kw in self._iter_arg_cols(xs, ys, ss, cc, **kw):
   3238     kw['s'], kw['c'] = s, c  # make _parse_cycle() detect these
-> 3239     kw = self._parse_cycle(n, cycle_manually=cycle_manually, **kw)
   3240     *eb, kw = self._add_error_bars(x, y, vert=vert, default_barstds=True, **kw)
   3241     *es, kw = self._add_error_shading(x, y, vert=vert, color_key='c', **kw)

File [~/repo/proplot-test/proplot/axes/plot.py:2346](http://localhost:8888/~/repo/proplot-test/proplot/axes/plot.py#line=2345), in PlotAxes._parse_cycle(self, ncycle, cycle, cycle_kw, cycle_manually, return_cycle, **kwargs)
   2344         props[prop] = key
   2345 if props:
-> 2346     dict_ = next(parser.prop_cycler)
   2347     for prop, key in props.items():
   2348         value = dict_[prop]

AttributeError: '_process_plot_var_args' object has no attribute 'prop_cycler'
fig, ax = pplt.subplots()
ax.scatter(x[:, 0], x[:, 1], c="black")
ValueError: 'fire' is not a valid value for cmap
fig, ax = pplt.subplots()
ax.scatter(x[:, 0], x[:, 1], c=np.linspace(0.0, 1.0, x.shape[0]), cmap="viridis")
ValueError: 'fire' is not a valid value for cmap

It works if you change the rc param:

pplt.rc["cmap.sequential"] = "viridis"

fig, ax = pplt.subplots()
ax.scatter(x[:, 0], x[:, 1], c=np.linspace(0.0, 1.0, x.shape[0]), cmap="fire")
Screenshot 2024-04-11 at 12 49 44 PM
riley-brady commented 2 months ago

@lukelbd are you able to resolve this into a minor patch release? I think we have most of what we need here to get it working. You know the ins and outs of proplot and matplotlib well enough to pull this together pretty quickly hopefully.

Migelo commented 1 month ago

Using both of your latest commits 0af21cffe23556b354940fe5c0e713a605c7ff3a and c0bf6b6b8ec047e302eea78631942467575e75cb

I get this error when running in a jupyter notebook.

import matplotlib
import proplot as pplt
import numpy as np

matplotlib.use("Agg")

state = np.random.RandomState(51423)
fig = pplt.figure(share=False, refwidth=2.3)

# Colorbars
ax = fig.subplot(121, title="Axes colorbars")
data = state.rand(10, 10)
m = ax.heatmap(data, cmap="dusk")
ax.colorbar(m, loc="r", cmap="viridis")
ax.colorbar(m, loc="t" , cmap="viridis") # title is automatically adjusted
ax.colorbar(m, loc="ll", label="colorbar label", cmap="viridis")  # inset colorbar demonstration

# Legends
ax = fig.subplot(122, title="Axes legends", titlepad="0em")
data = (state.rand(10, 5) - 0.5).cumsum(axis=0)
hs = ax.plot(data, lw=3, labels=list("abcde"))
ax.legend(loc="ll", label="legend label")  # automatically infer handles and labels
ax.legend(hs, loc="t", ncols=5, frame=False)  # automatically infer labels from handles
ax.legend(hs, list("jklmn"), loc="r", ncols=1, frame=False)  # manually override labels
fig.format(
    abc=True,
    xlabel="xlabel",
    ylabel="ylabel",
    suptitle="Colorbar and legend location demo",
)
fig.savefig("testproplot.png")

The error

ValueError                                Traceback (most recent call last)
Cell In[1], [line 14](vscode-notebook-cell:?execution_count=1&line=14)
     [12](vscode-notebook-cell:?execution_count=1&line=12) data = state.rand(10, 10)
     [13](vscode-notebook-cell:?execution_count=1&line=13) m = ax.heatmap(data, cmap="dusk")
---> [14](vscode-notebook-cell:?execution_count=1&line=14) ax.colorbar(m, loc="r", cmap="viridis")
     [15](vscode-notebook-cell:?execution_count=1&line=15) ax.colorbar(m, loc="t" , cmap="viridis") # title is automatically adjusted
     [16](vscode-notebook-cell:?execution_count=1&line=16) ax.colorbar(m, loc="ll", label="colorbar label", cmap="viridis")  # inset colorbar demonstration
....
ValueError: 'Fire' is not a valid value for cmap; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Grays', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_grey', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gist_yerg', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'grey', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'

Note that without

import matplotlib
matplotlib.use("Agg")

I get

ImportError: Cannot load backend 'TkAgg' which requires the 'tk' interactive framework, as 'headless' is currently running

Any tips would be much appreciated.

austin-hoover commented 1 month ago

@Migelo adding pplt.rc["cmap.sequential"] = "viridis" at the beginning of the script removes the colormap error for me. But then it throws the error:

File [~/miniconda3/envs/ment-flow/lib/python3.11/site-packages/proplot/axes/base.py:2614], in Axes.get_tightbbox(self, renderer, *args, **kwargs)
   2612 self._apply_title_above()
   2613 if self._colorbar_fill:
-> 2614     self._colorbar_fill.update_ticks(manual_only=True)  # only if needed
   2615 if self._inset_parent is not None and self._inset_zoom:
   2616     self.indicate_inset_zoom()

TypeError: Colorbar.update_ticks() got an unexpected keyword argument 'manual_only'

I don't have any issues with colorbars when using ax.pcolormesh, so that could be a solution for now?

riley-brady commented 1 month ago

Unfortunately I just don't know this package too well end-to-end nor matplotlib. So I did a very hacky fix and haven't been able to try to dig into this since.

Migelo commented 1 month ago

@austin-hoover thx for the fix. Same here.

Migelo commented 1 month ago

My example works with matplotlib==3.5.3 but breaks with matplotlib==3.6.0.

  File "/freya/ptmp/mpa/mihac/athenaruns/halo_profiles/testproplot.py", line 18, in <module>
    ax.colorbar(m, loc="r", cmap="viridis")
  File "/freya/ptmp/mpa/mihac/proplot/proplot/axes/base.py", line 2781, in colorbar
    return self._add_colorbar(mappable, values, loc=loc, align=align, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/freya/ptmp/mpa/mihac/proplot/proplot/internals/warnings.py", line 110, in _deprecate_kwargs_wrapper
    return func_orig(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/freya/ptmp/mpa/mihac/proplot/proplot/axes/base.py", line 1150, in _add_colorbar
    obj.minorlocator = minorlocator  # backwards compatibility
    ^^^^^^^^^^^^^^^^
  File "/u/mihac/.pyenv/versions/proplot/lib/python3.11/site-packages/matplotlib/colorbar.py", line 438, in minorlocator
    self._long_axis().set_minor_locator(loc)
  File "/u/mihac/.pyenv/versions/proplot/lib/python3.11/site-packages/matplotlib/axis.py", line 1816, in set_minor_locator
    _api.check_isinstance(mticker.Locator, locator=locator)
  File "/u/mihac/.pyenv/versions/proplot/lib/python3.11/site-packages/matplotlib/_api/__init__.py", line 93, in check_isinstance
    raise TypeError(
TypeError: 'locator' must be an instance of matplotlib.ticker.Locator, not a None
riley-brady commented 1 month ago

To clarify, I am running my edits here successfully with:

To name a few core packages. I spent a few mind-numbing days working on a standard analytics environment. I can't remember at this point who was driving up matplotlib versioning. Some packages are really eager with their lower-bound matplotlib version.

Migelo commented 1 month ago

I fixed my problem in https://github.com/riley-brady/proplot/pull/1

riley-brady commented 1 month ago

@Migelo and @austin-hoover , just merged your commit into here. I was just having problems again with proplot, even on this updated PR. I seemingly have to lock matplotlib at 3.5.3 for this to work at all. Which is problematic since 3.5.3 doesn't work with python 3.11.

Migelo commented 1 month ago

Great thx! What exactly is giving you issues, I'm happy to look.

riley-brady commented 1 month ago

@Migelo , I'm getting the dreaded colormap registry issue.

import proplot
import xarray as xr
data = xr.DataArray([[1,2,3],[1,2,3]])
data.plot()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 1
----> 1 data.plot()

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/xarray/plot/accessor.py:48, in DataArrayPlotAccessor.__call__(self, **kwargs)
     46 @functools.wraps(dataarray_plot.plot, assigned=("__doc__", "__annotations__"))
     47 def __call__(self, **kwargs) -> Any:
---> 48     return dataarray_plot.plot(self._da, **kwargs)

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/xarray/plot/dataarray_plot.py:309, in plot(darray, row, col, col_wrap, ax, hue, subplot_kws, **kwargs)
    305     plotfunc = hist
    307 kwargs["ax"] = ax
--> 309 return plotfunc(darray, **kwargs)

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/xarray/plot/dataarray_plot.py:1627, in _plot2d.<locals>.newplotfunc(***failed resolving arguments***)
   1625     if add_labels and "label" not in cbar_kwargs:
   1626         cbar_kwargs["label"] = label_from_attrs(darray)
-> 1627     cbar = _add_colorbar(primitive, ax, cbar_ax, cbar_kwargs, cmap_params)
   1628 elif cbar_ax is not None or cbar_kwargs:
   1629     # inform the user about keywords which aren't used
   1630     raise ValueError(
   1631         "cbar_ax and cbar_kwargs can't be used with add_colorbar=False."
   1632     )

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/xarray/plot/utils.py:724, in _add_colorbar(primitive, ax, cbar_ax, cbar_kwargs, cmap_params)
    721     cbar_kwargs.pop("extend")
    723 fig = ax.get_figure()
--> 724 cbar = fig.colorbar(primitive, **cbar_kwargs)
    726 return cbar

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/figure.py:1280, in FigureBase.colorbar(self, mappable, cax, ax, use_gridspec, **kwargs)
   1276 NON_COLORBAR_KEYS = ['fraction', 'pad', 'shrink', 'aspect', 'anchor',
   1277                      'panchor']
   1278 cb_kw = {k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS}
-> 1280 cb = cbar.Colorbar(cax, mappable, **cb_kw)
   1282 if not userax:
   1283     self.sca(current_ax)

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/_api/deprecation.py:384, in delete_parameter.<locals>.wrapper(*inner_args, **inner_kwargs)
    379 @functools.wraps(func)
    380 def wrapper(*inner_args, **inner_kwargs):
    381     if len(inner_args) <= name_idx and name not in inner_kwargs:
    382         # Early return in the simple, non-deprecated case (much faster than
    383         # calling bind()).
--> 384         return func(*inner_args, **inner_kwargs)
    385     arguments = signature.bind(*inner_args, **inner_kwargs).arguments
    386     if is_varargs and arguments.get(name):

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/colorbar.py:384, in Colorbar.__init__(self, ax, mappable, cmap, norm, alpha, values, boundaries, orientation, ticklocation, extend, spacing, ticks, format, drawedges, filled, extendfrac, extendrect, label, location)
    381     spine.set_visible(False)
    382 self.outline = self.ax.spines['outline'] = _ColorbarSpine(self.ax)
--> 384 self.dividers = collections.LineCollection(
    385     [],
    386     colors=[mpl.rcParams['axes.edgecolor']],
    387     linewidths=[0.5 * mpl.rcParams['axes.linewidth']],
    388     clip_on=False)
    389 self.ax.add_collection(self.dividers)
    391 self._locator = None

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/collections.py:1409, in LineCollection.__init__(self, segments, zorder, **kwargs)
   1407 # Unfortunately, mplot3d needs this explicit setting of 'facecolors'.
   1408 kwargs.setdefault('facecolors', 'none')
-> 1409 super().__init__(
   1410     zorder=zorder,
   1411     **kwargs)
   1412 self.set_segments(segments)

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/_api/deprecation.py:454, in make_keyword_only.<locals>.wrapper(*args, **kwargs)
    448 if len(args) > name_idx:
    449     warn_deprecated(
    450         since, message="Passing the %(name)s %(obj_type)s "
    451         "positionally is deprecated since Matplotlib %(since)s; the "
    452         "parameter will become keyword-only %(removal)s.",
    453         name=name, obj_type=f"parameter of {func.__name__}()")
--> 454 return func(*args, **kwargs)

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/collections.py:157, in Collection.__init__(self, edgecolors, facecolors, linewidths, linestyles, capstyle, joinstyle, antialiaseds, offsets, offset_transform, norm, cmap, pickradius, hatch, urls, zorder, **kwargs)
     97 """
     98 Parameters
     99 ----------
   (...)
    154     :doc:`/gallery/misc/zorder_demo` for all defaults and examples.
    155 """
    156 artist.Artist.__init__(self)
--> 157 cm.ScalarMappable.__init__(self, norm, cmap)
    158 # list of un-scaled dash patterns
    159 # this is needed scaling the dash pattern by linewidth
    160 self._us_linestyles = [(0, None)]

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/cm.py:400, in ScalarMappable.__init__(self, norm, cmap)
    398 self.set_norm(norm)  # The Normalize instance of this ScalarMappable.
    399 self.cmap = None  # So that the setter knows we're initializing.
--> 400 self.set_cmap(cmap)  # The Colormap instance of this ScalarMappable.
    401 #: The last colorbar associated with this ScalarMappable. May be None.
    402 self.colorbar = None

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/cm.py:585, in ScalarMappable.set_cmap(self, cmap)
    576 """
    577 Set the colormap for luminance data.
    578
   (...)
    581 cmap : `.Colormap` or str or None
    582 """
    583 in_init = self.cmap is None
--> 585 self.cmap = _ensure_cmap(cmap)
    586 if not in_init:
    587     self.changed()

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/cm.py:723, in _ensure_cmap(cmap)
    720 cmap_name = cmap if cmap is not None else mpl.rcParams["image.cmap"]
    721 # use check_in_list to ensure type stability of the exception raised by
    722 # the internal usage of this (ValueError vs KeyError)
--> 723 _api.check_in_list(sorted(_colormaps), cmap=cmap_name)
    724 return mpl.colormaps[cmap_name]

File ~/miniforge3/envs/analysis_py311/lib/python3.11/site-packages/matplotlib/_api/__init__.py:131, in check_in_list(_values, _print_supported_values, **kwargs)
    129 if _print_supported_values:
    130     msg += f"; supported values are {', '.join(map(repr, values))}"
--> 131 raise ValueError(msg)

ValueError: 'Fire' is not a valid value for cmap; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'
bnjmnr commented 4 weeks ago

Hi everyone, Thanks a lot for tackling the update on proplot to make it functional again. I really love proplot, it's an awesome package that really helped me through my PhD (1000s of kudos to @lukelbd) and it's of course when you can't use it anymore that you realize how much you miss it. And whenever I talk about it to friends, they look at the documentation and systematically say some thing along the lines of "I've been looking for something like that for ages, that would change my life if I could use it". So I wanted to help a bit with the maintenance. I'm pretty much stuck at the same place as you all are, all the corrections that @riley-brady, @austin-hoover and @Migelo have implemented in this pull request make proplot work for me (see packages versions below), except that I get that colormap error if I don't specify pplt.rc["cmap.sequential"] = "viridis". Funny enough, it still plots the figure I want with the "Fire" colormap... I tried to dig further and decorticate the colors.py file with no success. I just can't figure out where proplot specify the list of new cmap to add to the ColormapDatabase. But anyway, I don't think that's the issue. In a jupyter notebook, using the following code, I get no error:

import xarray as xr
import proplot as pplt
ds = xr.tutorial.load_dataset('air_temperature')
f, axs = pplt.subplots()
axs.pcolormesh(ds.air.isel(time=100),cmap='Fire')

But if I instead replace the last line by:

ds.air.isel(time=100).plot(ax =axs,cmap='Fire')

I do get that "Fire" colormap error (while still having the correct figure). Note that if I specify another colormap such as viridis in the plot call, I still get the fire error!

From there, I am tempted to suggest the following scenario: matplotlib does not know of the updated list of colormaps and doesn't care, yet uses the rcParam for the colormap. It does some test to check the cmap name (hence the error), then proplot takes over and changes the colormap eventually. What baffles me is why would a pplt.rc parameter be used by matplotlib even if we do not want that default parameter to be used. And why it wasn't throwing an error earlier, if that's the case. Does proplot overwrite the matplotlib rc parameters? If so, when and where? Can we then also specify a change in the matplotlib colormap database, to include the proplot and seaborn colormaps from the beginning? I'll try to dig in that direction.

If you have any clue on whether this is a good path to follow or not, I'm happy to hear! Once again, thanks for the hard work!

austin-hoover commented 4 weeks ago

On my fork, the issue comes when adding a colorbar and not specifying pplt.rc["cmap.sequential"] = "viridis". Without colorbar, everything works. Both of your examples work when I add the colorbar rcparam. I'm assuming xarray is calling ax.colorbar.

No colorbar

import numpy as np
import proplot as pplt
pplt.rc["grid"] = False

data = np.arange(100).reshape(10, 10)

fig, ax = pplt.subplots()
m = ax.pcolormesh(data, discrete=False);
Screenshot 2024-06-09 at 5 32 42 PM

Colorbar, no rcparam

import numpy as np
import proplot as pplt
pplt.rc["grid"] = False

data = np.arange(100).reshape(10, 10)

fig, ax = pplt.subplots()
m = ax.pcolormesh(data, discrete=False)
ax.colorbar(m);
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[1], line 9
      7 fig, ax = pplt.subplots()
      8 m = ax.pcolormesh(data, discrete=False)
----> 9 ax.colorbar(m);

File [~/repo/proplot-fork/proplot/axes/base.py:2786](http://localhost:8888/lab/workspaces/~/repo/proplot-fork/proplot/axes/base.py#line=2785), in Axes.colorbar(self, mappable, values, loc, location, **kwargs)
   2784     self._register_guide('colorbar', (mappable, values), (loc, align), **kwargs)
   2785 else:
-> 2786     return self._add_colorbar(mappable, values, loc=loc, align=align, **kwargs)

File [~/repo/proplot-fork/proplot/internals/warnings.py:110](http://localhost:8888/lab/workspaces/~/repo/proplot-fork/proplot/internals/warnings.py#line=109), in _rename_kwargs.<locals>._decorator.<locals>._deprecate_kwargs_wrapper(*args, **kwargs)
    105         key_new = key_new.format(value)
    106     _warn_proplot(
    107         f'Keyword {key_old!r} was deprecated in version {version} and may '
    108         f'be removed in {_next_release()}. Please use {key_new!r} instead.'
    109     )
--> 110 return func_orig(*args, **kwargs)

File [~/repo/proplot-fork/proplot/axes/base.py:1151](http://localhost:8888/lab/workspaces/~/repo/proplot-fork/proplot/axes/base.py#line=1150), in Axes._add_colorbar(self, mappable, values, loc, align, space, pad, width, length, shrink, label, title, reverse, rotation, grid, edges, drawedges, extend, extendsize, extendfrac, ticks, locator, locator_kw, format, formatter, ticklabels, formatter_kw, minorticks, minorlocator, minorlocator_kw, tickminor, ticklen, ticklenratio, tickdir, tickdirection, tickwidth, tickwidthratio, ticklabelsize, ticklabelweight, ticklabelcolor, labelloc, labellocation, labelsize, labelweight, labelcolor, c, color, lw, linewidth, edgefix, rasterized, **kwargs)
   1146         minorlocator = AutoLocator()
   1148 # Create colorbar and update ticks and axis direction
   1149 # NOTE: This also adds the guides._update_ticks() monkey patch that triggers
   1150 # updates to DiscreteLocator when parent axes is drawn.
-> 1151 obj = cax._colorbar_fill = cax.figure.colorbar(
   1152     mappable, cax=cax, ticks=locator, format=formatter,
   1153     drawedges=grid, extendfrac=extendfrac, **kwargs
   1154 )
   1155 obj.minorlocator = minorlocator  # backwards compatibility
   1156 obj.update_ticks = guides._update_ticks.__get__(obj)  # backwards compatible

File [~/repo/proplot-fork/proplot/figure.py:1658](http://localhost:8888/lab/workspaces/~/repo/proplot-fork/proplot/figure.py#line=1657), in Figure.colorbar(self, mappable, values, loc, location, row, col, rows, cols, span, space, pad, width, **kwargs)
   1656 if cax is not None:
   1657     with context._state_context(cax, _internal_call=True):  # do not wrap pcolor
-> 1658         cb = super().colorbar(mappable, cax=cax, **kwargs)
   1659 # Axes panel colorbar
   1660 elif ax is not None:

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/figure.py:1310](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/figure.py#line=1309), in FigureBase.colorbar(self, mappable, cax, ax, use_gridspec, **kwargs)
   1306     cax.grid(visible=False, which='both', axis='both')
   1308 NON_COLORBAR_KEYS = [  # remove kws that cannot be passed to Colorbar
   1309     'fraction', 'pad', 'shrink', 'aspect', 'anchor', 'panchor']
-> 1310 cb = cbar.Colorbar(cax, mappable, **{
   1311     k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS})
   1312 cax.figure.stale = True
   1313 return cb

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/colorbar.py:377](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/colorbar.py#line=376), in Colorbar.__init__(self, ax, mappable, cmap, norm, alpha, values, boundaries, orientation, ticklocation, extend, spacing, ticks, format, drawedges, extendfrac, extendrect, label, location)
    374     spine.set_visible(False)
    375 self.outline = self.ax.spines['outline'] = _ColorbarSpine(self.ax)
--> 377 self.dividers = collections.LineCollection(
    378     [],
    379     colors=[mpl.rcParams['axes.edgecolor']],
    380     linewidths=[0.5 * mpl.rcParams['axes.linewidth']],
    381     clip_on=False)
    382 self.ax.add_collection(self.dividers)
    384 self._locator = None

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/collections.py:1438](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/collections.py#line=1437), in LineCollection.__init__(self, segments, zorder, **kwargs)
   1436 # Unfortunately, mplot3d needs this explicit setting of 'facecolors'.
   1437 kwargs.setdefault('facecolors', 'none')
-> 1438 super().__init__(
   1439     zorder=zorder,
   1440     **kwargs)
   1441 self.set_segments(segments)

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/collections.py:156](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/collections.py#line=155), in Collection.__init__(self, edgecolors, facecolors, linewidths, linestyles, capstyle, joinstyle, antialiaseds, offsets, offset_transform, norm, cmap, pickradius, hatch, urls, zorder, **kwargs)
     96 """
     97 Parameters
     98 ----------
   (...)
    153     :doc:`[/gallery/misc/zorder_demo](http://localhost:8888/gallery/misc/zorder_demo)` for all defaults and examples.
    154 """
    155 artist.Artist.__init__(self)
--> 156 cm.ScalarMappable.__init__(self, norm, cmap)
    157 # list of un-scaled dash patterns
    158 # this is needed scaling the dash pattern by linewidth
    159 self._us_linestyles = [(0, None)]

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/cm.py:416](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/cm.py#line=415), in ScalarMappable.__init__(self, norm, cmap)
    414 self.set_norm(norm)  # The Normalize instance of this ScalarMappable.
    415 self.cmap = None  # So that the setter knows we're initializing.
--> 416 self.set_cmap(cmap)  # The Colormap instance of this ScalarMappable.
    417 #: The last colorbar associated with this ScalarMappable. May be None.
    418 self.colorbar = None

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/cm.py:605](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/cm.py#line=604), in ScalarMappable.set_cmap(self, cmap)
    596 """
    597 Set the colormap for luminance data.
    598 
   (...)
    601 cmap : `.Colormap` or str or None
    602 """
    603 in_init = self.cmap is None
--> 605 self.cmap = _ensure_cmap(cmap)
    606 if not in_init:
    607     self.changed()

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/cm.py:744](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/cm.py#line=743), in _ensure_cmap(cmap)
    741 # use check_in_list to ensure type stability of the exception raised by
    742 # the internal usage of this (ValueError vs KeyError)
    743 if cmap_name not in _colormaps:
--> 744     _api.check_in_list(sorted(_colormaps), cmap=cmap_name)
    745 return mpl.colormaps[cmap_name]

File [~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/_api/__init__.py:129](http://localhost:8888/lab/workspaces/~/miniconda3/envs/proplot-fork/lib/python3.11/site-packages/matplotlib/_api/__init__.py#line=128), in check_in_list(values, _print_supported_values, **kwargs)
    127 if _print_supported_values:
    128     msg += f"; supported values are {', '.join(map(repr, values))}"
--> 129 raise ValueError(msg)

ValueError: 'Fire' is not a valid value for cmap; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Grays', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_grey', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gist_yerg', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'grey', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'
Screenshot 2024-06-09 at 5 35 03 PM

Colorbar, rcparam

import numpy as np
import proplot as pplt
pplt.rc["grid"] = False
pplt.rc["cmap.sequential"] = "viridis"

data = np.arange(100).reshape(10, 10)

fig, ax = pplt.subplots()
m = ax.pcolormesh(data, discrete=False, cmap="fire")
ax.colorbar(m);
Screenshot 2024-06-09 at 5 36 53 PM
austin-hoover commented 4 weeks ago

Idk I'm confused. The colormaps are available from matplotlib.cm upon import, without changing rc.

import matplotlib.cm as cm
import matplotlib.pyplot as plt
import proplot as pplt

cm.get_cmap("fire")

However plt.get_cmap("fire") throws an error. I guess the colorbar function passes the default cmap to matplotlib.cm._ensure_cmap, which throws an error if outside default cmaps. Then later proplot is providing the custom cmap to the colorbar function.

@lukelbd can we just change the default cmap to "viridis" and merge these changes? That seems to fix remaining errors for now.

bnjmnr commented 3 weeks ago

There also seems to have been an important change in matplotlib colormap API in 3.6 or 3.7, with a lot of deprecations, including for get_cmap which will be removed in mpl 3.9: https://github.com/matplotlib/matplotlib/issues/20853 This wouldn't help for sure... I have tried to modify a bit the colors.py accordingly but without success yet. We might need to rethink the proplot colormap API to match those changes, as the matplotlib.cm functions will eventually be removed. But that would require some in-depth changes, and a proper understanding of what is going on in there. Which I do not have. Making sure we have backward compatibility will also be a nightmare.

EDIT: in colors.py, in _init_cmap_database(), adding the following lines helps:

    if _version_mpl >= '3.6':
        if mpl.colormaps.register is not _register_cmap:
            mpl.colormaps.register = _register_cmap
        if mpl.colormaps.get_cmap is not _get_cmap:
            mpl.colormaps.get_cmap = _get_cmap
    else:        
        if mcm.get_cmap is not _get_cmap:
               [...]

Then I can execute the following cell and get the proper colormap.

import xarray as xr
import matplotlib as mpl
import proplot as pplt
mpl.colormaps.get_cmap()

Same as you, except using the newest mpl colormaps API. Still get the colorbar issue, though.

bnjmnr commented 3 weeks ago

Just for info, I tried to follow the chain of errors thrown by the 'fire' cmap issue. I think one of the issues comes from the matplotlib.cm.py file, l. 238 (in latest version):

# public access to the colormaps should be via `matplotlib.colormaps`. For now,
# we still create the registry here, but that should stay an implementation
# detail.
_colormaps = ColormapRegistry(_gen_cmap_registry())
globals().update(_colormaps)

This last line declare the default list of cmap stored in _cm_listed.py as the recognized list of cmap. From what I can understand, it basically calls this everytime the _ensure_cmap method is called, which is the case when creating a cbar without a mappable. So it resets the list of _colormaps even when we changed it in proplot. I don't understand why the matplotlib developers opted for this method... Unless they change this, I don't think we can circumvent this. So changing the default cmap to "viridis" as suggested by @austin-hoover seems the best (and only) way forward at the moment.

austin-hoover commented 3 weeks ago

Indeed there is import error for matplotlib==3.9.0 related to this issue.

riley-brady commented 1 week ago

I'm not sure what more to do here, honestly. I haven't had the free time to dig deeply into this. We're currently locking a lot of packages at low versions at work to make this work, but at some point that won't be feasible.

Please anyone tag me with any branches off this PR with edits/any additional notes and edits I need to make to get over the hump with this PR. I'll see if I can try to get this merged from there.

austin-hoover commented 1 week ago

I think this PR should be merged. I have proplot working with python 3.11.9 and

matplotlib==3.8.4 matplotlib-inline==0.1.7 numpy==2.0.0 xarray==2024.6.0

SherFeyn commented 1 week ago

@lukelbd Hi Luke, we are really anticipating the new version of ProPlot, could you please spare some time to merge this PR?

riley-brady commented 1 week ago

I just updated the default cmap to Viridis I believe, and messaged Luke privately to see if I could get his eyes on this.

Mickychen00 commented 2 days ago

I just updated the default cmap to Viridis I believe, and messaged Luke privately to see if I could get his eyes on this.

Hi, thank you very much for the pull request. Unfortunately, I don't think that in the short term, the main developer will want to continue to refine proplot.