proplot-dev / proplot

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

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

Closed riley-brady closed 3 months ago

riley-brady commented 8 months ago
chudlerk commented 8 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 8 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 8 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 8 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 8 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 8 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 8 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 8 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 8 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 8 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 8 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 8 months ago

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

austin-hoover commented 7 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 7 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 7 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 6 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 6 months 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 6 months 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 6 months 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 6 months ago

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

Migelo commented 6 months 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 6 months 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 6 months ago

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

riley-brady commented 5 months 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 5 months ago

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

riley-brady commented 5 months 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 5 months 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 5 months 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 5 months 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 5 months 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 5 months 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 5 months ago

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

riley-brady commented 5 months 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 5 months 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 5 months 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 5 months 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 4 months 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.

cvanelteren commented 4 months ago

Perhaps we should combine our efforts https://github.com/cvanelteren/proplot/tree/mpl3.8.3

MengqingWangCUG commented 4 months ago

There are issues when using cartopy==0.23 and matplotlib==3.9 for geographic plotting. I attempted to fix them, and it was useful for me. However, there are still issues when plotting coastlines that I am currently unable to fix. my way is add _gridliners to class _CartopyAxes self._gridliners = [a for a in self.artists if isinstance(a, Gridliner)] and add self.axis_name='x'/‘y’ for _LonAxis and _LatAxis

output

riley-brady commented 4 months ago

Perhaps we should combine our efforts https://github.com/cvanelteren/proplot/tree/mpl3.8.3

@cvanelteren Have you made any headway on this? Anything separate from what I've incorporated here? Feel free to target this branch and tag me and I can merge into it.

Work has been insane for me lately. Lots of downsizing on my team so haven't been able to think about this at all. We've either version-locked everything to out-of-date environments to use proplot or just dropped it from our newer environments.

I have connected with @lukelbd. He's similarly slammed with his postdoc. He mentioned he's aware of this and hoping to get a v0.10 release out this summer with the MPL compatibility changes. I let him know how much work/thought people are putting into it here, so hoping he can draw from this thread or advise on it.

Again, I'm happy to merge anything into this branch that people find works so folks can do an install targeted at this branch to get higher MPL versions to work before Luke can do a release.

cvanelteren commented 4 months ago

I have made some headway and implemented some unittests of which this repo is in high need of. Unfortunately with the release of 3.9 there are breaking changes that overhauls the colormaps heavily. Haven't looked into these changes yet.

The unittests are key to ensure everything is working. There are plenty of examples in the proplot docs that we can leverage for this end. I think this step is crucial to prevent monkey patching it to work with future versions of mpl and ensure that the role of proplot is still warranted other than a mere mpl style that we could apply. Ofc this requires partly a vision from the original author rather than a mutiny ;-).

riley-brady commented 4 months ago

Yes definitely a big need for testing. @lukelbd said this is one of his main priorities. He had been working on that here: https://github.com/proplot-dev/proplot/pull/413.

It's tough in a package like this. Unit testing can only go so far. But you sort of need snapshot testing since you're ensuring that images align as expected. A healthy mix of both would be good.

Hopefully he has some breathing room from academia soon to do a focused effort here. Ideally a few of us can help contribute to get testing up to par. I think we need to set up a CI/CD with nightly testing against upstream package versions + have a suit of tests going along with that that just test against failures due to MPL/cartopy changes in particular.

Consolidating my ramble:

Outside of that it would be nice to release this package to a community org with a more broad set of merge permissions to help when Luke is too busy to focus on this for periods of time.

riley-brady commented 4 months ago

What's the best route here @cvanelteren? Do you want to just cherry pick anything I have here that you don't have and open a PR here on main targeting your branch? You can link to this thread here and we can continue discussion there. We bought a house and are getting married soon on top of the job craziness, so it's hard for me to drive the ship :) But once we get buy-in from Luke, I should hopefully be able to help a bit with testing stuff.

cvanelteren commented 4 months ago

I think the best way forward would be to finish small meaningful tasks. In the long run it would be good to get a stable unitsuite going. I am aware of the branch you listed, but am more concerned with the lack of communication on @lukelbd's side. As it currently stands this package is loosing track of the latest changes and makes it harder for people to just install and run it. I think my branch (and perhaps this one too) monkey patches too much in. I would say let's focus on these unittests first so that we can after it, at least have a quicker turn around on what broke with the latest mpl patches. This does require however, @lukelbd to become atleast communicative on these PRs -- after all we cannot proceed to merge and make this package up-to-date without his blessing (and merge powers).

Have a look, if time permits, at the unittests I provided. MPL provides a nice way to do it out of the box.

riley-brady commented 4 months ago

Agreed on all of that.

  1. Can you open your branch here as an actual PR so it is easier for me to directly review the code?
  2. I'll see if I can get merge permissions from @lukelbd with the explicit purpose of just setting up unit testing. I don't know the package well enough to know about bigger changes.

I agree that we should avoid this conditional monkey patching. We could have a coordinated effort to just get a unit test suite set up with merge permissions. Then can use that to diagnose what's going on and see if we can get focused attention from Luke.

If you open up a targeted branch I should be able to do some light reviewing soon. I am gone all next week for my wedding. Once I'm back I should have more time to review/help with unit testing getting set up.

cvanelteren commented 4 months ago

I can see if I can make a dent in the unittests -- now tracking this actively on https://github.com/proplot-dev/proplot/compare/master...cvanelteren:proplot:unittest

cvanelteren commented 4 months ago

@riley-brady would be lovely if you can arrange this. Again no mutiny is intended -- I really like the place that this package has and would hate to see it go down ;-).

cvanelteren commented 4 months ago

I have merged the unittests with mine and added some. I think this is a pretty good spot to move forward; will some thoughts about how to proceed in the coming week, perhaps getting some inspiration from mpl. Browsing through their source they don't do particularly sophisticated checks. Could use some input on how to test the internals further (if needed at all).

riley-brady commented 4 months ago

Awesome, thanks for pushing this forward!

Could you open a PR at https://github.com/cvanelteren/proplot/pulls or https://github.com/proplot-dev/proplot/pulls with your branch? I can't comment on https://github.com/proplot-dev/proplot/compare/master...cvanelteren:proplot:unittest without it being a targeted PR.

I have merged the unittests with mine and added some

Is this from @lukelbd's other branch or elsewhere? Just clarifying what you are merging in.

cvanelteren commented 4 months ago

@riley-brady just added it #458. I will have a look at latest mpl to see what changed, then we can skip the intermediate versions for working towards a mpl 3.9.1 release.

EDIT: see #459