Skip to content

Commit 69a92eb

Browse files
committed
ENH: Add Axes.colorbar() method
1 parent a895fb3 commit 69a92eb

4 files changed

Lines changed: 114 additions & 6 deletions

File tree

doc/api/axes_api.rst

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,8 @@ Axis limits and direction
314314
Axes.set_ybound
315315
Axes.get_ybound
316316

317-
Axis labels, title, and legend
318-
------------------------------
317+
Axis labels and title
318+
---------------------
319319

320320
.. autosummary::
321321
:toctree: _as_gen
@@ -330,9 +330,6 @@ Axis labels, title, and legend
330330

331331
Axes.set_title
332332
Axes.get_title
333-
Axes.legend
334-
Axes.get_legend
335-
Axes.get_legend_handles_labels
336333

337334
Axis scales
338335
-----------
@@ -438,6 +435,20 @@ Ticks and tick labels
438435
Axes.locator_params
439436

440437

438+
Legend and colorbar
439+
===================
440+
441+
.. autosummary::
442+
:toctree: _as_gen
443+
:template: autosummary.rst
444+
:nosignatures:
445+
446+
Axes.legend
447+
Axes.get_legend
448+
Axes.get_legend_handles_labels
449+
Axes.colorbar
450+
451+
441452
Units
442453
=====
443454

lib/matplotlib/axes/_axes.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,73 @@ def legend(self, *args, **kwargs):
355355
def _remove_legend(self, legend):
356356
self.legend_ = None
357357

358+
def colorbar(self, mappable=None, *, use_gridspec=True, **kwargs):
359+
"""
360+
Add a colorbar next to the Axes.
361+
362+
Parameters
363+
----------
364+
mappable : `matplotlib.colorizer.ColorizingArtist`, optional
365+
The `matplotlib.colorizer.ColorizingArtist` (i.e., `.AxesImage`,
366+
`.ContourSet`, etc.) described by this colorbar.
367+
368+
If not given, the mappable is inferred from the Axes. If there is exactly
369+
one image or collection, this is used as mappable. Otherwise, an error is
370+
raised and you must specify the mappable explicitly.
371+
372+
Note that one can create a `.colorizer.ColorizingArtist` "on-the-fly"
373+
to generate colorbars not attached to a previously drawn artist, e.g.
374+
::
375+
376+
cr = colorizer.Colorizer(norm=norm, cmap=cmap)
377+
ax.colorbar(colorizer.ColorizingArtist(cr))
378+
379+
Returns
380+
-------
381+
colorbar : `~matplotlib.colorbar.Colorbar`
382+
383+
Other Parameters
384+
----------------
385+
use_gridspec : bool, optional
386+
If *ax* is positioned with a subplotspec and *use_gridspec*
387+
is ``True``, then *cax* is also positioned with a subplotspec.
388+
389+
%(_make_axes_kw_doc)s
390+
%(_colormap_kw_doc)s
391+
392+
Notes
393+
-----
394+
This method is a convenience shortcut if you want to place a colorbar
395+
right next to an Axes. ``ax.colorbar(mappable)`` is equivalent to
396+
``fig.colorbar(mappable, ax=ax)``. In particular, if you have exactly one
397+
mappable in the Axes, the general
398+
399+
::
400+
im = ax.imshow(data)
401+
fig.colorbar(im, ax=ax)
402+
403+
can be written more concisely as ::
404+
405+
ax.imshow(data)
406+
ax.colorbar()
407+
408+
Use `.Figure.colorbar` if you need more control on placing the colorbar.
409+
"""
410+
if mappable is None:
411+
# autodetect the mappable
412+
colormapped_artists = [*self.images, *self.collections]
413+
if len(colormapped_artists) != 1:
414+
raise RuntimeError(
415+
"Axes.colormap() requires exactly one colormapped Artist in the "
416+
f"Axes, but found {len(colormapped_artists)}. In ambiguous cases, "
417+
"please specify the mappable for the colorbar explicitly."
418+
)
419+
mappable = colormapped_artists[0]
420+
421+
return self.figure.colorbar(
422+
mappable, ax=self, use_gridspec=use_gridspec, **kwargs
423+
)
424+
358425
def inset_axes(self, bounds, *, transform=None, zorder=5, **kwargs):
359426
"""
360427
Add a child inset Axes to this existing Axes.

lib/matplotlib/axes/_axes.pyi

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ from matplotlib.collections import (
1111
EventCollection,
1212
QuadMesh,
1313
)
14-
from matplotlib.colorizer import Colorizer
14+
from matplotlib.cm import ScalarMappable
15+
from matplotlib.colorbar import Colorbar
16+
from matplotlib.colorizer import Colorizer, ColorizingArtist
1517
from matplotlib.colors import Colormap, Normalize
1618
from matplotlib.container import (
1719
BarContainer, PieContainer, ErrorbarContainer, StemContainer)
@@ -77,6 +79,13 @@ class Axes(_AxesBase):
7779
@overload
7880
def legend(self, *, loc: LegendLocType | None = ..., **kwargs) -> Legend: ...
7981

82+
def colorbar(
83+
self,
84+
mappable: ScalarMappable | ColorizingArtist | None = ...,
85+
*,
86+
use_gridspec: bool = ...,
87+
**kwargs
88+
) -> Colorbar: ...
8089
def inset_axes(
8190
self,
8291
bounds: tuple[float, float, float, float],

lib/matplotlib/tests/test_axes.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9329,6 +9329,27 @@ def test_automatic_legend():
93299329
assert ax.get_yticklabels()[0].get_text() == 'b'
93309330

93319331

9332+
def test_colorbar():
9333+
"""Test that Axes.colorbar() without arguments uses the only mappable."""
9334+
fig, (ax1, ax2, ax3) = plt.subplots(3, 1)
9335+
9336+
im = ax1.imshow([[0, 1], [2, 3]])
9337+
cb = ax1.colorbar()
9338+
assert cb.mappable is im
9339+
9340+
path_collection = ax2.scatter([0, 1], [0, 1], c=[0, 1])
9341+
cb2 = ax2.colorbar()
9342+
assert cb2.mappable is path_collection
9343+
9344+
# in case of multiple mappables: bare colorbar() fails, but passing a mappable works
9345+
im = ax3.imshow([[0, 1], [2, 3]])
9346+
ax3.scatter([0, 1], [0, 1], c=[0, 1])
9347+
with pytest.raises(RuntimeError, match="requires exactly one colormapped Artist"):
9348+
ax3.colorbar()
9349+
cb3 = ax3.colorbar(im)
9350+
assert cb3.mappable is im
9351+
9352+
93329353
def test_plot_errors():
93339354
with pytest.raises(TypeError, match=r"plot\(\) got an unexpected keyword"):
93349355
plt.plot([1, 2, 3], x=1)

0 commit comments

Comments
 (0)