Skip to content

Commit 6d57fc1

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

4 files changed

Lines changed: 116 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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import matplotlib.category # Register category unit converter as side effect.
1212
import matplotlib.cbook as cbook
1313
import matplotlib.collections as mcoll
14+
import matplotlib.colorbar as mcolorbar # noqa: F401, needed for docstring substitution
1415
import matplotlib.colorizer as mcolorizer
1516
import matplotlib.colors as mcolors
1617
import matplotlib.contour as mcontour
@@ -355,6 +356,74 @@ def legend(self, *args, **kwargs):
355356
def _remove_legend(self, legend):
356357
self.legend_ = None
357358

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