diff --git a/doc/api/next_api_changes/deprecations/23668-TC.rst b/doc/api/next_api_changes/deprecations/23668-TC.rst new file mode 100644 index 000000000000..d131ff497fb1 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/23668-TC.rst @@ -0,0 +1,22 @@ +Pending deprecation top-level cmap registration and access functions in ``mpl.cm`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As part of a `multi-step process +`_ we are refactoring +the global state for managing the registered colormaps. + +In Matplotlib 3.5 we added a `.ColormapRegistry` class and exposed an +instance at the top level as ``matplotlib.colormaps``. The existing +top level functions in `matplotlib.cm` (``get_cmap``, ``register_cmap``, +``unregister_cmap``) were changed to be aliases around the same instance. + +In Matplotlib 3.6 we have marked those top level functions as pending +deprecation with the intention of deprecation in Matplotlib 3.7. The +following functions have been marked for pending deprecation: + +- `matplotlib.cm.get_cmap` - use ``matplotlib.colormaps[name]`` instead +- `matplotlib.cm.register_cmap` - use ``matplotlib.colormaps.register`` instead +- `matplotlib.cm.unregister_cmap` - use ``matplotlib.colormaps.unregister`` instead +- ``matplotlib.pyplot.register_cmap`` - use ``matplotlib.colormaps.register`` instead + +The `matplotlib.pyplot.get_cmap` function will stay available for backward compatibility. diff --git a/doc/api/pyplot_summary.rst b/doc/api/pyplot_summary.rst index a3472f18be31..d329d495709c 100644 --- a/doc/api/pyplot_summary.rst +++ b/doc/api/pyplot_summary.rst @@ -64,6 +64,7 @@ Plotting commands gcf gci get + get_cmap get_figlabels get_fignums getp diff --git a/doc/users/next_whats_new/resample_colormaps.rst b/doc/users/next_whats_new/resample_colormaps.rst new file mode 100644 index 000000000000..f2fbe4da4d83 --- /dev/null +++ b/doc/users/next_whats_new/resample_colormaps.rst @@ -0,0 +1,13 @@ +Colormap method for creating a different lookup table size +---------------------------------------------------------- +The new method `.Colormap.resampled` creates a new `.Colormap` instance +with the specified lookup table size. This is a replacement for manipulating +the lookup table size via ``get_cmap``. + +Use:: + + get_cmap(name).resampled(N) + +instead of:: + + get_cmap(name, lut=N) diff --git a/examples/images_contours_and_fields/contour_image.py b/examples/images_contours_and_fields/contour_image.py index 444d4df4a78f..a2a07f20f273 100644 --- a/examples/images_contours_and_fields/contour_image.py +++ b/examples/images_contours_and_fields/contour_image.py @@ -41,7 +41,7 @@ axs = _axs.flatten() cset1 = axs[0].contourf(X, Y, Z, levels, norm=norm, - cmap=cm.get_cmap(cmap, len(levels) - 1)) + cmap=cmap.resampled(len(levels) - 1)) # It is not necessary, but for the colormap, we need only the # number of levels minus 1. To avoid discretization error, use # either this number or a large number such as the default (256). diff --git a/examples/images_contours_and_fields/image_annotated_heatmap.py b/examples/images_contours_and_fields/image_annotated_heatmap.py index 14ee0c0235f5..16054d145ea5 100644 --- a/examples/images_contours_and_fields/image_annotated_heatmap.py +++ b/examples/images_contours_and_fields/image_annotated_heatmap.py @@ -39,6 +39,7 @@ import numpy as np import matplotlib +import matplotlib as mpl import matplotlib.pyplot as plt # sphinx_gallery_thumbnail_number = 2 @@ -272,7 +273,7 @@ def annotate_heatmap(im, data=None, valfmt="{x:.2f}", fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: qrates[::-1][norm(x)]) im, _ = heatmap(data, y, x, ax=ax3, - cmap=plt.get_cmap("PiYG", 7), norm=norm, + cmap=mpl.colormaps["PiYG"].resampled(7), norm=norm, cbar_kw=dict(ticks=np.arange(-3, 4), format=fmt), cbarlabel="Quality Rating") diff --git a/examples/lines_bars_and_markers/multivariate_marker_plot.py b/examples/lines_bars_and_markers/multivariate_marker_plot.py index 156bb7bef5e2..89670b5a223f 100644 --- a/examples/lines_bars_and_markers/multivariate_marker_plot.py +++ b/examples/lines_bars_and_markers/multivariate_marker_plot.py @@ -31,7 +31,7 @@ positions = np.random.normal(size=(N, 2)) * 5 data = zip(skills, takeoff_angles, thrusts, successful, positions) -cmap = plt.cm.get_cmap("plasma") +cmap = plt.colormaps["plasma"] fig, ax = plt.subplots() fig.suptitle("Throwing success", size=14) for skill, takeoff, thrust, mood, pos in data: diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index 2e6741add7ea..6001683358f1 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -201,6 +201,11 @@ def unregister(self, name): globals().update(_colormaps) +@_api.deprecated( + '3.6', + pending=True, + alternative="``matplotlib.colormaps.register_cmap(name)``" +) def register_cmap(name=None, cmap=None, *, override_builtin=False): """ Add a colormap to the set recognized by :func:`get_cmap`. @@ -244,13 +249,10 @@ def register_cmap(name=None, cmap=None, *, override_builtin=False): _colormaps._allow_override_builtin = False -def get_cmap(name=None, lut=None): +def _get_cmap(name=None, lut=None): """ Get a colormap instance, defaulting to rc values if *name* is None. - Colormaps added with :func:`register_cmap` take precedence over - built-in colormaps. - Parameters ---------- name : `matplotlib.colors.Colormap` or str or None, default: None @@ -260,6 +262,10 @@ def get_cmap(name=None, lut=None): lut : int or None, default: None If *name* is not already a Colormap instance and *lut* is not None, the colormap will be resampled to have *lut* entries in the lookup table. + + Returns + ------- + Colormap """ if name is None: name = mpl.rcParams['image.cmap'] @@ -269,9 +275,20 @@ def get_cmap(name=None, lut=None): if lut is None: return _colormaps[name] else: - return _colormaps[name]._resample(lut) + return _colormaps[name].resampled(lut) +# do it in two steps like this so we can have an un-deprecated version in +# pyplot. +get_cmap = _api.deprecated( + '3.6', pending=True, alternative="``matplotlib.colormaps[name]``" +)(_get_cmap) + +@_api.deprecated( + '3.6', + pending=True, + alternative="``matplotlib.colormaps.unregister_cmap(name)``" +) def unregister_cmap(name): """ Remove a colormap recognized by :func:`get_cmap`. @@ -550,8 +567,8 @@ def set_cmap(self, cmap): cmap : `.Colormap` or str or None """ in_init = self.cmap is None - cmap = get_cmap(cmap) - self.cmap = cmap + + self.cmap = _ensure_cmap(cmap) if not in_init: self.changed() # Things are not set up properly yet. @@ -663,3 +680,26 @@ def changed(self): *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm* name together with *vmin*/*vmax* is acceptable).""", ) + + +def _ensure_cmap(cmap): + """ + Ensure that we have a `.Colormap` object. + + Parameters + ---------- + cmap : None, str, Colormap + + - if a `Colormap`, return it + - if a string, look it up in mpl.colormaps + - if None, look up the default color map in mpl.colormaps + + Returns + ------- + Colormap + """ + if isinstance(cmap, colors.Colormap): + return cmap + return mpl.colormaps[ + cmap if cmap is not None else mpl.rcParams['image.cmap'] + ] diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 2a537b6c9b7b..1eedf3d695cb 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -854,8 +854,17 @@ def is_gray(self): return (np.all(self._lut[:, 0] == self._lut[:, 1]) and np.all(self._lut[:, 0] == self._lut[:, 2])) - def _resample(self, lutsize): + def resampled(self, lutsize): """Return a new colormap with *lutsize* entries.""" + if hasattr(self, '_resample'): + _api.warn_external( + "The ability to resample a color map is now public API " + f"However the class {type(self)} still only implements " + "the previous private _resample method. Please update " + "your class." + ) + return self._resample(lutsize) + raise NotImplementedError() def reversed(self, name=None): @@ -1050,7 +1059,7 @@ def from_list(name, colors, N=256, gamma=1.0): return LinearSegmentedColormap(name, cdict, N, gamma) - def _resample(self, lutsize): + def resampled(self, lutsize): """Return a new colormap with *lutsize* entries.""" new_cmap = LinearSegmentedColormap(self.name, self._segmentdata, lutsize) @@ -1154,7 +1163,7 @@ def _init(self): self._isinit = True self._set_extremes() - def _resample(self, lutsize): + def resampled(self, lutsize): """Return a new colormap with *lutsize* entries.""" colors = self(np.linspace(0, 1, lutsize)) new_cmap = ListedColormap(colors, name=self.name) diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 951e6e09b2fe..49e45e4a282b 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -68,7 +68,7 @@ from matplotlib.scale import get_scale_names from matplotlib import cm -from matplotlib.cm import _colormaps as colormaps, get_cmap, register_cmap +from matplotlib.cm import _colormaps as colormaps, register_cmap from matplotlib.colors import _color_sequences as color_sequences import numpy as np @@ -2069,6 +2069,13 @@ def clim(vmin=None, vmax=None): im.set_clim(vmin, vmax) +# eventually this implementation should move here, use indirection for now to +# avoid having two copies of the code floating around. +def get_cmap(name=None, lut=None): + return cm._get_cmap(name=name, lut=lut) +get_cmap.__doc__ = cm._get_cmap.__doc__ + + def set_cmap(cmap): """ Set the default colormap, and applies it to the current image if any. @@ -2084,7 +2091,7 @@ def set_cmap(cmap): matplotlib.cm.register_cmap matplotlib.cm.get_cmap """ - cmap = cm.get_cmap(cmap) + cmap = get_cmap(cmap) rc('image', cmap=cmap.name) im = gci() diff --git a/lib/matplotlib/streamplot.py b/lib/matplotlib/streamplot.py index 31ad96044a1e..8656fb0ab53c 100644 --- a/lib/matplotlib/streamplot.py +++ b/lib/matplotlib/streamplot.py @@ -187,7 +187,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, if use_multicolor_lines: if norm is None: norm = mcolors.Normalize(color.min(), color.max()) - cmap = cm.get_cmap(cmap) + cmap = cm._ensure_cmap(cmap) streamlines = [] arrows = [] diff --git a/lib/matplotlib/tests/test_artist.py b/lib/matplotlib/tests/test_artist.py index 0fb1fa442fe7..7e302784c332 100644 --- a/lib/matplotlib/tests/test_artist.py +++ b/lib/matplotlib/tests/test_artist.py @@ -5,7 +5,6 @@ import pytest -from matplotlib import cm import matplotlib.colors as mcolors import matplotlib.pyplot as plt import matplotlib.patches as mpatches @@ -14,6 +13,7 @@ import matplotlib.transforms as mtransforms import matplotlib.collections as mcollections import matplotlib.artist as martist +import matplotlib as mpl from matplotlib.testing.decorators import check_figures_equal, image_comparison @@ -415,7 +415,7 @@ def test_format_cursor_data_BoundaryNorm(): # map range -1..1 to 0..256 in 0.01 steps fig, ax = plt.subplots() fig.suptitle("-1..1 to 0..256 in 0.01") - cmap = cm.get_cmap('RdBu_r', 200) + cmap = mpl.colormaps['RdBu_r'].resampled(200) norm = mcolors.BoundaryNorm(np.linspace(-1, 1, 200), 200) img = ax.imshow(X, cmap=cmap, norm=norm) @@ -439,7 +439,7 @@ def test_format_cursor_data_BoundaryNorm(): # map range -1..1 to 0..256 in 0.01 steps fig, ax = plt.subplots() fig.suptitle("-1..1 to 0..256 in 0.001") - cmap = cm.get_cmap('RdBu_r', 2000) + cmap = mpl.colormaps['RdBu_r'].resampled(2000) norm = mcolors.BoundaryNorm(np.linspace(-1, 1, 2000), 2000) img = ax.imshow(X, cmap=cmap, norm=norm) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 51fbea3cfaaa..2a22dcf93439 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -1220,7 +1220,7 @@ def test_pcolormesh_alpha(): Qy = Y + np.sin(X) Z = np.hypot(X, Y) / 5 Z = (Z - Z.min()) / Z.ptp() - vir = plt.get_cmap("viridis", 16) + vir = mpl.colormaps["viridis"].resampled(16) # make another colormap with varying alpha colors = vir(np.arange(16)) colors[:, 3] = 0.5 + 0.5*np.sin(np.arange(16)) @@ -2250,7 +2250,7 @@ def test_contour_hatching(): x, y, z = contour_dat() fig, ax = plt.subplots() ax.contourf(x, y, z, 7, hatches=['/', '\\', '//', '-'], - cmap=plt.get_cmap('gray'), + cmap=mpl.colormaps['gray'], extend='both', alpha=0.5) @@ -2260,7 +2260,7 @@ def test_contour_colorbar(): fig, ax = plt.subplots() cs = ax.contourf(x, y, z, levels=np.arange(-1.8, 1.801, 0.2), - cmap=plt.get_cmap('RdBu'), + cmap=mpl.colormaps['RdBu'], vmin=-0.6, vmax=0.6, extend='both') @@ -2444,7 +2444,7 @@ def test_scatter_edgecolor_RGB(self): @check_figures_equal(extensions=["png"]) def test_scatter_invalid_color(self, fig_test, fig_ref): ax = fig_test.subplots() - cmap = plt.get_cmap("viridis", 16) + cmap = mpl.colormaps["viridis"].resampled(16) cmap.set_bad("k", 1) # Set a nonuniform size to prevent the last call to `scatter` (plotting # the invalid points separately in fig_ref) from using the marker @@ -2453,7 +2453,7 @@ def test_scatter_invalid_color(self, fig_test, fig_ref): c=[1, np.nan, 2, np.nan], s=[1, 2, 3, 4], cmap=cmap, plotnonfinite=True) ax = fig_ref.subplots() - cmap = plt.get_cmap("viridis", 16) + cmap = mpl.colormaps["viridis"].resampled(16) ax.scatter([0, 2], [0, 2], c=[1, 2], s=[1, 3], cmap=cmap) ax.scatter([1, 3], [1, 3], s=[2, 4], color="k") @@ -2461,7 +2461,7 @@ def test_scatter_invalid_color(self, fig_test, fig_ref): def test_scatter_no_invalid_color(self, fig_test, fig_ref): # With plotnonfinite=False we plot only 2 points. ax = fig_test.subplots() - cmap = plt.get_cmap("viridis", 16) + cmap = mpl.colormaps["viridis"].resampled(16) cmap.set_bad("k", 1) ax.scatter(range(4), range(4), c=[1, np.nan, 2, np.nan], s=[1, 2, 3, 4], diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index bbc653419805..3647e561d2d6 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -955,7 +955,7 @@ def test_quadmesh_set_array(): def test_quadmesh_vmin_vmax(): # test when vmin/vmax on the norm changes, the quadmesh gets updated fig, ax = plt.subplots() - cmap = mpl.cm.get_cmap('plasma') + cmap = mpl.colormaps['plasma'] norm = mpl.colors.Normalize(vmin=0, vmax=1) coll = ax.pcolormesh([[1]], cmap=cmap, norm=norm) fig.canvas.draw() diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 2dd9692dc2e3..c1faef435f55 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -3,6 +3,7 @@ from matplotlib import cm import matplotlib.colors as mcolors +import matplotlib as mpl from matplotlib import rc_context from matplotlib.testing.decorators import image_comparison @@ -24,7 +25,7 @@ def _get_cmap_norms(): colorbar_extension_length. """ # Create a colormap and specify the levels it represents. - cmap = cm.get_cmap("RdBu", lut=5) + cmap = mpl.colormaps["RdBu"].resampled(5) clevs = [-5., -2.5, -.5, .5, 1.5, 3.5] # Define norms for the colormaps. norms = dict() @@ -132,8 +133,8 @@ def test_colorbar_extension_inverted_axis(orientation, extend, expected): """Test extension color with an inverted axis""" data = np.arange(12).reshape(3, 4) fig, ax = plt.subplots() - cmap = plt.get_cmap("viridis").with_extremes(under=(0, 0, 0, 1), - over=(1, 1, 1, 1)) + cmap = mpl.colormaps["viridis"].with_extremes(under=(0, 0, 0, 1), + over=(1, 1, 1, 1)) im = ax.imshow(data, cmap=cmap) cbar = fig.colorbar(im, orientation=orientation, extend=extend) if orientation == "horizontal": @@ -268,7 +269,7 @@ def test_colorbar_single_scatter(): plt.figure() x = y = [0] z = [50] - cmap = plt.get_cmap('jet', 16) + cmap = mpl.colormaps['jet'].resampled(16) cs = plt.scatter(x, y, z, c=z, cmap=cmap) plt.colorbar(cs) @@ -326,7 +327,7 @@ def test_colorbar_closed_patch(): ax4 = fig.add_axes([0.05, 0.25, 0.9, 0.1]) ax5 = fig.add_axes([0.05, 0.05, 0.9, 0.1]) - cmap = cm.get_cmap("RdBu", lut=5) + cmap = mpl.colormaps["RdBu"].resampled(5) im = ax1.pcolormesh(np.linspace(0, 10, 16).reshape((4, 4)), cmap=cmap) @@ -957,7 +958,7 @@ def test_colorbar_extend_drawedges(): fig.subplots_adjust(left=0.05, bottom=0.05, right=0.95, top=0.95) for ax, (extend, coloroffset, res) in zip(axs, params): - cmap = plt.get_cmap("viridis") + cmap = mpl.colormaps["viridis"] bounds = np.arange(5) nb_colors = len(bounds) + coloroffset colors = cmap(np.linspace(100, 255, nb_colors).astype(int)) @@ -980,7 +981,7 @@ def test_colorbar_extend_drawedges(): def test_negative_boundarynorm(): fig, ax = plt.subplots(figsize=(1, 3)) - cmap = plt.get_cmap("viridis") + cmap = mpl.colormaps["viridis"] clevs = np.arange(-94, -85) norm = BoundaryNorm(clevs, cmap.N) @@ -1016,7 +1017,7 @@ def test_nonorm(): fig.subplots_adjust(bottom=0.5) norm = NoNorm(vmin=min(data), vmax=max(data)) - cmap = cm.get_cmap("viridis", len(data)) + cmap = mpl.colormaps["viridis"].resampled(len(data)) mappable = cm.ScalarMappable(norm=norm, cmap=cmap) cbar = fig.colorbar(mappable, cax=ax, orientation="horizontal") diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index fa30bba50a47..30f28fc67ab7 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -12,6 +12,7 @@ from matplotlib import cbook, cm, cycler import matplotlib +import matplotlib as mpl import matplotlib.colors as mcolors import matplotlib.colorbar as mcolorbar import matplotlib.pyplot as plt @@ -29,9 +30,9 @@ def test_create_lookup_table(N, result): assert_array_almost_equal(mcolors._create_lookup_table(N, data), result) -def test_resample(): +def test_resampled(): """ - GitHub issue #6025 pointed to incorrect ListedColormap._resample; + GitHub issue #6025 pointed to incorrect ListedColormap.resampled; here we test the method for LinearSegmentedColormap as well. """ n = 101 @@ -47,8 +48,8 @@ def test_resample(): cmap.set_under('r') cmap.set_over('g') cmap.set_bad('b') - lsc3 = lsc._resample(3) - lc3 = lc._resample(3) + lsc3 = lsc.resampled(3) + lc3 = lc.resampled(3) expected = np.array([[0.0, 0.2, 1.0, 0.7], [0.5, 0.2, 0.5, 0.7], [1.0, 0.2, 0.0, 0.7]], float) @@ -64,24 +65,48 @@ def test_resample(): def test_register_cmap(): - new_cm = cm.get_cmap("viridis") + new_cm = mpl.colormaps["viridis"] target = "viridis2" - cm.register_cmap(target, new_cm) - assert plt.get_cmap(target) == new_cm + with pytest.warns( + PendingDeprecationWarning, + match=r"matplotlib\.colormaps\.register_cmap\(name\)" + ): + cm.register_cmap(target, new_cm) + assert mpl.colormaps[target] == new_cm with pytest.raises(ValueError, match="Arguments must include a name or a Colormap"): - cm.register_cmap() - - cm.unregister_cmap(target) + with pytest.warns( + PendingDeprecationWarning, + match=r"matplotlib\.colormaps\.register_cmap\(name\)" + ): + cm.register_cmap() + + with pytest.warns( + PendingDeprecationWarning, + match=r"matplotlib\.colormaps\.unregister_cmap\(name\)" + ): + cm.unregister_cmap(target) with pytest.raises(ValueError, match=f'{target!r} is not a valid value for name;'): - cm.get_cmap(target) - # test that second time is error free - cm.unregister_cmap(target) + with pytest.warns( + PendingDeprecationWarning, + match=r"matplotlib\.colormaps\[name\]" + ): + cm.get_cmap(target) + with pytest.warns( + PendingDeprecationWarning, + match=r"matplotlib\.colormaps\.unregister_cmap\(name\)" + ): + # test that second time is error free + cm.unregister_cmap(target) with pytest.raises(TypeError, match="'cmap' must be"): - cm.register_cmap('nome', cmap='not a cmap') + with pytest.warns( + PendingDeprecationWarning, + match=r"matplotlib\.colormaps\.register_cmap\(name\)" + ): + cm.register_cmap('nome', cmap='not a cmap') def test_double_register_builtin_cmap(): @@ -89,19 +114,22 @@ def test_double_register_builtin_cmap(): match = f"Re-registering the builtin cmap {name!r}." with pytest.raises(ValueError, match=match): matplotlib.colormaps.register( - cm.get_cmap(name), name=name, force=True + mpl.colormaps[name], name=name, force=True ) with pytest.raises(ValueError, match='A colormap named "viridis"'): - cm.register_cmap(name, cm.get_cmap(name)) + with pytest.warns(): + cm.register_cmap(name, mpl.colormaps[name]) with pytest.warns(UserWarning): - cm.register_cmap(name, cm.get_cmap(name), override_builtin=True) + # TODO is warning more than once! + cm.register_cmap(name, mpl.colormaps[name], override_builtin=True) def test_unregister_builtin_cmap(): name = "viridis" match = f'cannot unregister {name!r} which is a builtin colormap.' with pytest.raises(ValueError, match=match): - cm.unregister_cmap(name) + with pytest.warns(): + cm.unregister_cmap(name) def test_colormap_copy(): @@ -127,7 +155,7 @@ def test_colormap_copy(): def test_colormap_equals(): - cmap = plt.get_cmap("plasma") + cmap = mpl.colormaps["plasma"] cm_copy = cmap.copy() # different object id's assert cm_copy is not cmap @@ -155,7 +183,7 @@ def test_colormap_endian(): mapping of 1.0 when input from a non-native-byteorder array. """ - cmap = cm.get_cmap("jet") + cmap = mpl.colormaps["jet"] # Test under, over, and invalid along with values 0 and 1. a = [-0.5, 0, 0.5, 1, 1.5, np.nan] for dt in ["f2", "f4", "f8"]: @@ -170,7 +198,7 @@ def test_colormap_invalid(): rather than bad. This tests to make sure all invalid values (-inf, nan, inf) are mapped respectively to (under, bad, over). """ - cmap = cm.get_cmap("plasma") + cmap = mpl.colormaps["plasma"] x = np.array([-np.inf, -1, 0, np.nan, .7, 2, np.inf]) expected = np.array([[0.050383, 0.029803, 0.527975, 1.], @@ -203,7 +231,7 @@ def test_colormap_return_types(): Make sure that tuples are returned for scalar input and that the proper shapes are returned for ndarrays. """ - cmap = cm.get_cmap("plasma") + cmap = mpl.colormaps["plasma"] # Test return types and shapes # scalar input needs to return a tuple of length 4 assert isinstance(cmap(0.5), tuple) @@ -318,7 +346,7 @@ def test_BoundaryNorm(): # Testing extend keyword, with interpolation (large cmap) bounds = [1, 2, 3] - cmap = cm.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] mynorm = mcolors.BoundaryNorm(bounds, cmap.N, extend='both') refnorm = mcolors.BoundaryNorm([0] + bounds + [4], cmap.N) x = np.random.randn(100) * 10 + 2 @@ -789,7 +817,7 @@ def test_boundarynorm_and_colorbarbase(): # Set the colormap and bounds bounds = [-1, 2, 5, 7, 12, 15] - cmap = cm.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] # Default behavior norm = mcolors.BoundaryNorm(bounds, cmap.N) @@ -1134,13 +1162,13 @@ def test_pandas_iterable(pd): assert_array_equal(cm1.colors, cm2.colors) -@pytest.mark.parametrize('name', sorted(plt.colormaps())) +@pytest.mark.parametrize('name', sorted(mpl.colormaps())) def test_colormap_reversing(name): """ Check the generated _lut data of a colormap and corresponding reversed colormap if they are almost the same. """ - cmap = plt.get_cmap(name) + cmap = mpl.colormaps[name] cmap_r = cmap.reversed() if not cmap_r._isinit: cmap._init() @@ -1307,7 +1335,7 @@ def test_hex_shorthand_notation(): def test_repr_png(): - cmap = plt.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] png = cmap._repr_png_() assert len(png) > 0 img = Image.open(BytesIO(png)) @@ -1320,7 +1348,7 @@ def test_repr_png(): def test_repr_html(): - cmap = plt.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] html = cmap._repr_html_() assert len(html) > 0 png = cmap._repr_png_() @@ -1331,7 +1359,7 @@ def test_repr_html(): def test_get_under_over_bad(): - cmap = plt.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] assert_array_equal(cmap.get_under(), cmap(-np.inf)) assert_array_equal(cmap.get_over(), cmap(np.inf)) assert_array_equal(cmap.get_bad(), cmap(np.nan)) @@ -1339,7 +1367,7 @@ def test_get_under_over_bad(): @pytest.mark.parametrize('kind', ('over', 'under', 'bad')) def test_non_mutable_get_values(kind): - cmap = copy.copy(plt.get_cmap('viridis')) + cmap = copy.copy(mpl.colormaps['viridis']) init_value = getattr(cmap, f'get_{kind}')() getattr(cmap, f'set_{kind}')('k') black_value = getattr(cmap, f'get_{kind}')() @@ -1348,7 +1376,7 @@ def test_non_mutable_get_values(kind): def test_colormap_alpha_array(): - cmap = plt.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] vals = [-1, 0.5, 2] # under, valid, over with pytest.raises(ValueError, match="alpha is array-like but"): cmap(vals, alpha=[1, 1, 1, 1]) @@ -1360,7 +1388,7 @@ def test_colormap_alpha_array(): def test_colormap_bad_data_with_alpha(): - cmap = plt.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] c = cmap(np.nan, alpha=0.5) assert c == (0, 0, 0, 0) c = cmap([0.5, np.nan], alpha=0.5) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index bd1efa0fe68d..46dbe4cfe8f8 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -883,7 +883,7 @@ def test_imshow_endianess(): remove_text=True, style='mpl20') def test_imshow_masked_interpolation(): - cmap = plt.get_cmap('viridis').with_extremes(over='r', under='b', bad='k') + cmap = mpl.colormaps['viridis'].with_extremes(over='r', under='b', bad='k') N = 20 n = colors.Normalize(vmin=0, vmax=N*N-1) @@ -1096,7 +1096,7 @@ def test_image_array_alpha(fig_test, fig_ref): zz = np.exp(- 3 * ((xx - 0.5) ** 2) + (yy - 0.7 ** 2)) alpha = zz / zz.max() - cmap = plt.get_cmap('viridis') + cmap = mpl.colormaps['viridis'] ax = fig_test.add_subplot() ax.imshow(zz, alpha=alpha, cmap=cmap, interpolation='nearest') @@ -1113,7 +1113,7 @@ def test_image_array_alpha_validation(): @mpl.style.context('mpl20') def test_exact_vmin(): - cmap = copy(plt.cm.get_cmap("autumn_r")) + cmap = copy(mpl.colormaps["autumn_r"]) cmap.set_under(color="lightgrey") # make the image exactly 190 pixels wide @@ -1234,7 +1234,7 @@ def test_norm_change(fig_test, fig_ref): masked_data = np.ma.array(data, mask=False) masked_data.mask[0:2, 0:2] = True - cmap = plt.get_cmap('viridis').with_extremes(under='w') + cmap = mpl.colormaps['viridis'].with_extremes(under='w') ax = fig_test.subplots() im = ax.imshow(data, norm=colors.LogNorm(vmin=0.5, vmax=1), @@ -1268,7 +1268,7 @@ def test_huge_range_log(fig_test, fig_ref, x): data[0:2, :] = 1000 ax = fig_ref.subplots() - cmap = plt.get_cmap('viridis').with_extremes(under='w') + cmap = mpl.colormaps['viridis'].with_extremes(under='w') ax.imshow(data, norm=colors.Normalize(vmin=1, vmax=data.max()), interpolation='nearest', cmap=cmap) diff --git a/lib/matplotlib/tests/test_lines.py b/lib/matplotlib/tests/test_lines.py index 852233a71c6c..e7002df8a558 100644 --- a/lib/matplotlib/tests/test_lines.py +++ b/lib/matplotlib/tests/test_lines.py @@ -12,6 +12,7 @@ import pytest import matplotlib +import matplotlib as mpl import matplotlib.lines as mlines from matplotlib.markers import MarkerStyle from matplotlib.path import Path @@ -365,7 +366,7 @@ def test_markevery_prop_cycle(fig_test, fig_ref): slice(100, 200, 3), 0.1, 0.3, 1.5, (0.0, 0.1), (0.45, 0.1)] - cmap = plt.get_cmap('jet') + cmap = mpl.colormaps['jet'] colors = cmap(np.linspace(0.2, 0.8, len(cases))) x = np.linspace(-1, 1) diff --git a/lib/matplotlib/tests/test_triangulation.py b/lib/matplotlib/tests/test_triangulation.py index 1b1cdc91f878..365f9a0360cd 100644 --- a/lib/matplotlib/tests/test_triangulation.py +++ b/lib/matplotlib/tests/test_triangulation.py @@ -5,7 +5,6 @@ import pytest import matplotlib as mpl -import matplotlib.cm as cm import matplotlib.pyplot as plt import matplotlib.tri as mtri from matplotlib.path import Path @@ -925,7 +924,7 @@ def dipole_potential(x, y): plt.triplot(triang, color='0.8') levels = np.arange(0., 1., 0.01) - cmap = cm.get_cmap(name='hot', lut=None) + cmap = mpl.colormaps['hot'] plt.tricontour(tri_refi, z_test_refi, levels=levels, cmap=cmap, linewidths=[2.0, 1.0, 1.0, 1.0]) # Plots direction of the electrical vector field diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index ea5a000f0d70..cda8144529bf 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -206,7 +206,7 @@ def test_contourf3d_extend(fig_test, fig_ref, extend, levels): Z = X**2 + Y**2 # Manually set the over/under colors to be the end of the colormap - cmap = plt.get_cmap('viridis').copy() + cmap = mpl.colormaps['viridis'].copy() cmap.set_under(cmap(0)) cmap.set_over(cmap(255)) # Set vmin/max to be the min/max values plotted on the reference image @@ -546,7 +546,7 @@ def test_surface3d_masked(): ) z = np.ma.masked_less(matrix, 0) norm = mcolors.Normalize(vmax=z.max(), vmin=z.min()) - colors = plt.get_cmap("plasma")(norm(z)) + colors = mpl.colormaps["plasma"](norm(z)) ax.plot_surface(x, y, z, facecolors=colors) ax.view_init(30, -80, 0) diff --git a/tutorials/colors/colormap-manipulation.py b/tutorials/colors/colormap-manipulation.py index bbd8cb8d2b2f..297506004861 100644 --- a/tutorials/colors/colormap-manipulation.py +++ b/tutorials/colors/colormap-manipulation.py @@ -4,7 +4,7 @@ ******************************** Matplotlib has a number of built-in colormaps accessible via -`.matplotlib.cm.get_cmap`. There are also external libraries like +`.matplotlib.colormaps`. There are also external libraries like palettable_ that have many extra colormaps. .. _palettable: https://jiffyclub.github.io/palettable/ @@ -24,19 +24,19 @@ ============================================ First, getting a named colormap, most of which are listed in -:doc:`/tutorials/colors/colormaps`, may be done using -`.matplotlib.cm.get_cmap`, which returns a colormap object. -The second argument gives the size of the list of colors used to define the -colormap, and below we use a modest value of 8 so there are not a lot of -values to look at. +:doc:`/tutorials/colors/colormaps`, may be done using `.matplotlib.colormaps`, +which returns a colormap object. The length of the list of colors used +internally to define the colormap can be adjusted via `.Colormap.resampled`. +Below we use a modest value of 8 so there are not a lot of values to look at. + """ import numpy as np import matplotlib.pyplot as plt -from matplotlib import cm +import matplotlib as mpl from matplotlib.colors import ListedColormap, LinearSegmentedColormap -viridis = cm.get_cmap('viridis', 8) +viridis = mpl.colormaps['viridis'].resampled(8) ############################################################################## # The object ``viridis`` is a callable, that when passed a float between @@ -72,7 +72,7 @@ # However, one may still call the colormap with an integer array, or with a # float array between 0 and 1. -copper = cm.get_cmap('copper', 8) +copper = mpl.colormaps['copper'].resampled(8) print('copper(range(8))', copper(range(8))) print('copper(np.linspace(0, 1, 8))', copper(np.linspace(0, 1, 8))) @@ -123,7 +123,7 @@ def plot_examples(colormaps): # For example, suppose we want to make the first 25 entries of a 256-length # "viridis" colormap pink for some reason: -viridis = cm.get_cmap('viridis', 256) +viridis = mpl.colormaps['viridis'].resampled(256) newcolors = viridis(np.linspace(0, 1, 256)) pink = np.array([248/256, 24/256, 148/256, 1]) newcolors[:25, :] = pink @@ -138,15 +138,15 @@ def plot_examples(colormaps): # values that were in the original colormap. This method does not interpolate # in color-space to add new colors. -viridis_big = cm.get_cmap('viridis') +viridis_big = mpl.colormaps['viridis'] newcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128))) plot_examples([viridis, newcmp]) ############################################################################## # and we can easily concatenate two colormaps: -top = cm.get_cmap('Oranges_r', 128) -bottom = cm.get_cmap('Blues', 128) +top = mpl.colormaps['Oranges_r'].resampled(128) +bottom = mpl.colormaps['Blues'].resampled(128) newcolors = np.vstack((top(np.linspace(0, 1, 128)), bottom(np.linspace(0, 1, 128)))) @@ -268,4 +268,4 @@ def plot_linearmap(cdict): # - `matplotlib.colors.LinearSegmentedColormap` # - `matplotlib.colors.ListedColormap` # - `matplotlib.cm` -# - `matplotlib.cm.get_cmap` +# - `matplotlib.colormaps` diff --git a/tutorials/colors/colormaps.py b/tutorials/colors/colormaps.py index 97cf8b8f28b2..2868a15079c7 100644 --- a/tutorials/colors/colormaps.py +++ b/tutorials/colors/colormaps.py @@ -4,7 +4,7 @@ ******************************** Matplotlib has a number of built-in colormaps accessible via -`.matplotlib.cm.get_cmap`. There are also external libraries that +`.matplotlib.colormaps`. There are also external libraries that have many extra colormaps, which can be viewed in the `Third-party colormaps`_ section of the Matplotlib documentation. Here we briefly discuss how to choose between the many options. For @@ -80,7 +80,6 @@ import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt -from matplotlib import cm from colorspacious import cspace_converter @@ -105,7 +104,7 @@ def plot_color_gradients(category, cmap_list): axs[0].set_title(f'{category} colormaps', fontsize=14) for ax, name in zip(axs, cmap_list): - ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) + ax.imshow(gradient, aspect='auto', cmap=mpl.colormaps[name]) ax.text(-0.01, 0.5, name, va='center', ha='right', fontsize=10, transform=ax.transAxes) @@ -277,7 +276,7 @@ def plot_color_gradients(category, cmap_list): # Get RGB values for colormap and convert the colormap in # CAM02-UCS colorspace. lab[0, :, 0] is the lightness. - rgb = cm.get_cmap(cmap)(x)[np.newaxis, :, :3] + rgb = mpl.colormaps[cmap](x)[np.newaxis, :, :3] lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb) # Plot colormap L values. Do separately for each category @@ -380,14 +379,14 @@ def plot_color_gradients(cmap_category, cmap_list): for ax, name in zip(axs, cmap_list): # Get RGB values for colormap. - rgb = cm.get_cmap(plt.get_cmap(name))(x)[np.newaxis, :, :3] + rgb = mpl.colormaps[name](x)[np.newaxis, :, :3] # Get colormap in CAM02-UCS colorspace. We want the lightness. lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb) L = lab[0, :, 0] L = np.float32(np.vstack((L, L, L))) - ax[0].imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) + ax[0].imshow(gradient, aspect='auto', cmap=mpl.colormaps[name]) ax[1].imshow(L, aspect='auto', cmap='binary_r', vmin=0., vmax=100.) pos = list(ax[0].get_position().bounds) x_text = pos[0] - 0.01